diff --git a/.github/actions/main-build/action.yml b/.github/actions/main-build/action.yml index a7817cd314f0..4c6c611412d7 100644 --- a/.github/actions/main-build/action.yml +++ b/.github/actions/main-build/action.yml @@ -4,7 +4,7 @@ inputs: arguments: required: true description: Gradle arguments - default: build + default: :platform-tooling-support-tests:test build --configuration-cache runs: using: "composite" steps: @@ -12,8 +12,3 @@ runs: - uses: ./.github/actions/run-gradle with: arguments: ${{ inputs.arguments }} - - uses: actions/upload-artifact@v3 - if: ${{ always() }} - with: - name: Test Distribution trace files (${{ github.job }}) - path: '**/build/test-results/*/trace.json' diff --git a/.github/actions/run-gradle/action.yml b/.github/actions/run-gradle/action.yml index a4b1bc129f6c..e825551ce6f5 100644 --- a/.github/actions/run-gradle/action.yml +++ b/.github/actions/run-gradle/action.yml @@ -8,18 +8,20 @@ inputs: runs: using: "composite" steps: - - uses: actions/setup-java@v3 + - uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4 id: setup-gradle-jdk with: distribution: temurin - java-version: 17 - - uses: gradle/gradle-build-action@v2 + java-version: 21 + check-latest: true + - uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4 + - shell: bash env: JAVA_HOME: ${{ steps.setup-gradle-jdk.outputs.path }} - with: - arguments: | - -Porg.gradle.java.installations.auto-download=false - -Penterprise.predictiveTestSelection.enabled=${{ github.event_name == 'pull_request' }} - "-Dscan.value.GitHub job=${{ github.job }}" - javaToolchains - ${{ inputs.arguments }} + run: | + ./gradlew \ + -Porg.gradle.java.installations.auto-download=false \ + -Pjunit.develocity.predictiveTestSelection.enabled=${{ github.event_name == 'pull_request' }} \ + "-Dscan.value.GitHub job=${{ github.job }}" \ + javaToolchains \ + ${{ inputs.arguments }} diff --git a/.github/actions/setup-test-jdk/action.yml b/.github/actions/setup-test-jdk/action.yml index 4e8c96266c69..9492cfae4ad6 100644 --- a/.github/actions/setup-test-jdk/action.yml +++ b/.github/actions/setup-test-jdk/action.yml @@ -1,11 +1,17 @@ name: Set up Test JDK description: Sets up the JDK required to run platform-tooling-support-tests +inputs: + distribution: + required: true + description: 'The JDK distribution to use' + default: 'liberica' runs: using: "composite" steps: - - uses: actions/setup-java@v3 + - uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4 with: - distribution: temurin + distribution: ${{ inputs.distribution }} java-version: 8 + check-latest: true - shell: bash run: echo "JDK8=$JAVA_HOME" >> $GITHUB_ENV diff --git a/.codecov.yml b/.github/codecov.yml similarity index 65% rename from .codecov.yml rename to .github/codecov.yml index 04dae7644042..608f022d9eea 100644 --- a/.codecov.yml +++ b/.github/codecov.yml @@ -1,8 +1,12 @@ comment: false + coverage: status: project: default: - threshold: 1 + threshold: "1" informational: true patch: off + +codecov: + max_report_age: off diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 5c19f255d870..000000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,35 +0,0 @@ -version: 2 -registries: - gradle-plugin-portal: - type: maven-repository - url: https://plugins.gradle.org/m2 - username: dummy # Required by dependabot - password: dummy # Required by dependabot -updates: - - package-ecosystem: "gradle" - directory: "/" - registries: - - gradle-plugin-portal - schedule: - interval: "weekly" - labels: [ ] - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - labels: [ ] - - package-ecosystem: "github-actions" - directory: "/.github/actions/main-build" - schedule: - interval: "weekly" - labels: [ ] - - package-ecosystem: "github-actions" - directory: "/.github/actions/run-gradle" - schedule: - interval: "weekly" - labels: [ ] - - package-ecosystem: "github-actions" - directory: "/.github/actions/setup-test-jdk" - schedule: - interval: "weekly" - labels: [ ] diff --git a/.github/workflows/close-inactive-issues.yml b/.github/workflows/close-inactive-issues.yml index ab8bb97a6f28..687cfcdc4f19 100644 --- a/.github/workflows/close-inactive-issues.yml +++ b/.github/workflows/close-inactive-issues.yml @@ -10,7 +10,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@v8 + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9 with: only-labels: "status: waiting-for-feedback" days-before-stale: 14 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 63b648c7b424..133179e59c25 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -14,7 +14,7 @@ on: - cron: '0 19 * * 3' env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} jobs: analyze: @@ -30,9 +30,9 @@ jobs: - javascript steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3 with: languages: ${{ matrix.language }} tools: latest @@ -40,8 +40,8 @@ jobs: uses: ./.github/actions/run-gradle with: arguments: | - --no-build-cache - -Dscan.tag.CodeQL + --no-build-cache \ + -Dscan.tag.CodeQL \ allMainClasses - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3 diff --git a/.github/workflows/combine-prs.yml b/.github/workflows/combine-prs.yml index 826472911980..593acdf8a7aa 100644 --- a/.github/workflows/combine-prs.yml +++ b/.github/workflows/combine-prs.yml @@ -11,6 +11,6 @@ jobs: runs-on: ubuntu-latest steps: - name: combine-prs - uses: github/combine-prs@v3.1.1 + uses: github/combine-prs@20d70f9d80eeb5958667d0602da433bd04ca6713 # v5.1.0 with: github_token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/cross-version.yml b/.github/workflows/cross-version.yml index 8480a68a49c7..9df3eeea19d7 100644 --- a/.github/workflows/cross-version.yml +++ b/.github/workflows/cross-version.yml @@ -1,6 +1,8 @@ name: Cross-Version on: + schedule: + - cron: '0 0 * * 6' # Every Saturday at 00:00 UTC push: branches: - main @@ -10,32 +12,79 @@ on: - '*' env: - ENTERPRISE_TESTDISTRIBUTION_ENABLED: true - BUILDCACHE_USERNAME: ${{ secrets.BUILD_CACHE_USERNAME }} - BUILDCACHE_PASSWORD: ${{ secrets.BUILD_CACHE_PASSWORD }} - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} jobs: openjdk: strategy: fail-fast: false matrix: - jdk: [20, 21, 22] - name: "OpenJDK ${{ matrix.jdk }}" + jdk: + - version: 22 + type: ga + - version: 23 + type: ga + - version: 24 + type: ea + - version: 24 + type: ea + release: leyden + name: "OpenJDK ${{ matrix.jdk.version }} (${{ matrix.jdk.release || matrix.jdk.type }})" runs-on: ubuntu-latest steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 with: fetch-depth: 1 - name: Set up Test JDK uses: ./.github/actions/setup-test-jdk - - name: 'Set up JDK ${{ matrix.jdk }}' - uses: oracle-actions/setup-java@v1 + - name: "Set up JDK ${{ matrix.jdk.version }} (${{ matrix.jdk.release || 'ea' }})" + if: matrix.jdk.type == 'ea' + uses: oracle-actions/setup-java@2e744f723b003fdd759727d0ff654c8717024845 # v1.4.0 with: website: jdk.java.net - release: ${{ matrix.jdk }} + release: ${{ matrix.jdk.release || matrix.jdk.version }} version: latest + - name: "Set up JDK ${{ matrix.jdk.version }} (${{ matrix.jdk.distribution || 'temurin' }})" + if: matrix.jdk.type == 'ga' + uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4 + with: + distribution: ${{ matrix.jdk.distribution || 'temurin' }} + java-version: ${{ matrix.jdk.version }} + check-latest: true + - name: 'Prepare JDK${{ matrix.jdk.version }} env var' + shell: bash + run: echo "JDK${{ matrix.jdk.version }}=$JAVA_HOME" >> $GITHUB_ENV + - name: Build + uses: ./.github/actions/run-gradle + with: + arguments: | + -PjavaToolchain.version=${{ matrix.jdk.version }} \ + -Dscan.tag.JDK_${{ matrix.jdk.version }} \ + build \ + --configuration-cache + openj9: + strategy: + fail-fast: false + matrix: + jdk: [ 21 ] + name: "OpenJ9 ${{ matrix.jdk }}" + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + with: + fetch-depth: 1 + - name: Set up Test JDK + uses: ./.github/actions/setup-test-jdk + with: + distribution: semeru + - name: 'Set up JDK ${{ matrix.jdk }}' + uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4 + with: + distribution: semeru + java-version: ${{ matrix.jdk }} + check-latest: true - name: 'Prepare JDK${{ matrix.jdk }} env var' shell: bash run: echo "JDK${{ matrix.jdk }}=$JAVA_HOME" >> $GITHUB_ENV @@ -43,11 +92,9 @@ jobs: uses: ./.github/actions/run-gradle with: arguments: | - -PjavaToolchainVersion=${{ matrix.jdk }} - -Dscan.tag.JDK_${{ matrix.jdk }} - build - - name: Upload Test Distribution trace files - uses: actions/upload-artifact@v3 - with: - name: "Test Distribution trace files (OpenJDK ${{ matrix.jdk }})" - path: '**/build/test-results/*/trace.json' + -PjavaToolchain.version=${{ matrix.jdk }} \ + -PjavaToolchain.implementation=j9 \ + -Dscan.tag.JDK_${{ matrix.jdk }} \ + -Dscan.tag.OpenJ9 \ + build \ + --configuration-cache diff --git a/.github/workflows/gradle-dependency-submission.yml b/.github/workflows/gradle-dependency-submission.yml new file mode 100644 index 000000000000..c867584a5835 --- /dev/null +++ b/.github/workflows/gradle-dependency-submission.yml @@ -0,0 +1,27 @@ +name: Gradle Dependency Submission + +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + dependency-submission: + if: github.repository == 'junit-team/junit5' + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + with: + fetch-depth: 1 + - name: Setup Java + uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4 + with: + distribution: temurin + java-version: 21 + check-latest: true + - name: Generate and submit dependency graph + uses: gradle/actions/dependency-submission@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4 diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml deleted file mode 100644 index de5d0346a9f6..000000000000 --- a/.github/workflows/gradle-wrapper-validation.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: "Validate Gradle Wrapper" - -on: - push: - branches: - - main - - 'releases/**' - pull_request: - branches: - - '*' - -jobs: - validation: - name: "Validation" - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v3 - with: - fetch-depth: 1 - - name: Validate Gradle wrapper - uses: gradle/wrapper-validation-action@v1 diff --git a/.github/workflows/issue-labels.yml b/.github/workflows/issue-labels.yml index 763dfe889565..5a921d952e49 100644 --- a/.github/workflows/issue-labels.yml +++ b/.github/workflows/issue-labels.yml @@ -9,7 +9,7 @@ jobs: permissions: issues: write steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 with: script: | github.rest.issues.addLabels({ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 676ec1a2f87a..d9297eb720a2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,46 +10,42 @@ on: - '*' env: - ENTERPRISE_TESTDISTRIBUTION_ENABLED: true - BUILDCACHE_USERNAME: ${{ secrets.BUILD_CACHE_USERNAME }} - BUILDCACHE_PASSWORD: ${{ secrets.BUILD_CACHE_PASSWORD }} - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} jobs: Linux: runs-on: ubuntu-latest steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 with: fetch-depth: 1 - - name: Install Graphviz - run: | - sudo apt-get update - sudo apt-get install graphviz - name: Install GraalVM - uses: graalvm/setup-graalvm@v1 + uses: graalvm/setup-graalvm@22cc13fe88ef133134b3798e128fb208df55e1f5 # v1 with: + distribution: graalvm-community version: 'latest' - java-version: '17' - components: 'native-image' + java-version: '21' github-token: ${{ secrets.GITHUB_TOKEN }} - name: Build uses: ./.github/actions/main-build with: arguments: | - -Ptesting.enableJaCoCo - build - jacocoRootReport - prepareDocsForUploadToGhPages + -Ptesting.enableJaCoCo \ + :platform-tooling-support-tests:test \ + build \ + jacocoRootReport \ + --configuration-cache - name: Upload to Codecov.io - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} Windows: runs-on: windows-latest steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 with: fetch-depth: 1 - name: Build @@ -59,7 +55,7 @@ jobs: runs-on: macos-latest steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 with: fetch-depth: 1 - name: Build @@ -69,10 +65,13 @@ jobs: name: Publish Snapshot Artifacts needs: linux runs-on: ubuntu-latest + permissions: + attestations: write # required for build provenance attestation + id-token: write # required for build provenance attestation if: github.event_name == 'push' && github.repository == 'junit-team/junit5' && (startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/main') steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 with: fetch-depth: 1 - name: Publish @@ -81,30 +80,42 @@ jobs: ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }} ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }} with: - arguments: publish -x check + arguments: | + publish -x check \ + prepareGitHubAttestation + - name: Generate build provenance attestations + uses: actions/attest-build-provenance@210c1913531870065f03ce1f9440dd87bc0938cd # v1.4.0 + with: + subject-path: documentation/build/attestation/*.jar - update_documentation: - name: Update Snapshot Documentation + documentation: + name: Build Documentation concurrency: group: github-pages cancel-in-progress: true needs: Linux runs-on: ubuntu-latest - if: github.event_name == 'push' && github.repository == 'junit-team/junit5' && github.ref == 'refs/heads/main' steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 with: fetch-depth: 1 - name: Install Graphviz run: | sudo apt-get update sudo apt-get install graphviz - - name: Restore Gradle cache and display toolchains + - name: Build Documentation uses: ./.github/actions/run-gradle with: - arguments: --quiet + arguments: | + prepareDocsForUploadToGhPages \ + -Dscan.tag.Documentation - name: Upload Documentation + if: github.event_name == 'push' && github.repository == 'junit-team/junit5' && github.ref == 'refs/heads/main' + uses: ./.github/actions/run-gradle + with: + arguments: | + gitPublishPush \ + -Dscan.tag.Documentation env: GRGIT_USER: ${{ secrets.GH_TOKEN }} - run: ./gradle/scripts/publishDocumentationSnapshotOnlyIfNecessary.sh diff --git a/.github/workflows/reproducible-build.yml b/.github/workflows/reproducible-build.yml index a32a51ac0002..39eb9160dbdc 100644 --- a/.github/workflows/reproducible-build.yml +++ b/.github/workflows/reproducible-build.yml @@ -10,7 +10,7 @@ on: - '*' env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} jobs: check_build_reproducibility: @@ -18,13 +18,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 with: fetch-depth: 1 - name: Restore Gradle cache and display toolchains uses: ./.github/actions/run-gradle with: - arguments: --quiet + arguments: | + --quiet \ + --configuration-cache - name: Build and compare checksums shell: bash run: | diff --git a/.gitignore b/.gitignore index 6d276115b4be..2764c9175b99 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ gradle-app.setting .project /bin/ /*/bin +/gradle/plugins/*/bin # IntelliJ *.iml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 000000000000..e6094d409c45 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000000..79ee123c2b23 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2313cecda3ff..081a337257e9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -157,3 +157,16 @@ To deprecate an item: an eventual replacement. - If the item is used in existing code, add `@SuppressWarnings("deprecation")` to make the build pass. + +## Building the Project + +Please refer to [the readme](README.md#building-from-source) for the most common +build commands. + +### Build Parameters + +The build can be influenced by a number of parameters. For example, measuring +JaCoCo code coverage of Test tasks can be enabled, or Predictive Test Selection +disabled. To see the full list, please run the following task: + +`./gradlew :plugins:build-parameters:parameters` diff --git a/README.md b/README.md index 43f900ade772..fcbe4486edd7 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,17 @@ This repository is the home of _JUnit 5_. +## Sponsors + [![Support JUnit](https://img.shields.io/badge/%F0%9F%92%9A-Support%20JUnit-brightgreen.svg)](https://junit.org/sponsoring) +* **Gold Sponsors:** [JetBrains](https://jb.gg/junit-logo) +* **Silver Sponsors:** [Micromata](https://www.micromata.de), [Quo Card](https://quo-digital.jp) +* **Bronze Sponsors:** [Premium Minds](https://www.premium-minds.com), [Testmo](https://www.testmo.com), [codefortynine](https://codefortynine.com), [Info Support](https://www.infosupport.com), [Stiltsoft](https://stiltsoft.com), [Code Intelligence](https://www.code-intelligence.com), [Route4Me](https://route4me.com/) + ## Latest Releases -- General Availability (GA): [JUnit 5.10.0](https://github.com/junit-team/junit5/releases/tag/r5.10.0) (July 23, 2023) +- General Availability (GA): [JUnit 5.11.3](https://github.com/junit-team/junit5/releases/tag/r5.11.3) (October 21, 2024) - Preview (Milestone/Release Candidate): N/A ## Documentation @@ -46,14 +52,16 @@ A code coverage report can also be generated locally via the [Gradle Wrapper] by executing `./gradlew -Ptesting.enableJaCoCo clean jacocoRootReport`. The results will be available in `build/reports/jacoco/jacocoRootReport/html/index.html`. -## Gradle Enterprise +## Develocity -[![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.junit.org/scans) +[![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.junit.org/scans) -JUnit 5 utilizes [Gradle Enterprise](https://gradle.com/) for _Build Scans_, _Build Cache_, and _Test Distribution_. +JUnit 5 utilizes [Develocity](https://gradle.com/) for [Build Scans](https://scans.gradle.com/), +[Build Cache](https://docs.gradle.org/current/userguide/build_cache.html), and +[Predictive Test Selection](https://docs.gradle.com/enterprise/predictive-test-selection/). The latest Build Scans are available on [ge.junit.org](https://ge.junit.org/). Currently, -only core team members can publish Build Scans and use Test Distribution on that server. +only core team members can publish Build Scans on that server. You can, however, publish a Build Scan to [scans.gradle.com](https://scans.gradle.com/) by using the `--scan` parameter explicitly. @@ -62,7 +70,7 @@ task outputs from previous CI builds. ## Building from Source -You need [JDK 17] to build JUnit 5. [Gradle toolchains] are used to detect and +You need [JDK 21] to build JUnit 5. [Gradle toolchains] are used to detect and potentially download additional JDKs for compilation and test execution. All modules can be _built_ and _tested_ with the [Gradle Wrapper] using the following command. @@ -97,7 +105,7 @@ See also for releases and [Gradle Wrapper]: https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:using_wrapper [JaCoCo]: https://www.eclemma.org/jacoco/ [Javadoc]: https://junit.org/junit5/docs/current/api/ -[JDK 17]: https://foojay.io/almanac/java-17/ +[JDK 21]: https://javaalmanac.io/jdk/21/ [Release Notes]: https://junit.org/junit5/docs/current/release-notes/ [Samples]: https://github.com/junit-team/junit5-samples [StackOverflow]: https://stackoverflow.com/questions/tagged/junit5 diff --git a/SECURITY.md b/SECURITY.md index fca52da512fa..0593bdb0acbc 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,9 +3,9 @@ ## Supported Versions | Version | Supported | -| ------- | ------------------ | -| 5.9.x | :white_check_mark: | -| < 5.9 | :x: | +|---------| ------------------ | +| 5.11.x | :white_check_mark: | +| < 5.11 | :x: | ## Reporting a Vulnerability diff --git a/build.gradle.kts b/build.gradle.kts index 048d466a4546..87509e4ad9fc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,9 +48,12 @@ val mavenizedProjects by extra(platformProjects + jupiterProjects + vintageProje val modularProjects by extra(mavenizedProjects - listOf(projects.junitPlatformConsoleStandalone.dependencyProject)) dependencies { - (modularProjects + listOf(projects.platformTests.dependencyProject)).forEach { - jacocoAggregation(project(it.path)) + modularProjects.forEach { + jacocoAggregation(it) } + jacocoAggregation(projects.documentation) + jacocoAggregation(projects.jupiterTests) + jacocoAggregation(projects.platformTests) } nexusPublishing { @@ -61,7 +64,7 @@ nexusPublishing { } nohttp { - source.exclude("**/.gradle/**", "gradle/plugins/**/build/**", "buildSrc/build/**") + source.exclude("**/.gradle/**", "gradle/plugins/**/build/**") } tasks.checkstyleNohttp { diff --git a/documentation/documentation.gradle.kts b/documentation/documentation.gradle.kts index 4cc640563170..f5f6e1b6391a 100644 --- a/documentation/documentation.gradle.kts +++ b/documentation/documentation.gradle.kts @@ -11,11 +11,14 @@ plugins { alias(libs.plugins.asciidoctorConvert) alias(libs.plugins.asciidoctorPdf) alias(libs.plugins.gitPublish) + alias(libs.plugins.plantuml) id("junitbuild.build-parameters") + id("junitbuild.java-multi-release-test-sources") id("junitbuild.kotlin-library-conventions") id("junitbuild.testing-conventions") } +val mavenizedProjects: List by rootProject val modularProjects: List by rootProject // Because we need to set up Javadoc aggregation @@ -26,8 +29,22 @@ javaLibrary { testJavaVersion = JavaVersion.VERSION_1_8 } -val apiReport by configurations.creatingResolvable -val standaloneConsoleLauncher by configurations.creatingResolvable +val apiReport = configurations.dependencyScope("apiReport") +val apiReportClasspath = configurations.resolvable("apiReportClasspath") { + extendsFrom(apiReport.get()) +} +val standaloneConsoleLauncher = configurations.dependencyScope("standaloneConsoleLauncher") +val standaloneConsoleLauncherClasspath = configurations.resolvable("standaloneConsoleLauncherClasspath") { + extendsFrom(standaloneConsoleLauncher.get()) +} +val attestation = configurations.dependencyScope("attestation") +val attestationClasspath = configurations.resolvable("attestationClasspath") { + extendsFrom(attestation.get()) + isTransitive = false +} + +val tools by sourceSets.creating +val toolsImplementation by configurations.getting dependencies { implementation(projects.junitJupiterApi) { @@ -38,37 +55,43 @@ dependencies { // in reports generated by the ApiReportGenerator. modularProjects.forEach { apiReport(it) } + // Pull in all "mavenized projects" to ensure that they are included + // in the generation of build provenance attestation. + mavenizedProjects.forEach { attestation(it) } + testImplementation(projects.junitJupiterMigrationsupport) testImplementation(projects.junitPlatformConsole) testImplementation(projects.junitPlatformRunner) testImplementation(projects.junitPlatformSuite) testImplementation(projects.junitPlatformTestkit) - testImplementation(kotlin("stdlib")) - testImplementation(projects.junitVintageEngine) - testRuntimeOnly(libs.apiguardian) { - because("it's required to generate API tables") - } + testImplementation(kotlin("stdlib")) - testImplementation(libs.classgraph) { - because("ApiReportGenerator needs it") - } + toolsImplementation(projects.junitPlatformCommons) + toolsImplementation(libs.classgraph) + toolsImplementation(libs.apiguardian) testImplementation(libs.jimfs) { because("Jimfs is used in src/test/java") } + if (java.toolchain.implementation.orNull == JvmImplementation.J9) { + testRuntimeOnly(libs.jfrPolyfill) { + because("OpenJ9 does not include JFR") + } + } + standaloneConsoleLauncher(projects.junitPlatformConsoleStandalone) } asciidoctorj { modules { - diagram.use() - pdf.version(libs.versions.asciidoctor.pdf) + pdf.version(libs.versions.asciidoctorj.pdf) } requires(file("src/docs/asciidoc/resources/themes/rouge_junit.rb")) } +val buildRevision: String by rootProject.extra val snapshot = rootProject.version.toString().contains("SNAPSHOT") val docsVersion = if (snapshot) "snapshot" else rootProject.version val releaseBranch = if (snapshot) "HEAD" else "r${rootProject.version}" @@ -108,7 +131,7 @@ val experimentalApisTableFile = generatedAsciiDocPath.map { it.file("experimenta val deprecatedApisTableFile = generatedAsciiDocPath.map { it.file("deprecated-apis-table.adoc") } val standaloneConsoleLauncherShadowedArtifactsFile = generatedAsciiDocPath.map { it.file("console-launcher-standalone-shadowed-artifacts.adoc") } -val jdkJavadocBaseUrl = "https://docs.oracle.com/en/java/javase/11/docs/api" +val jdkJavadocBaseUrl = "https://docs.oracle.com/en/java/javase/${JavaVersion.current().majorVersion}/docs/api" val elementListsDir = layout.buildDirectory.dir("elementLists") val externalModulesWithoutModularJavadoc = mapOf( "org.apiguardian.api" to "https://apiguardian-team.github.io/apiguardian/docs/$apiGuardianDocVersion/api/", @@ -134,7 +157,6 @@ tasks { ) }) - args.addAll("--config", "enableHttpServer=true") args.addAll("--include-classname", ".*Tests") args.addAll("--include-classname", ".*Demo") args.addAll("--exclude-tag", "exclude") @@ -154,68 +176,86 @@ tasks { } } + testRelease21 { + include("**/*Demo.class") + } + check { dependsOn(consoleLauncherTest) } + named(tools.compileJavaTaskName) { + options.release.set(21) + } + + named("checkstyleTools") { + config = resources.text.fromFile(checkstyle.configDirectory.file("checkstyleMain.xml")) + } + val generateConsoleLauncherOptions by registering(CaptureJavaExecOutput::class) { - classpath.from(sourceSets["test"].runtimeClasspath) + classpath.from(standaloneConsoleLauncherClasspath) mainClass = "org.junit.platform.console.ConsoleLauncher" args.addAll("--help", "--disable-banner") outputFile = consoleLauncherOptionsFile } val generateConsoleLauncherDiscoverOptions by registering(CaptureJavaExecOutput::class) { - classpath.from(sourceSets["test"].runtimeClasspath) + classpath.from(standaloneConsoleLauncherClasspath) mainClass = "org.junit.platform.console.ConsoleLauncher" args.addAll("discover", "--help", "--disable-banner") outputFile = consoleLauncherDiscoverOptionsFile } val generateConsoleLauncherExecuteOptions by registering(CaptureJavaExecOutput::class) { - classpath.from(sourceSets["test"].runtimeClasspath) + classpath.from(standaloneConsoleLauncherClasspath) mainClass = "org.junit.platform.console.ConsoleLauncher" args.addAll("execute", "--help", "--disable-banner") outputFile = consoleLauncherExecuteOptionsFile } val generateConsoleLauncherEnginesOptions by registering(CaptureJavaExecOutput::class) { - classpath.from(sourceSets["test"].runtimeClasspath) + classpath.from(standaloneConsoleLauncherClasspath) mainClass = "org.junit.platform.console.ConsoleLauncher" args.addAll("engines", "--help", "--disable-banner") outputFile = consoleLauncherEnginesOptionsFile } - val generateExperimentalApisTable by registering(CaptureJavaExecOutput::class) { - classpath.from(sourceSets["test"].runtimeClasspath) + val generateApiTables by registering(JavaExec::class) { + classpath = tools.runtimeClasspath mainClass = "org.junit.api.tools.ApiReportGenerator" - jvmArgumentProviders += ClasspathSystemPropertyProvider("api.classpath", apiReport) - args.add("EXPERIMENTAL") - outputFile = experimentalApisTableFile - } - - val generateDeprecatedApisTable by registering(CaptureJavaExecOutput::class) { - classpath.from(sourceSets["test"].runtimeClasspath) - mainClass = "org.junit.api.tools.ApiReportGenerator" - jvmArgumentProviders += ClasspathSystemPropertyProvider("api.classpath", apiReport) - args.add("DEPRECATED") - outputFile = deprecatedApisTableFile + jvmArgumentProviders += ClasspathSystemPropertyProvider("api.classpath", apiReportClasspath.get()) + argumentProviders += CommandLineArgumentProvider { + listOf( + "DEPRECATED=${deprecatedApisTableFile.get().asFile.absolutePath}", + "EXPERIMENTAL=${experimentalApisTableFile.get().asFile.absolutePath}", + ) + } + outputs.cacheIf { true } + outputs.file(deprecatedApisTableFile) + outputs.file(experimentalApisTableFile) } val generateStandaloneConsoleLauncherShadowedArtifactsFile by registering(GenerateStandaloneConsoleLauncherShadowedArtifactsFile::class) { - inputJar.fileProvider(standaloneConsoleLauncher.elements.map { it.single().asFile }) + inputJar.fileProvider(standaloneConsoleLauncherClasspath.flatMap { it.elements.map { it.single().asFile } }) outputFile = standaloneConsoleLauncherShadowedArtifactsFile } + plantUml { + fileFormat = "SVG" + outputs.cacheIf { true } + } + + val componentDiagram = plantUml.flatMap { it.outputDirectory.file("component-diagram.svg") } + withType().configureEach { inputs.files( generateConsoleLauncherOptions, generateConsoleLauncherDiscoverOptions, generateConsoleLauncherExecuteOptions, generateConsoleLauncherEnginesOptions, - generateExperimentalApisTable, - generateDeprecatedApisTable, - generateStandaloneConsoleLauncherShadowedArtifactsFile + generateApiTables, + generateStandaloneConsoleLauncherShadowedArtifactsFile, + componentDiagram ) resources { @@ -223,16 +263,22 @@ tasks { include("**/images/**/*.png") include("**/images/**/*.svg") } + from(componentDiagram) { + into("user-guide/images") + } } // Temporary workaround for https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/599 inputs.dir(sourceDir).withPropertyName("sourceDir").withPathSensitivity(RELATIVE) + val platformVersion: String by project + val vintageVersion: String by project + attributeProviders += AsciidoctorAttributeProvider { mapOf( "jupiter-version" to version, - "platform-version" to project.property("platformVersion"), - "vintage-version" to project.property("vintageVersion"), + "platform-version" to platformVersion, + "vintage-version" to vintageVersion, "bom-version" to version, "junit4-version" to libs.versions.junit4.get(), "apiguardian-version" to libs.versions.apiguardian.get(), @@ -272,7 +318,14 @@ tasks { inputs.dir(kotlin.srcDirs.first()) } - forkOptions { + sourceSets["testRelease21"].apply { + attributes(mapOf( + "testRelease21Dir" to java.srcDirs.first() + )) + inputs.dir(java.srcDirs.first()) + } + + jvm { // To avoid warning, see https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/597 jvmArgs( "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", @@ -372,8 +425,10 @@ tasks { val moduleSourcePathOption = addPathOption("-module-source-path") moduleSourcePathOption.value = modularProjects.map { it.file("src/module") } moduleSourcePathOption.value.forEach { inputs.dir(it) } - addOption(ModuleSpecificJavadocFileOption("-patch-module", modularProjects.associate { - it.javaModuleName to files(it.sourceSets.matching { it.name.startsWith("main") }.map { it.allJava.srcDirs }).asPath + addOption(ModuleSpecificJavadocFileOption("-patch-module", modularProjects.associate { project -> + project.javaModuleName to files( + project.sourceSets.named { it.startsWith("main") }.map { it.allJava.srcDirs } + ).asPath })) addStringOption("-add-modules", "info.picocli") addOption(ModuleSpecificJavadocFileOption("-add-reads", mapOf( @@ -383,11 +438,13 @@ tasks { ))) } - source(modularProjects.map { files(it.sourceSets.matching { it.name.startsWith("main") }.map { it.allJava }) }) + source(modularProjects.map { project -> + files(project.sourceSets.named { it.startsWith("main") }.map { it.allJava }) + }) classpath = files(modularProjects.map { it.sourceSets.main.get().compileClasspath }) - maxMemory = "1024m" - destinationDir = layout.buildDirectory.dir("docs/javadoc").get().asFile + setMaxMemory("1024m") + options.destinationDirectory = layout.buildDirectory.dir("docs/javadoc").get().asFile doFirst { (options as CoreJavadocOptions).modulePath = classpath.files.toList() @@ -447,11 +504,10 @@ tasks { val createCurrentDocsFolder by registering(Copy::class) { dependsOn(prepareDocsForUploadToGhPages) - outputs.dir("$docsDir/current") onlyIf { replaceCurrentDocs } - from("$docsDir/$docsVersion") - into("$docsDir/current") + from(docsDir.map { it.dir(docsVersion.toString()) }) + into(docsDir.map { it.dir("current") }) } val configureGitAuthor by registering { @@ -472,18 +528,24 @@ tasks { gitPublishCommit { dependsOn(configureGitAuthor) } + + val prepareGitHubAttestation by registering(Sync::class) { + from(attestationClasspath) + into(layout.buildDirectory.dir("attestation")) + rename("(.*)-SNAPSHOT.jar", "$1-SNAPSHOT+${buildRevision.substring(0, 7)}.jar") + } } eclipse { classpath { - plusConfigurations.add(projects.junitPlatformConsole.dependencyProject.configurations["shadowed"]) - plusConfigurations.add(projects.junitJupiterParams.dependencyProject.configurations["shadowed"]) + plusConfigurations.add(projects.junitPlatformConsole.dependencyProject.configurations["shadowedClasspath"]) + plusConfigurations.add(projects.junitJupiterParams.dependencyProject.configurations["shadowedClasspath"]) } } idea { module { - scopes["PROVIDED"]!!["plus"]!!.add(projects.junitPlatformConsole.dependencyProject.configurations["shadowed"]) - scopes["PROVIDED"]!!["plus"]!!.add(projects.junitJupiterParams.dependencyProject.configurations["shadowed"]) + scopes["PROVIDED"]!!["plus"]!!.add(projects.junitPlatformConsole.dependencyProject.configurations["shadowedClasspath"]) + scopes["PROVIDED"]!!["plus"]!!.add(projects.junitJupiterParams.dependencyProject.configurations["shadowedClasspath"]) } } diff --git a/documentation/src/docs/asciidoc/link-attributes.adoc b/documentation/src/docs/asciidoc/link-attributes.adoc index 6aa27c3d46b8..dfbe7932d479 100644 --- a/documentation/src/docs/asciidoc/link-attributes.adoc +++ b/documentation/src/docs/asciidoc/link-attributes.adoc @@ -12,6 +12,7 @@ endif::[] :junit-platform-support-package: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/package-summary.html[org.junit.platform.commons.support] :AnnotationSupport: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/AnnotationSupport.html[AnnotationSupport] :ClassSupport: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ClassSupport.html[ClassSupport] +:ConversionSupport: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/conversion/ConversionSupport.html[ConversionSupport] :ModifierSupport: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ModifierSupport.html[ModifierSupport] :ReflectionSupport: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ReflectionSupport.html[ReflectionSupport] :Testable: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/annotation/Testable.html[@Testable] @@ -21,9 +22,35 @@ endif::[] // Platform Engine :junit-platform-engine: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/package-summary.html[junit-platform-engine] :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] -:DiscoverySelectors_selectMethod: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectMethod-java.lang.String-[selectMethod(String) in DiscoverySelectors] +:ClasspathResourceSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClasspathResourceSelector.html[ClasspathResourceSelector] +:ClasspathRootSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClasspathRootSelector.html[ClasspathRootSelector] +:ClassSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClassSelector.html[ClassSelector] +:DirectorySelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DirectorySelector.html[DirectorySelector] +:DiscoverySelectors: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html[DiscoverySelectors] +:DiscoverySelectors_selectClasspathResource: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClasspathResource(java.lang.String)[selectClasspathResource] +:DiscoverySelectors_selectClasspathRoots: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClasspathRoots(java.util.Set)[selectClasspathRoots] +:DiscoverySelectors_selectClass: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClass(java.lang.String)[selectClass] +:DiscoverySelectors_selectDirectory: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectDirectory(java.lang.String)[selectDirectory] +:DiscoverySelectors_selectFile: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectFile(java.lang.String)[selectFile] +:DiscoverySelectors_selectIteration: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectIteration(org.junit.platform.engine.DiscoverySelector,int\...)[selectIteration] +:DiscoverySelectors_selectMethod: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectMethod(java.lang.String)[selectMethod] +:DiscoverySelectors_selectModule: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectModule(java.lang.String)[selectModule] +:DiscoverySelectors_selectNestedClass: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectNestedClass(java.util.List,java.lang.Class)[selectNestedClass] +: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] +:DiscoverySelectors_selectPackage: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectPackage(java.lang.String)[selectPackage] +:DiscoverySelectors_selectUniqueId: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectUniqueId(java.lang.String)[selectUniqueId] +:DiscoverySelectors_selectUri: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectUri(java.lang.String)[selectUri] +:FileSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/FileSelector.html[FileSelector] :HierarchicalTestEngine: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/hierarchical/HierarchicalTestEngine.html[HierarchicalTestEngine] +:IterationSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/IterationSelector.html[IterationSelector] +:MethodSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/MethodSelector.html[MethodSelector] +:ModuleSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ModuleSelector.html[ModuleSelector] +:NestedClassSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/NestedClassSelector.html[NestedClassSelector] +:NestedMethodSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/NestedMethodSelector.html[NestedMethodSelector] +:PackageSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/PackageSelector.html[PackageSelector] :ParallelExecutionConfigurationStrategy: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfigurationStrategy.html[ParallelExecutionConfigurationStrategy] +:UniqueIdSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/UniqueIdSelector.html[UniqueIdSelector] +:UriSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/UriSelector.html[UriSelector] :TestEngine: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/TestEngine.html[TestEngine] // Platform Launcher API :junit-platform-launcher: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/package-summary.html[junit-platform-launcher] @@ -50,6 +77,15 @@ endif::[] // Platform Suite :suite-api-package: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/package-summary.html[org.junit.platform.suite.api] :junit-platform-suite-engine: {javadoc-root}/org.junit.platform.suite.engine/org/junit/platform/suite/engine/package-summary.html[junit-platform-suite-engine] +:Select: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/Select.html[@Select] +:SelectClasspathResource: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectClasspathResource.html[@SelectClasspathResource] +:SelectClasses: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectClasses.html[@SelectClasses] +:SelectDirectories: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectDirectories.html[@SelectDirectories] +:SelectFile: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectFile.html[@SelectFile] +:SelectMethod: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectMethod.html[@SelectMethod] +:SelectModules: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectModules.html[@SelectModules] +:SelectPackages: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectPackages.html[@SelectPackages] +:SelectUris: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectUris.html[@SelectUris] // Platform Test Kit :testkit-engine-package: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/package-summary.html[org.junit.platform.testkit.engine] :EngineExecutionResults: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EngineExecutionResults.html[EngineExecutionResults] @@ -67,6 +103,7 @@ endif::[] :api-package: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/package-summary.html[org.junit.jupiter.api] :Assertions: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Assertions.html[org.junit.jupiter.api.Assertions] :Assumptions: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Assumptions.html[org.junit.jupiter.api.Assumptions] +:AutoClose: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/AutoClose.html[@AutoClose] :ClassOrderer_ClassName: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.ClassName.html[ClassOrderer.ClassName] :ClassOrderer_DisplayName: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.DisplayName.html[ClassOrderer.DisplayName] :ClassOrderer_OrderAnnotation: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.OrderAnnotation.html[ClassOrderer.OrderAnnotation] @@ -138,20 +175,22 @@ endif::[] :TempDir: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/io/TempDir.html[@TempDir] // Jupiter Params :params-provider-package: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/package-summary.html[org.junit.jupiter.params.provider] -:AnnotationBasedArgumentConverter: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/AnnotationBasedArgumentConverter.html[AnnotationBasedArgumentConverter] +:AnnotationBasedArgumentConverter: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/converter/AnnotationBasedArgumentConverter.html[AnnotationBasedArgumentConverter] :AnnotationBasedArgumentsProvider: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.html[AnnotationBasedArgumentsProvider] :ArgumentsAccessor: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/aggregator/ArgumentsAccessor.html[ArgumentsAccessor] :ArgumentsAggregator: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/aggregator/ArgumentsAggregator.html[ArgumentsAggregator] -:CsvArgumentsProvider: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/CsvArgumentsProvider.html[CsvArgumentsProvider] +:CsvArgumentsProvider: {junit5-repo}/blob/main/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvArgumentsProvider.java[CsvArgumentsProvider] :EmptySource: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/EmptySource.html[@EmptySource] +:FieldSource: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/FieldSource.html[@FieldSource] :MethodSource: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/MethodSource.html[@MethodSource] :NullAndEmptySource: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/NullAndEmptySource.html[@NullAndEmptySource] :NullSource: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/NullSource.html[@NullSource] :ParameterizedTest: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/ParameterizedTest.html[@ParameterizedTest] -:ValueArgumentsProvider: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/ValueArgumentsProvider.html[ValueArgumentsProvider] +:ValueArgumentsProvider: {junit5-repo}/blob/main/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueArgumentsProvider.java[ValueArgumentsProvider] // Jupiter Engine :junit-jupiter-engine: {javadoc-root}/org.junit.jupiter.engine/org/junit/jupiter/engine/package-summary.html[junit-jupiter-engine] // Jupiter Extension Implementations +:AutoCloseExtension: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/AutoCloseExtension.java[AutoCloseExtension] :DisabledCondition: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DisabledCondition.java[DisabledCondition] :RepetitionExtension: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java[RepetitionExtension] :TempDirectory: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java[TempDirectory] @@ -186,7 +225,7 @@ endif::[] :Log4j_JDK_Logging_Adapter: https://logging.apache.org/log4j/2.x/log4j-jul/index.html[Log4j JDK Logging Adapter] :Logback: https://logback.qos.ch/[Logback] :LogManager: https://docs.oracle.com/javase/8/docs/api/java/util/logging/LogManager.html[LogManager] -:Maven_Central: https://search.maven.org/[Maven Central] +:Maven_Central: https://central.sonatype.com/[Maven Central] :MockitoExtension: https://github.com/mockito/mockito/blob/release/2.x/subprojects/junit-jupiter/src/main/java/org/mockito/junit/jupiter/MockitoExtension.java[MockitoExtension] :ServiceLoader: {jdk-javadoc-base-url}/java.base/java/util/ServiceLoader.html[ServiceLoader] :SpringExtension: https://github.com/spring-projects/spring-framework/tree/HEAD/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java[SpringExtension] diff --git a/documentation/src/docs/asciidoc/release-notes/index.adoc b/documentation/src/docs/asciidoc/release-notes/index.adoc index ec787091b41e..caa8abe1b4d8 100644 --- a/documentation/src/docs/asciidoc/release-notes/index.adoc +++ b/documentation/src/docs/asciidoc/release-notes/index.adoc @@ -6,9 +6,10 @@ Stefan Bechtold; Sam Brannen; Johannes Link; Matthias Merdes; Marc Philipp; Juli :docinfodir: {includedir}/docinfos :docinfo2: :numbered!: +:last-update-label!: // -This document contains the _change log_ for all JUnit 5 releases since 5.9 GA. +This document contains the _change log_ for all JUnit 5 releases since 5.10 GA. Please refer to the <<../user-guide/index.adoc#user-guide,User Guide>> for comprehensive reference documentation for programmers writing tests, extension authors, and engine @@ -16,12 +17,22 @@ authors as well as build tool and IDE vendors. include::{includedir}/link-attributes.adoc[] -include::{basedir}/release-notes-5.10.0.adoc[] +include::{basedir}/release-notes-5.11.3.adoc[] + +include::{basedir}/release-notes-5.11.2.adoc[] + +include::{basedir}/release-notes-5.11.1.adoc[] + +include::{basedir}/release-notes-5.11.0.adoc[] -include::{basedir}/release-notes-5.9.3.adoc[] +include::{basedir}/release-notes-5.10.5.adoc[] -include::{basedir}/release-notes-5.9.2.adoc[] +include::{basedir}/release-notes-5.10.4.adoc[] -include::{basedir}/release-notes-5.9.1.adoc[] +include::{basedir}/release-notes-5.10.3.adoc[] -include::{basedir}/release-notes-5.9.0.adoc[] +include::{basedir}/release-notes-5.10.2.adoc[] + +include::{basedir}/release-notes-5.10.1.adoc[] + +include::{basedir}/release-notes-5.10.0.adoc[] diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0.adoc index 3141c2dbae97..1c3d7952307f 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0.adoc @@ -21,153 +21,5 @@ * Improved configurability of parallel execution * Numerous bug fixes and minor improvements -For a complete list of all _closed_ issues and pull requests for this release, consult the -link:{junit5-repo}+/milestone/65?closed=1+[5.10.0-M1], -link:{junit5-repo}+/milestone/69?closed=1+[5.10.0-RC1], -link:{junit5-repo}+/milestone/71?closed=1+[5.10.0-RC2], and -link:{junit5-repo}+/milestone/70?closed=1+[5.10.0 GA] milestone pages in the JUnit -repository on GitHub. - - -[[release-notes-5.10.0-junit-platform]] -=== JUnit Platform - -==== Deprecations and Breaking Changes - -* Building native images with GraalVM now requires configuring the build arg - `--initialize-at-build-time=org.junit.platform.launcher.core.LauncherConfig` and - `--initialize-at-build-time=org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter`. -* The `getMethodParameterTypes()` methods in `MethodSelector` and `NestedMethodSelector` - have been deprecated and replaced by `getParameterTypeNames()` for greater clarity. - -==== New Features and Improvements - -* Various "experimental" APIs have been promoted to "stable", including - `ModuleSelector`, `EngineDiscoveryListener`, `EngineDiscoveryRequestResolver`, - `LauncherSession`, `LauncherSessionListener`, parallel execution support classes, - `@Suite` and related annotations, and others. -* All utility methods in `ReflectionSupport` that return a `List` now have counterparts - which return a `Stream`. -* New `tryToLoadClass(...)` variant in `ReflectionSupport` that accepts an explicit - `ClassLoader`, allowing classes to be resolved with custom `ClassLoader` arrangements. -* `ReflectionSupport.findMethod(Class, String, String)` now uses the `ClassLoader` of - the supplied `Class` to load parameter types instead of using the _default_ - `ClassLoader`. This allows parameter types to be resolved with custom `ClassLoader` - arrangements (such as OSGi). Consequently, `DiscoverySelectors.selectMethod(Class, - String, String)` also now works properly with custom `ClassLoader` arrangements. - -* New `@SelectMethod` selector support in the `@Suite` test engine. -* Classes may now be selected by fully-qualified name via the `names` attribute in - `@SelectClasses`. -* New overloaded constructors for `ClassSelector`, `NestedClassSelector`, - `MethodSelector`, and `NestedMethodSelector` that take an explicit `ClassLoader` as a - parameter, allowing selectors to select classes in custom `ClassLoader` arrangements - like in OSGi. -* New `selectMethod()` and `selectNestedMethod()` variants in `DiscoverySelectors` that - accept a `Class...` argument of parameter types as a type-safe alternative to - providing the names of parameter types as a comma-delimited string. -* For consistency with JUnit Jupiter lifecycle callbacks, listener method pairs for - started/finished and opened/closed events are now invoked using "wrapping" semantics. - This means that finished/closed event methods are invoked in reverse order compared to - the corresponding started/opened event methods when multiple listeners are registered. - This affects the following listener interfaces: - `TestExecutionListener`, `EngineExecutionListener`, `LauncherDiscoveryListener`, and - `LauncherSessionListener`. -* New `LauncherInterceptor` SPI for intercepting the creation of instances of `Launcher` - and `LauncherSessionlistener` as well as invocations of the `discover` and `execute` - methods of the former. Please refer to the - <<../user-guide/index.adoc#launcher-api-launcher-interceptors-custom, User Guide>> for - details. -* Support for limiting the `max-pool-size-factor` for parallel execution via a - configuration parameter. -* New `testfeed` details mode for `ConsoleLauncher` that prints test execution events as - they occur in a concise format. -* The existing functionality of the `ConsoleLauncher` has been split into two subcommands: - `execute` for executing tests and `engines` for listing registered test engines. -* A new `discover` subcommand has been added to the `ConsoleLauncher` to print the - discovered tests for the specified details mode without executing them. -* Improved error message for cyclic graphs detected during test discovery to be more - actionable. -* Extracted `NamespacedHierarchicalStore` from JUnit Jupiter engine for reuse by other - test engines and their extensions. -* New dry-run mode to simulate test execution without actually running tests. Please refer - to the <<../user-guide/index.adoc#launcher-api-dry-run-mode, User Guide>> for details. -* Stack traces produced by failing tests are now pruned of calls from the `org.junit`, - `jdk.internal.reflect`, and `sun.reflect` packages. This feature can be disabled via a - configuration parameter. Please refer to the - <<../user-guide/index.adoc#stacktrace-pruning, User Guide>> for details. -* New `getAncestors()` method in `TestDescriptor`. - - -[[release-notes-5.10.0-junit-jupiter]] -=== JUnit Jupiter - -==== Bug Fixes - -* The extensions supporting `@MethodSource`, `@EnabledIf`, and `@DisabledIf` now load - classes by fully-qualified class name using the `ClassLoader` obtained from the test - class when possible. This allows classes to be resolved with custom `ClassLoader` - arrangements (such as OSGi). -* When converting an argument for a `@ParameterizedTest` method from a fully-qualified - class name (`String`) to a `Class`, the `ClassLoader` of the class in which the - `@ParameterizedTest` method is declared is now used to resolve the `Class` instead of - the _default_ `ClassLoader`. - -==== Deprecations and Breaking Changes - -* The `dynamic` parallel execution strategy now allows the thread pool to be saturated by - default. -* Implicit type conversion of boolean values like in `@CsvSource` is now stricter, only - allowing values `"true"` or `"false"` (case-insensitive), in order to make accidental - mistakes apparent and to avoid potential confusion. - -==== New Features and Improvements - -* Various "experimental" APIs have been promoted to "stable", including - `MethodOrderer`, `ClassOrderer`, `InvocationInterceptor`, - `LifecycleMethodExecutionExceptionHandler`, `@TempDir`, parallel execution annotations, - and others. -* `JAVA_22` has been added to the `JRE` enum for use with JRE-based execution conditions. -* New `reason` attribute in `@Execution` which can be used to document the reason for - using the selected execution mode. -* New `junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor` configuration - parameter to set the maximum pool size factor. -* New `junit.jupiter.execution.parallel.config.dynamic.saturate` configuration - parameter to disable pool saturation. -* `@RepeatedTest` can now be configured with a failure threshold which signifies the - number of failures after which remaining repetitions will be automatically skipped. See - the <<../user-guide/index.adoc#writing-tests-repeated-tests, User Guide>> for details. -* If `@MethodSource` is used with a non-static factory method that should be `static`, the - exception thrown now provides the user a meaningful explanation of how to address the - problem. -* `@EmptySource` now supports additional types, including `Collection` and `Map` subtypes - with a public no-arg constructor. -* New `ArgumentsAccessor.getInvocationIndex()` method that supplies the index of a - `@ParameterizedTest` invocation. -* New `AnnotationBasedArgumentsProvider` convenience base class which implements both - `ArgumentsProvider` and `AnnotationConsumer`. -* New `AnnotationBasedArgumentConverter` convenience base class which implements both - `ArgumentConverter` and `AnnotationConsumer`. -* `@TempDir` can now be used as a meta-annotation in order to create custom _composed - annotations_. See the `@JimfsTempDir` example in the - <<../user-guide/index.adoc#writing-tests-built-in-extensions-TempDirectory, User Guide>> - for details. -* `@TempDir` now successfully cleans up files and directories on Windows that are set to - read-only. -* New `TempDirFactory` SPI for customizing how the `@TempDir` extension creates temporary - directories. See the - <<../user-guide/index.adoc#writing-tests-built-in-extensions-TempDirectory, User Guide>> - for details. -* The <<../user-guide/index.adoc#extensions-RandomNumberExtension, User Guide>> now - includes an example implementation of the `RandomNumberExtension` in order to improve - the documentation for extension registration via `@ExtendWith` on fields. -* The scope of applicability for `TestWatcher` implementations is now more extensively - documented in the User Guide and Javadoc. -* `DisplayNameGenerator` methods are now allowed to return `null`, in order to signal to - fall back to the default display name generator. - - -[[release-notes-5.10.0-junit-vintage]] -=== JUnit Vintage - -No changes. +For complete details consult the +https://junit.org/junit5/docs/5.10.0/release-notes/index.html[5.10.0 Release Notes] online. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.1.adoc new file mode 100644 index 000000000000..af0ac43916b4 --- /dev/null +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.1.adoc @@ -0,0 +1,60 @@ +[[release-notes-5.10.1]] +== 5.10.1 + +*Date of Release:* November 5, 2023 + +*Scope:* minor bug fixes and improvements since 5.10.0. + +For a complete list of all _closed_ issues and pull requests for this release, consult the +link:{junit5-repo}+/milestone/72?closed=1+[5.10.1] milestone page in the +JUnit repository on GitHub. + + +[[release-notes-5.10.1-junit-platform]] +=== JUnit Platform + +==== Bug Fixes + +* Field predicates are now applied while searching the type hierarchy. This fixes bugs in + `findFields(...)` and `streamFields(...)` in `ReflectionSupport` as well as + `findAnnotatedFields(...)` and `findAnnotatedFieldValues(...)` in `AnnotationSupport`. + - See link:https://github.com/junit-team/junit5/issues/3532[issue 3532] for details. +* Method predicates are now applied while searching the type hierarchy. This fixes bugs + in `findMethods(...)` and `streamMethods(...)` in `ReflectionSupport` as well as + `findAnnotatedMethods(...)` in `AnnotationSupport`. + - See link:https://github.com/junit-team/junit5/issues/3498[issue 3498] for details. + + +[[release-notes-5.10.1-junit-jupiter]] +=== JUnit Jupiter + +==== Bug Fixes + +* A package-private static field annotated with `@TempDir` is no longer _shadowed_ by a + non-static field annotated with `@TempDir` when the non-static field resides in a + different package and has the same name as the static field. + - See link:https://github.com/junit-team/junit5/issues/3532[issue 3532] for details. +* A package-private class-level lifecycle method annotated with `@BeforeAll` or + `@AfterAll` is no longer _shadowed_ by a method-level lifecycle method annotated with + `@BeforeEach` or `@AfterEach` when the method-level lifecycle method resides in a + different package and has the same name as the class-level lifecycle method. + - See link:https://github.com/junit-team/junit5/issues/3498[issue 3498] for details. +* The `ON_SUCCESS` cleanup mode of `@TempDir` now takes into account failures of test + methods and nested tests when it's declared on the class level, e.g. as a static field. +* The `RandomNumberExtension` example in the + <<../user-guide/index.adoc#extensions-RandomNumberExtension, User Guide>> has been + updated to properly support `Integer` types as well as non-static field injection. + +==== New Features and Improvements + +* Improved Javadoc for `Assertions.assertTimeoutPreemptively` regarding thread interrupt. +* Documentation for `@Disabled` and conditional annotations now explicitly explains that + such annotations are not inherited by subclasses. + + +[[release-notes-5.10.1-junit-vintage]] +=== JUnit Vintage + +==== Bug Fixes + +* Fixed reporting for JUnit 3 test classes that use JUnit 4's `@Ignored` annotation. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.2.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.2.adoc new file mode 100644 index 000000000000..1558506cb03c --- /dev/null +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.2.adoc @@ -0,0 +1,63 @@ +[[release-notes-5.10.2]] +== 5.10.2 + +*Date of Release:* February 4, 2024 + +*Scope:* minor bug fixes and changes since 5.10.1. + +For a complete list of all _closed_ issues and pull requests for this release, consult the +link:{junit5-repo}+/milestone/73?closed=1+[5.10.2] milestone page in the JUnit repository +on GitHub. + + +[[release-notes-5.10.2-junit-platform]] +=== JUnit Platform + +==== Bug Fixes + +* The `junit-platform-launcher` may now be used as a Java module when + `junit.platform.launcher.interceptors.enabled` is set to `true`. + - See issue link:https://github.com/junit-team/junit5/issues/3561[#3561] for details. + +==== Deprecations and Breaking Changes + +* Field predicates are no longer applied eagerly while searching the type hierarchy. + - This reverts changes made in 5.10.1 that affected `findFields(...)` and + `streamFields(...)` in `ReflectionSupport` as well as `findAnnotatedFields(...)` and + `findAnnotatedFieldValues(...)` in `AnnotationSupport`. + - See issue link:https://github.com/junit-team/junit5/issues/3638[#3638] for details. +* Method predicates are no longer applied eagerly while searching the type hierarchy. + - This reverts changes made in 5.10.1 that affected `findMethods(...)` and + `streamMethods(...)` in `ReflectionSupport` as well as `findAnnotatedMethods(...)` in + `AnnotationSupport`. + - See issue link:https://github.com/junit-team/junit5/issues/3600[#3600] for details. + + +[[release-notes-5.10.2-junit-jupiter]] +=== JUnit Jupiter + +==== Bug Fixes + +* JUnit Jupiter once again properly detects when a `@Test` method is overridden in a + subclass. + - See issue link:https://github.com/junit-team/junit5/issues/3600[#3600] for details. + +==== Deprecations and Breaking Changes + +* A package-private static field annotated with `@TempDir` is once again _shadowed_ by a + non-static field annotated with `@TempDir` when the non-static field resides in a + different package and has the same name as the static field. + - This reverts changes made in 5.10.1. + - See issue link:https://github.com/junit-team/junit5/issues/3638[#3638] for details. +* A package-private class-level lifecycle method annotated with `@BeforeAll` or + `@AfterAll` is once again _shadowed_ by a method-level lifecycle method annotated with + `@BeforeEach` or `@AfterEach` when the method-level lifecycle method resides in a + different package and has the same name as the class-level lifecycle method. + - This reverts changes made in 5.10.1. + - See issue link:https://github.com/junit-team/junit5/issues/3600[#3600] for details. + + +[[release-notes-5.10.2-junit-vintage]] +=== JUnit Vintage + +No changes. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.3.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.3.adoc new file mode 100644 index 000000000000..f98b8fd062f7 --- /dev/null +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.3.adoc @@ -0,0 +1,41 @@ +[[release-notes-5.10.3]] +== 5.10.3 + +*Date of Release:* June 27, 2024 + +*Scope:* Bug fixes and enhancements since 5.10.2 + +For a complete list of all _closed_ issues and pull requests for this release, consult the +link:{junit5-repo}+/milestone/78?closed=1+[5.10.3] milestone page in the JUnit repository +on GitHub. + + +[[release-notes-5.10.3-junit-platform]] +=== JUnit Platform + +==== Bug Fixes + +* The `junit-platform-suite-engine` now includes configuration provided via + `@ConfigurationParameter` when selecting tests by `UniqueId`. +* In order to support using `@EnabledInNativeImage` on test classes, + `UniqueIdTrackingListener` now tracks descendants of skipped test containers. +* Attempting to deserialize a `TestIdentifier` no longer causes a `NullPointerException` + when there is no parent identifier. See + link:https://github.com/junit-team/junit5/issues/3819[issue 3819]. + + +[[release-notes-5.10.3-junit-jupiter]] +=== JUnit Jupiter + +==== Bug Fixes + +* `TempDir` suppresses `NoSuchFileException` when deleting files that may have been deleted + by another thread or process. +* `MethodOrderer.Random` and `ClassOrderer.Random` now use the same default seed that is + generated during class initialization. + + +[[release-notes-5.10.3-junit-vintage]] +=== JUnit Vintage + +No changes. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.4.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.4.adoc new file mode 100644 index 000000000000..ed574643f6f1 --- /dev/null +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.4.adoc @@ -0,0 +1,44 @@ +[[release-notes-5.10.4]] +== 5.10.4 + +*Date of Release:* September 24, 2024 + +*Scope:* Bug fixes and enhancements since 5.10.3 + +For a complete list of all _closed_ issues and pull requests for this release, consult the +link:{junit5-repo}+/milestone/79?closed=1+[5.10.4] milestone page in the JUnit repository +on GitHub. + + +[[release-notes-5.10.4-junit-platform]] +=== JUnit Platform + +==== Bug Fixes + +* Fix support for disabling ANSI colors on the console when the `NO_COLOR` environment + variable is available. +* Fixed potential locking issue with `ExclusiveResource` in the + `HierarchicalTestExecutorService`, which could lead to deadlocks in certain scenarios. + +==== New Features and Improvements + +* Improve parallelism and reduce number of blocked threads used by + `HierarchicalTestEngine` implementations when parallel execution is enabled and the + global read-write lock is used. + + +[[release-notes-5.10.4-junit-jupiter]] +=== JUnit Jupiter + +==== New Features and Improvements + +* `JAVA_23` and `JAVA_24` have been added to the `JRE` enum for use with JRE-based + execution conditions. +* Improve parallelism and reduce number of blocked threads in the presence of `@Isolated` + tests when parallel execution is enabled + + +[[release-notes-5.10.4-junit-vintage]] +=== JUnit Vintage + +No changes. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.5.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.5.adoc new file mode 100644 index 000000000000..72c85d21cdeb --- /dev/null +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.5.adoc @@ -0,0 +1,33 @@ +[[release-notes-5.10.5]] +== 5.10.5 + +*Date of Release:* October 4, 2024 + +*Scope:* Bug fixes and enhancements since 5.10.4 + +For a complete list of all _closed_ issues and pull requests for this release, consult the +link:{junit5-repo}+/milestone/83?closed=1+[5.10.5] milestone page in the JUnit repository +on GitHub. + + +[[release-notes-5.10.5-junit-platform]] +=== JUnit Platform + +[[release-notes-5.10.5-junit-platform-bug-fixes]] +==== Bug Fixes + +* Fix regression in parallel execution that was introduced in 5.10.4 regarding global + read-write locks. When such a lock was declared on descendants of top-level nodes in the + test tree, such as Cucumber scenarios, test execution failed. + + +[[release-notes-5.10.5-junit-jupiter]] +=== JUnit Jupiter + +No changes. + + +[[release-notes-5.10.5-junit-vintage]] +=== JUnit Vintage + +No changes. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0.adoc new file mode 100644 index 000000000000..862e81213d53 --- /dev/null +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0.adoc @@ -0,0 +1,196 @@ +[[release-notes-5.11.0]] +== 5.11.0 + +*Date of Release:* August 14, 2024 + +*Scope:* + +* `@FieldSource` annotation for use with `@ParameterizedTest` methods +* Repeatable `@..Source` annotations for parameterized tests +* Enhancements for authoring dynamic and parameterized tests +* `@AutoClose` annotation to automatically close field resources in tests +* `ConversionSupport` utility for converting from a string to a supported target type +* Extensible syntax for specifying discovery selectors +* `@BeforeSuite` and `@AfterSuite` annotations +* Classpath resource scanning support for engines +* Numerous bug fixes and enhancements regarding field and method search algorithms + +For a complete list of all _closed_ issues and pull requests for this release, consult the +link:{junit5-repo}+/milestone/68?closed=1+[5.11.0-M1], +link:{junit5-repo}+/milestone/74?closed=1+[5.11.0-M2], +link:{junit5-repo}+/milestone/77?closed=1+[5.11.0-RC1], and +link:{junit5-repo}+/milestone/76?closed=1+[5.11.0] milestone pages in the JUnit repository +on GitHub. + + +[[release-notes-5.11.0-overall-improvements]] +=== Overall Improvements + +[[release-notes-5.11.0-overall-new-features-and-improvements]] +==== New Features and Improvements + +* Java classes in published artifacts are now compiled with the `-parameters` option of + `javac` and thus now contain metadata for reflection on parameters such as their names. + + +[[release-notes-5.11.0-junit-platform]] +=== JUnit Platform + +[[release-notes-5.11.0-junit-platform-bug-fixes]] +==== Bug Fixes + +* Field and method search algorithms now adhere to standard Java semantics regarding + whether a given field or method is visible or overridden according to the rules of the + Java language. See the new + <<../user-guide/index.adoc#extensions-supported-utilities-search-semantics, Field and + Method Search Semantics>> section of the User Guide for details. +* `ReflectionSupport.findFields(...)` now returns a distinct set of fields. +* Fixed parsing of recursive jar URIs which allows the JUnit Platform Launcher to be used + inside Spring Boot executable jars for Spring Boot 3.2 and later. +* The `junit-platform-suite-engine` now includes configuration provided via + `@ConfigurationParameter` when selecting tests by `UniqueId` (backported to 5.10.3). +* In order to support using `@EnabledInNativeImage` on test classes, + `UniqueIdTrackingListener` now tracks descendants of skipped test containers (backported + to 5.10.3). +* Attempting to deserialize a `TestIdentifier` no longer causes a `NullPointerException` + when there is no parent identifier. See + link:https://github.com/junit-team/junit5/issues/3819[issue 3819] (backported to + 5.10.3). + +[[release-notes-5.11.0-junit-platform-deprecations-and-breaking-changes]] +==== Deprecations and Breaking Changes + +* As mentioned in the _Bug Fixes_ section above, field and method search algorithms now + adhere to standard Java semantics regarding whether a given field or method is visible + or overridden according to the rules of the Java language. The changes in the search + algorithms may, however, result in breaking changes for some use cases. In light of + that, it is possible to revert to the previous "legacy semantics". See the new + <<../user-guide/index.adoc#extensions-supported-utilities-search-semantics, Field and + Method Search Semantics>> section of the User Guide for details. + +[[release-notes-5.11.0-junit-platform-new-features-and-improvements]] +==== New Features and Improvements + +* New `ConversionSupport` utility in `junit-platform-commons` which exposes the conversion + logic that was previously private to JUnit Jupiter's `@ParameterizedTest` infrastructure + -- for use in third-party extensions and test engines. +* Error messages for type mismatches in `NamespacedHierarchicalStore` now include the + actual type and value in addition to the required type. +* Updated `open-test-reporting` dependency to `0.1.0-M2`. +* All Platform implementations of `DiscoverySelector` now have a parseable string + representation that can be generated by calling the new + `DiscoverySelector.toIdentifier()` method and `toString()` on the returned + `DiscoverySelectorIdentifier`. This string representation can be used to reconstruct + the original `DiscoverySelector` by calling the new `DiscoverySelectors.parse()` method. + This change will allow build tools and IDEs to provide generic mechanisms for specifying + selectors on the command line or in configuration files without having to support each + selector type individually. + - The Console Launcher supports specifying selectors via their identifiers using the + `--select` option. For example, `--select class:foo.Bar` will run all tests in the + `foo.Bar` class. + - Similarly, the JUnit Platform Suite engine provides a new `@Select("")` + annotation. +* The Console Launcher now provides a `--version` option. +* `NamespacedHierarchicalStore` now throws an `IllegalStateException` for any attempt to + modify or query the store after it has been closed. In addition, an attempt to close a + store that has already been closed will have no effect. + - See link:https://github.com/junit-team/junit5/issues/3614[issue 3614] for details. +* Introduce `@ConfigurationParametersResource` for `@Suite` classes and + `--config-resource` option for ConsoleLauncher that allow specifying additional + properties files on the classpath as sources of configuration parameters. +* New `rootCause()` condition in `TestExecutionResultConditions` that matches if an + exception's _root_ cause matches all supplied conditions, for use with the + `EngineTestKit`. +* `ReflectionSupport` now supports scanning for classpath resources. +* Introduce `@BeforeSuite` and `@AfterSuite` lifecycle methods for `@Suite` classes. + + +[[release-notes-5.11.0-junit-jupiter]] +=== JUnit Jupiter + +[[release-notes-5.11.0-junit-jupiter-bug-fixes]] +==== Bug Fixes + +* Due to changes in the JUnit Platform regarding field and method search algorithms (see + <> above), numerous bugs have been + addressed within JUnit Jupiter, including but not limited to the following. + ** Two `@TempDir` fields with the same name in a superclass and subclass will now both + be injected. + ** Two `@Test` methods with the same signature in a superclass and subclass will now + both be invoked, as long as the `@Test` method in the subclass does not override the + `@Test` method in the superclass, which can occur if the superclass method is `private` + or if the superclass method is package-private and resides in a different package than + the subclass. + *** The same applies to other types of test methods (`@TestFactory`, + `@ParameterizedTest`, etc.) as well as lifecycle methods (`@BeforeAll`, + `@AfterAll`, `@BeforeEach`, and `@AfterEach`). +* `MethodOrderer.Random` and `ClassOrderer.Random` now use the same default seed that is + generated during class initialization (backported to 5.10.3). +* `TestInstancePostProcessor` extensions can now be registered via the `@ExtendWith` + annotation on non-static fields. +* Methods and constructors invoked via `ExecutableInvoker` now use the same extensions as + the context they are invoked from and the same `ExtensionContext` is passed to + registered instances of `ParameterResolver`. +* The `org.junit.jupiter.api.extension.support` package is now exported from the + `org.junit.jupiter.api` module + +[[release-notes-5.11.0-junit-jupiter-deprecations-and-breaking-changes]] +==== Deprecations and Breaking Changes + +* The registration order of extensions was changed to allow non-static fields to be + processed earlier. This change may affect extensions that rely on the order of + registration. +* Kotlin support now depends on Kotlin API and language version 1.6; whereas, it + previously depended on version 1.3. + +[[release-notes-5.11.0-junit-jupiter-new-features-and-improvements]] +==== New Features and Improvements + +* New `@FieldSource` annotation for use with `@ParameterizedTest` methods which allows + you to source arguments from a local field or an external field referenced by + fully qualified field name. This feature is similar to the existing `@MethodSource` + feature. See the + <<../user-guide/index.adoc#writing-tests-parameterized-tests-sources-FieldSource, User + Guide>> for details. +* New `@AutoClose` annotation that can be applied to fields within tests to automatically + close the annotated resource after test execution. See the + <<../user-guide/index.adoc#writing-tests-built-in-extensions-AutoClose, User Guide>> for + details. +* `JAVA_23` and `JAVA_24` have been added to the `JRE` enum for use with JRE-based + execution conditions. +* New <<../user-guide/index.adoc#writing-tests-exceptions, Exception Handling>> + documentation in the User Guide. +* Improved documentation for <<../user-guide/index.adoc#writing-tests-assumptions, + Assumptions>> in the User Guide. +* Improved Javadoc for `assertThrows()` and `assertThrowsExactly()` to make it clear that + the supplied message is not the _expected message_ of the thrown exception. +* Improved documentation for semantics of a disabled test regarding class-level lifecycle + methods and callbacks. +* `@..Source` annotations for parameterized tests can now be used as repeatable + annotations. See the + <<../user-guide/index.adoc#writing-tests-parameterized-repeatable-sources, User Guide>> + for details. +* New `argumentSet()` factory method for providing a custom name for an entire set of + arguments for a `@ParameterizedTest`. See the + <<../user-guide/index.adoc#writing-tests-parameterized-tests-display-names, User Guide>> + for details. +* New `assertInstanceOf` methods added for Kotlin following up with similar Java + `assertInstanceOf` methods introduced in `5.8` version. +* New generators in `DynamicTest` that take a `Stream`/`Iterator` of `Named` + along with a convenient `NamedExecutable` interface that can simplify writing dynamic + tests, in particular in recent versions of Java that support records. +* `@TempDir` now suppresses `NoSuchFileException` when attempting to delete files that may + have been already deleted by another thread or process. +* `@TempDir` now fails fast in case `TempDirFactory::createTempDirectory` returns + `null`, a file, or a symbolic link to a file. +* `@TempDir` now fails fast in case the annotated target is of type `File` and + `TempDirFactory::createTempDirectory` returns a `Path` that does not belong to the + default file system. +* Allow potentially unlimited characters per column in `@CsvSource` and `@CsvFileSource` + by specifying `maxCharsPerColumn = -1`. + + +[[release-notes-5.11.0-junit-vintage]] +=== JUnit Vintage + +No changes. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.1.adoc new file mode 100644 index 000000000000..80af4a1a53d3 --- /dev/null +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.1.adoc @@ -0,0 +1,55 @@ +[[release-notes-5.11.1]] +== 5.11.1 + +*Date of Release:* September 25, 2024 + +*Scope:* Bug fixes and enhancements since 5.11.0 + +For a complete list of all _closed_ issues and pull requests for this release, consult the +link:{junit5-repo}+/milestone/80?closed=1+[5.11.1] milestone page in the JUnit repository +on GitHub. + + +[[release-notes-5.11.1-junit-platform]] +=== JUnit Platform + +[[release-notes-5.11.1-junit-platform-bug-fixes]] +==== Bug Fixes + +* Fix support for disabling ANSI colors on the console when the `NO_COLOR` environment + variable is available. +* `NamespacedHierarchicalStore` no longer throws an exception after it has been closed if + the store is queried via one of the `get(...)` or `getOrComputeIfAbsent(...)` methods; + however, if a `getOrComputeIfAbsent(...)` invocation results in the computation of a new + value, an exception will still be thrown. +* Fixed potential locking issue with `ExclusiveResource` in the + `HierarchicalTestExecutorService`, which could lead to deadlocks in certain scenarios. + +[[release-notes-5.11.1-junit-platform-new-features-and-improvements]] +==== New Features and Improvements + +* Improve parallelism and reduce number of blocked threads used by + `HierarchicalTestEngine` implementations when parallel execution is enabled and the + global read-write lock is used. + + +[[release-notes-5.11.1-junit-jupiter]] +=== JUnit Jupiter + +[[release-notes-5.11.1-junit-jupiter-bug-fixes]] +==== Bug Fixes + +* `TestWatcher` callback methods can once again access data in the + `ExtensionContext.Store`. + +[[release-notes-5.11.1-junit-jupiter-new-features-and-improvements]] +==== New Features and Improvements + +* Improve parallelism and reduce number of blocked threads in the presence of `@Isolated` + tests when parallel execution is enabled + + +[[release-notes-5.11.1-junit-vintage]] +=== JUnit Vintage + +No changes. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.2.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.2.adoc new file mode 100644 index 000000000000..0bb4b292e1d9 --- /dev/null +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.2.adoc @@ -0,0 +1,33 @@ +[[release-notes-5.11.2]] +== 5.11.2 + +*Date of Release:* October 4, 2024 + +*Scope:* Bug fixes and enhancements since 5.11.1 + +For a complete list of all _closed_ issues and pull requests for this release, consult the +link:{junit5-repo}+/milestone/82?closed=1+[5.11.2] milestone page in the JUnit repository +on GitHub. + + +[[release-notes-5.11.2-junit-platform]] +=== JUnit Platform + +[[release-notes-5.11.2-junit-platform-bug-fixes]] +==== Bug Fixes + +* Fix regression in parallel execution that was introduced in 5.11.1 regarding global + read-write locks. When such a lock was declared on descendants of top-level nodes in the + test tree, such as Cucumber scenarios, test execution failed. + + +[[release-notes-5.11.2-junit-jupiter]] +=== JUnit Jupiter + +No changes. + + +[[release-notes-5.11.2-junit-vintage]] +=== JUnit Vintage + +No changes. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.3.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.3.adoc new file mode 100644 index 000000000000..0588af0f1cc7 --- /dev/null +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.3.adoc @@ -0,0 +1,40 @@ +[[release-notes-5.11.3]] +== 5.11.3 + +*Date of Release:* October 21, 2024 + +*Scope:* Bug fixes and enhancements since 5.11.2 + +For a complete list of all _closed_ issues and pull requests for this release, consult the +link:{junit5-repo}+/milestone/84?closed=1+[5.11.3] milestone page in the JUnit repository +on GitHub. + + +[[release-notes-5.11.3-junit-platform]] +=== JUnit Platform + +[[release-notes-5.11.3-junit-platform-bug-fixes]] +==== Bug Fixes + +* Fixed a regression in method search algorithms introduced in 5.11.0 when classes reside + in the default package and using a Java 8 runtime. + + +[[release-notes-5.11.3-junit-jupiter]] +=== JUnit Jupiter + +[[release-notes-5.11.3-junit-jupiter-bug-fixes]] +==== Bug Fixes + +* Extensions can once again be registered via multiple `@ExtendWith` meta-annotations on + the same composed annotation on a field within a test class. +* `@ExtendWith` annotations can now also be repeated when used directly on fields and + parameters. +* All `@...Source` annotations of parameterized tests can now also be repeated when used + as meta annotations. + + +[[release-notes-5.11.3-junit-vintage]] +=== JUnit Vintage + +No changes. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.0.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.0.adoc deleted file mode 100644 index e5ee0004d778..000000000000 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.0.adoc +++ /dev/null @@ -1,21 +0,0 @@ -[[release-notes-5.9.0]] -== 5.9.0 - -*Date of Release:* July 26, 2022 - -*Scope:* - -* XML reports in the new https://github.com/ota4j-team/open-test-reporting[Open Test Reporting] - format -* Configurable cleanup mode for `@TempDir` -* Configurable thread mode for `@Timeout` -* Conditional execution based on OS architectures -* New `TestInstancePreConstructCallback` extension API -* Reusable parameter resolution for custom extension methods via `ExecutableInvoker` -* Parameter injection for `@MethodSource` methods -* New `IterationSelector` -* Various improvements to `ConsoleLauncher` -* Numerous bug fixes and minor improvements - -For complete details consult the -https://junit.org/junit5/docs/5.9.0/release-notes/index.html[5.9.0 Release Notes] online. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.1.adoc deleted file mode 100644 index 3c82815368e1..000000000000 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.1.adoc +++ /dev/null @@ -1,53 +0,0 @@ -[[release-notes-5.9.1]] -== 5.9.1 - -*Date of Release:* September 20, 2022 - -*Scope:* - -* New `@EnabledInNativeImage` and `@DisabledInNativeImage` annotations for testing in - GraalVM native images. -* Minor bug fixes and enhancements since 5.9.0 - -For a complete list of all _closed_ issues and pull requests for this release, consult the -link:{junit5-repo}+/milestone/63?closed=1+[5.9.1] milestone page in the JUnit repository -on GitHub. - - -[[release-notes-5.9.1-junit-platform]] -=== JUnit Platform - -==== Bug Fixes - -* `ReflectionSupport.findMethods(...)` now returns a distinct set of methods. -* Execution in GraalVM native images no longer requires `--initialize-at-build-time` for - `OpenTestReportGeneratingListener`. - - -[[release-notes-5.9.1-junit-jupiter]] -=== JUnit Jupiter - -==== Bug Fixes - -* Headers provided via the `value` attribute in `@CsvSource` for a `@ParameterizedTest` - are now properly parsed when the `useHeadersInDisplayName` attribute is set to `true`. -* A `@ParameterizedTest` method configured with a `@MethodSource` annotation that - references a factory method inherited from multiple interfaces no longer fails with an - exception stating that multiple factory methods with the same name were found. -* A `@ParameterizedTest` method configured with a `@MethodSource` annotation that - references a factory method whose name is the same as other non-factory methods in the - same class no longer fails with an exception stating that multiple factory methods with - the same name were found. -* Assertion failures thrown from methods with applied timeouts using `ThreadMode.SEPARATE` - are now properly reported. - -==== New Features and Improvements - -* New `@EnabledInNativeImage` and `@DisabledInNativeImage` annotations for enabling and - disabling tests within a GraalVM native image. - - -[[release-notes-5.9.1-junit-vintage]] -=== JUnit Vintage - -No changes. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.2.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.2.adoc deleted file mode 100644 index 249e805dd0f0..000000000000 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.2.adoc +++ /dev/null @@ -1,61 +0,0 @@ -[[release-notes-5.9.2]] -== 5.9.2 - -*Date of Release:* January 10, 2023 - -*Scope:* Bug fixes and enhancements since 5.9.1 - -For a complete list of all _closed_ issues and pull requests for this release, consult the -link:{junit5-repo}+/milestones/5.9.2+[5.9.2] milestone page in the JUnit repository on -GitHub. - - -[[release-notes-5.9.2-junit-platform]] -=== JUnit Platform - -==== Bug Fixes - -* The Java 7 based constructor for `ForkJoinPool` is no longer accidentally used on Java 9 - or higher when invalid `ParallelExecutionConfiguration` is provided. Instead, an - exception is thrown for invalid configuration, thereby preventing invalid configuration - from being silently ignored. - -==== New Features and Improvements - -* New `TestPlan.getTestIdentifier(UniqueId)` and `TestPlan.getChildren(UniqueId)` methods - to avoid parsing unique IDs unnecessarily during test execution. -* Support for limiting the `max-pool-size` for parallel execution via a configuration - parameter. -* Suite discovery now ignores cycles encountered in a test suite and logs an informational - message at `CONFIG` level instead of throwing an exception. - - -[[release-notes-5.9.2-junit-jupiter]] -=== JUnit Jupiter - -==== Bug Fixes - -* New `@MethodSource` syntax for explicitly selecting an overloaded local factory method - without specifying its fully qualified name. - -==== Deprecations and Breaking Changes - -* The `fixed` parallel execution strategy now allows the thread pool to be saturated by - default. - -==== New Features and Improvements - -* `JAVA_21` has been added to the `JRE` enum for use with JRE-based execution conditions. -* New `junit.jupiter.execution.parallel.config.fixed.max-pool-size` configuration - parameter to set the maximum pool size. -* New `junit.jupiter.execution.parallel.config.fixed.saturate` configuration parameter to - disable pool saturation. - - -[[release-notes-5.9.2-junit-vintage]] -=== JUnit Vintage - -==== Bug Fixes - -* `Parameterized` tests are now properly reported when used in combination with the - `Enclosed` runner. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.3.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.3.adoc deleted file mode 100644 index 6d1020227e39..000000000000 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.3.adoc +++ /dev/null @@ -1,59 +0,0 @@ -[[release-notes-5.9.3]] -== 5.9.3 - -*Date of Release:* April 26, 2023 - -*Scope:* Bug fixes and enhancements since 5.9.2 - -For a complete list of all _closed_ issues and pull requests for this release, consult the -link:{junit5-repo}+/milestone/67?closed=1+[5.9.3] milestone page in the -JUnit repository on GitHub. - - -[[release-notes-5.9.3-junit-platform]] -=== JUnit Platform - -No changes. - - -[[release-notes-5.9.3-junit-jupiter]] -=== JUnit Jupiter - -==== Bug Fixes - -* Parameter types for _local_ `@MethodSource` factory method names are now validated. For - example, `@MethodSource("myFactory(example.NonexistentType)")` will now result in an - exception stating that `example.NonexistentType` cannot be resolved to a valid type. -* The syntax for parameter types in _local_ `@MethodSource` factory method names now - supports canonical array names -- for example, you may now specify `int[]` as in - `@MethodSource("myFactory(int[])")` instead of the _binary_ name `[I` as in - `@MethodSource("myFactory([I)")` (which was already supported) and - `@MethodSource("myFactory(java.lang.String[])")` instead of - `@MethodSource("myFactory([Ljava.lang.String;)")`. -* The search algorithm used to find `@MethodSource` factory methods now applies consistent - semantics for _local_ qualified method names and fully-qualified method names for - overloaded factory methods. -* The `+{displayName}+` placeholder for `@ParameterizedTest` invocation names is no longer - parsed using `java.text.MessageFormat`. Consequently, any text in the display name of - the `@ParameterizedTest` method will be included unmodified in the invocation display - name. For example, Kotlin method names and custom display names configured via - `@DisplayName` can now contain apostrophes (`'`) as well as text resembling - `MessageFormat` elements such as `+{0}+` or `+{data}+`. -* Exceptions thrown for files that cannot be deleted when cleaning up a temporary - directory created via `@TempDir` now include the root cause. -* Lifecycle methods are allowed to be declared as `private` again for backwards - compatibility; however, using `private` visibility for lifecycle methods is strongly - discouraged and will be disallowed in a future release. - -==== New Features and Improvements - -* The search algorithm used to find `@MethodSource` factory methods now falls back to - lenient search semantics when a factory method cannot be found by qualified name - (without a parameter list) and also provides better diagnostics when a unique factory - method cannot be found. - - -[[release-notes-5.9.3-junit-vintage]] -=== JUnit Vintage - -No changes. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-TEMPLATE.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-TEMPLATE.adoc index ef56e8c27ff9..fb490f9d0da1 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-TEMPLATE.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-TEMPLATE.adoc @@ -28,14 +28,17 @@ JUnit repository on GitHub. [[release-notes-VERSION-junit-platform]] === JUnit Platform +[[release-notes-VERSION-junit-platform-bug-fixes]] ==== Bug Fixes * ❓ +[[release-notes-VERSION-junit-platform-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ +[[release-notes-VERSION-junit-platform-new-features-and-improvements]] ==== New Features and Improvements * ❓ @@ -44,30 +47,36 @@ JUnit repository on GitHub. [[release-notes-VERSION-junit-jupiter]] === JUnit Jupiter +[[release-notes-VERSION-junit-jupiter-bug-fixes]] ==== Bug Fixes * ❓ +[[release-notes-VERSION-junit-jupiter-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ +[[release-notes-VERSION-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements * ❓ -= [[release-notes-VERSION-junit-vintage]] +[[release-notes-VERSION-junit-vintage]] === JUnit Vintage +[[release-notes-VERSION-junit-vintage-bug-fixes]] ==== Bug Fixes * ❓ +[[release-notes-VERSION-junit-vintage-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ +[[release-notes-VERSION-junit-vintage-new-features-and-improvements]] ==== New Features and Improvements * ❓ diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-reporting.adoc b/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-reporting.adoc index 655d6bc7903b..287917904eea 100644 --- a/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-reporting.adoc +++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-reporting.adoc @@ -136,3 +136,12 @@ $ java -jar junit-platform-console-standalone-{platform-version}.jar \ --config=junit.platform.reporting.open.xml.enabled=true \ --config=junit.platform.reporting.output.dir=reports ---- + +Configuration parameters can also be set in a custom properties file supplied as a classpath resource +via the `--config-resource` option: + +[source,console,subs=attributes+] +---- +$ java -jar junit-platform-console-standalone-{platform-version}.jar \ + --config-resource=configuration.properties +---- diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-suite-engine.adoc b/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-suite-engine.adoc index d38a312d799f..ebb1d6495b04 100644 --- a/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-suite-engine.adoc +++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-suite-engine.adoc @@ -48,3 +48,14 @@ include::{testDir}/example/SuiteDemo.java[tags=user_guide] NOTE: There are numerous configuration options for discovering and filtering tests in a test suite. Please consult the Javadoc of the `{suite-api-package}` package for a full list of supported annotations and further details. + +==== @BeforeSuite and @AfterSuite + +`@BeforeSuite` and `@AfterSuite` annotations can be used on methods inside a +`@Suite`-annotated class. They will be executed respectively before and after +all tests of the test suite. + +[source,java,indent=0] +---- +include::{testDir}/example/BeforeAndAfterSuiteDemo.java[tags=user_guide] +---- diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics/launcher-api.adoc b/documentation/src/docs/asciidoc/user-guide/advanced-topics/launcher-api.adoc index 18c0b261e299..e1fb5e37efca 100644 --- a/documentation/src/docs/asciidoc/user-guide/advanced-topics/launcher-api.adoc +++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics/launcher-api.adoc @@ -169,12 +169,22 @@ include::{testDir}/example/session/HttpTests.java[tags=user_guide] In order to intercept the creation of instances of `{Launcher}` and `{LauncherSessionListener}` and calls to the `discover` and `execute` methods of the former, clients can register custom implementations of `{LauncherInterceptor}` via Java's -`{ServiceLoader}` mechanism by additionally setting the +`{ServiceLoader}` mechanism by setting the `junit.platform.launcher.interceptors.enabled` <> to `true`. -A typical use case is to create a custom replace the `ClassLoader` used by the JUnit -Platform to load test classes and engine implementations. +[NOTE] +==== +Since interceptors are registered before the test run starts, the +`junit.platform.launcher.interceptors.enabled` _configuration parameter_ can only be +supplied as a JVM system property or via the JUnit Platform configuration file (see +<> for details). This _configuration parameter_ cannot be +supplied in the `LauncherDiscoveryRequest` that is passed to the `{Launcher}`. +==== + + +A typical use case is to create a custom interceptor to replace the `ClassLoader` used by +the JUnit Platform to load test classes and engine implementations. [source,java] ---- diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics/testkit.adoc b/documentation/src/docs/asciidoc/user-guide/advanced-topics/testkit.adoc index 665c4daea788..3e74e28b4b7d 100644 --- a/documentation/src/docs/asciidoc/user-guide/advanced-topics/testkit.adoc +++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics/testkit.adoc @@ -2,8 +2,9 @@ === JUnit Platform Test Kit The `junit-platform-testkit` artifact provides support for executing a test plan on the -JUnit Platform and then verifying the expected results. As of JUnit Platform 1.4, this -support is limited to the execution of a single `TestEngine` (see <>). +JUnit Platform and then verifying the expected results. As of JUnit Platform +{platform-version}, this support is limited to the execution of a single `TestEngine` (see +<>). [[testkit-engine]] ==== Engine Test Kit @@ -112,7 +113,7 @@ include::{testDir}/example/testkit/EngineTestKitFailedMethodDemo.java[tags=user_ <4> Filter by _test_ events. <5> Assert that the recorded _test_ events contain exactly one failing test named `failingTest` with an exception of type `ArithmeticException` and an error message - equal to `"/ by zero"`. + that ends with `"/ by zero"`. Although typically unnecessary, there are times when you need to verify **all** of the events fired during the execution of a `TestPlan`. The following test demonstrates how to diff --git a/documentation/src/docs/asciidoc/user-guide/appendix.adoc b/documentation/src/docs/asciidoc/user-guide/appendix.adoc index d92194fd2b79..2f3ae2bf5e93 100644 --- a/documentation/src/docs/asciidoc/user-guide/appendix.adoc +++ b/documentation/src/docs/asciidoc/user-guide/appendix.adoc @@ -35,7 +35,7 @@ artifacts are deployed to Sonatype's {snapshot-repo}[snapshots repository] under Support for discovering and executing tests on the JUnit Platform from the console. See <> for details. `junit-platform-console-standalone`:: - An executable JAR with all dependencies included is provided in Maven Central under the + An executable _Fat JAR_ that contains all dependencies is provided in Maven Central under the https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone[junit-platform-console-standalone] directory. See <> for details. `junit-platform-engine`:: @@ -107,7 +107,7 @@ artifacts are deployed to Sonatype's {snapshot-repo}[snapshots repository] under The _Bill of Materials_ POM provided under the following Maven coordinates can be used to ease dependency management when referencing multiple of the above artifacts using https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies[Maven] -or https://docs.gradle.org/current/userguide/managing_transitive_dependencies.html#sec:bom_import[Gradle]. +or https://docs.gradle.org/current/userguide/platforms.html#sub:bom_import[Gradle]. * *Group ID*: `org.junit` * *Artifact ID*: `junit-bom` @@ -133,100 +133,4 @@ following _OpenTest4J_ JAR. [[dependency-diagram]] === Dependency Diagram -[plantuml, component-diagram, svg] ----- -skinparam { - defaultFontName Open Sans -} - -package org.junit.jupiter { - [junit-jupiter] as jupiter - [junit-jupiter-api] as jupiter_api - [junit-jupiter-engine] as jupiter_engine - [junit-jupiter-params] as jupiter_params - [junit-jupiter-migrationsupport] as jupiter_migration_support -} - -package org.junit.vintage { - [junit-vintage-engine] as vintage_engine -} - -package org.junit.platform { - [junit-platform-commons] as commons - [junit-platform-console] as console - [junit-platform-engine] as engine - [junit-platform-jfr] as jfr - [junit-platform-launcher] as launcher - [junit-platform-reporting] as reporting - [junit-platform-runner] as runner - [junit-platform-suite] as suite - [junit-platform-suite-api] as suite_api - [junit-platform-suite-commons] as suite_commons - [junit-platform-suite-engine] as suite_engine - [junit-platform-testkit] as testkit -} - -package "JUnit 4" { - [junit:junit] as junit4 -} - -package org.opentest4j { - [opentest4j] -} - -package org.apiguardian { - [apiguardian-api] as apiguardian - note bottom of apiguardian #white - All artifacts except - opentest4j and junit:junit - have a dependency on this - artifact. The edges have - been omitted from this - diagram for the sake of - readability. - endnote -} - -jupiter ..> jupiter_api -jupiter ..> jupiter_params -jupiter ..> jupiter_engine - -jupiter_api ....> opentest4j -jupiter_api ...> commons - -jupiter_engine ...> engine -jupiter_engine ..> jupiter_api - -jupiter_params ..> jupiter_api -jupiter_migration_support ..> jupiter_api -jupiter_migration_support ...> junit4 - -console ..> launcher -console ..> reporting - -launcher ..> engine - -jfr ..> launcher - -engine ....> opentest4j -engine ..> commons - -reporting ..> launcher - -runner ..> suite_commons -runner ...> junit4 - -suite ..> suite_api -suite ..> suite_engine - -suite_engine ..> suite_commons - -suite_commons ..> launcher -suite_commons ..> suite_api - -testkit ....> opentest4j -testkit ..> launcher - -vintage_engine ...> engine -vintage_engine ..> junit4 ----- +image::component-diagram.svg[] diff --git a/documentation/src/docs/asciidoc/user-guide/extensions.adoc b/documentation/src/docs/asciidoc/user-guide/extensions.adoc index 09ca2ec5e88b..bffbd523d5ab 100644 --- a/documentation/src/docs/asciidoc/user-guide/extensions.adoc +++ b/documentation/src/docs/asciidoc/user-guide/extensions.adoc @@ -1,3 +1,6 @@ +:testDir: ../../../../src/test/java +:kotlinTestDir: ../../../../src/test/kotlin + [[extensions]] == Extension Model @@ -99,7 +102,7 @@ public @interface DatabaseAndWebServerExtension { The above examples demonstrate how `@ExtendWith` can be applied at the class level or at the method level; however, for certain use cases it makes sense for an extension to be registered declaratively at the field or parameter level. Consider a -`RandomNumberExtension` that generates random numbers that can be injected into a field or +`RandomNumberExtension` which generates random numbers that can be injected into a field or via a parameter in a constructor, test method, or lifecycle method. If the extension provides a `@Random` annotation that is meta-annotated with `@ExtendWith(RandomNumberExtension.class)` (see listing below), the extension can be used @@ -118,9 +121,9 @@ include::{testDir}/example/extensions/RandomNumberDemo.java[tags=user_guide] [[extensions-RandomNumberExtension]] The following code listing provides an example of how one might choose to implement such a `RandomNumberExtension`. This implementation works for the use cases in -`RandomNumberDemo`; however, it may not prove robust enough to cover all use cases – for -example, the random number generation support is limited to integers, it uses -`java.util.Random` instead of `java.security.SecureRandom`, etc. In any case, it is +`RandomNumberDemo`; however, it may not prove robust enough to cover all use cases -- for +example, the random number generation support is limited to integers; it uses +`java.util.Random` instead of `java.security.SecureRandom`; etc. In any case, it is important to note which extension APIs are implemented and for what reasons. Specifically, `RandomNumberExtension` implements the following extension APIs: @@ -144,6 +147,15 @@ using the `@Order` annotation. See the <> tip for `@RegisterExtension` fields for details. ==== +[TIP] +.Extension Inheritance +==== +Extensions registered declaratively via `@ExtendWith` on fields in superclasses will be +inherited. + +See <> for details. +==== + NOTE: `@ExtendWith` fields may be either `static` or non-static. The documentation on <> and <> for @@ -184,6 +196,15 @@ extensions to be registered last and _after_ callback extensions to be registere relative to other programmatically registered extensions. ==== +[TIP] +.Extension Inheritance +==== +Extensions registered via `@RegisterExtension` or `@ExtendWith` on fields in superclasses +will be inherited. + +See <> for details. +==== + NOTE: `@RegisterExtension` fields must not be `null` (at evaluation time) but may be either `static` or non-static. @@ -239,11 +260,8 @@ will be registered after the test class has been instantiated and after each reg (potentially injecting the instance of the extension to be used into the annotated field). Thus, if such an _instance extension_ implements class-level or instance-level extension APIs such as `BeforeAllCallback`, `AfterAllCallback`, or -`TestInstancePostProcessor`, those APIs will not be honored. By default, an instance -extension will be registered _after_ extensions that are registered at the method level -via `@ExtendWith`; however, if the test class is configured with -`@TestInstance(Lifecycle.PER_CLASS)` semantics, an instance extension will be registered -_before_ extensions that are registered at the method level via `@ExtendWith`. +`TestInstancePostProcessor`, those APIs will not be honored. Instance extensions will be +registered _before_ extensions that are registered at the method level via `@ExtendWith`. In the following example, the `docs` field in the test class is initialized programmatically by invoking a custom `lookUpDocsDir()` method and supplying the result @@ -292,11 +310,23 @@ support for `TestInfo`, `TestReporter`, etc.). [[extensions-registration-inheritance]] ==== Extension Inheritance -Registered extensions are inherited within test class hierarchies with top-down -semantics. Similarly, extensions registered at the class-level are inherited at the -method-level. Furthermore, a specific extension implementation can only be registered -once for a given extension context and its parent contexts. Consequently, any attempt to -register a duplicate extension implementation will be ignored. +Registered extensions are inherited within test class hierarchies with top-down semantics. +Similarly, extensions registered at the class-level are inherited at the method-level. +This applies to all extensions, independent of how they are registered (declaratively or +programmatically). + +This means that extensions registered declaratively via `@ExtendWith` on a superclass will +be registered before extensions registered declaratively via `@ExtendWith` on a subclass. + +Similarly, extensions registered programmatically via `@RegisterExtension` or +`@ExtendWith` on fields in a superclass will be registered before extensions registered +programmatically via `@RegisterExtension` or `@ExtendWith` on fields in a subclass, unless +`@Order` is used to alter that behavior (see <> for details). + +NOTE: A specific extension implementation can only be registered once for a given +extension context and its parent contexts. Consequently, any attempt to register a +duplicate extension implementation will be ignored. [[extensions-conditions]] === Conditional Test Execution @@ -442,6 +472,57 @@ constructor invocations, using the `{ExecutableInvoker}` available via the `getExecutableInvoker()` method in the `ExtensionContext`. ==== +[[extensions-parameter-resolution-conflicts]] +==== Parameter Conflicts + +If multiple implementations of `ParameterResolver` that support the same type are +registered for a test, a `ParameterResolutionException` will be thrown, with a +message to indicate that competing resolvers have been discovered. See the following +example: + +[source,java,indent=0] +.Conflicting parameter resolution due to multiple resolvers claiming support for integers +---- +include::{testDir}/example/extensions/ParameterResolverConflictDemo.java[tags=user_guide] +---- + +If the conflicting `ParameterResolver` implementations are applied to different test +methods as shown in the following example, no conflict occurs. + +[source,java,indent=0] +.Fine-grained registration to avoid conflict +---- +include::{testDir}/example/extensions/ParameterResolverNoConflictDemo.java[tags=user_guide] +---- + +If the conflicting `ParameterResolver` implementations need to be applied to the same test +method, you can implement a custom type or custom annotation as illustrated by +`{CustomTypeParameterResolver}` and `{CustomAnnotationParameterResolver}`, respectively. + +[source,java,indent=0] +.Custom type to resolve duplicate types +---- +include::{testDir}/example/extensions/ParameterResolverCustomTypeDemo.java[tags=user_guide] +---- + +A custom annotation makes the duplicate type distinguishable from its counterpart: + +[source,java,indent=0] +.Custom annotation to resolve duplicate types +---- +include::{testDir}/example/extensions/ParameterResolverCustomAnnotationDemo.java[tags=user_guide] +---- + +JUnit includes some built-in parameter resolvers that can cause conflicts if a resolver +attempts to claim their supported types. For example, `{TestInfo}` provides metadata about +tests. See <> for details. Third-party frameworks such +as Spring may also define parameter resolvers. Apply one of the techniques in this section +to resolve any conflicts. + +Parameterized tests are another potential source of conflict. Ensure that tests annotated +with `@ParameterizedTest` are not also annotated with `@Test` and see +<> for more details. + [[extensions-test-result-processing]] === Test Result Processing @@ -651,7 +732,6 @@ Please refer to the implementations of <> or <> which use this extension point to provide their functionality. - [[extensions-keeping-state]] === Keeping State in Extensions @@ -672,14 +752,43 @@ extension context lifecycle ends it closes its associated store. All stored valu that are instances of `CloseableResource` are notified by an invocation of their `close()` method in the inverse order they were added in. +An example implementation of `CloseableResource` is shown below, using an `HttpServer` +resource. + +[source,java,indent=0] +.`HttpServer` resource implementing `CloseableResource` +---- +include::{testDir}/example/extensions/HttpServerResource.java[tags=user_guide] +---- + +This resource can then be stored in the desired `ExtensionContext`. It may be stored at +class or method level, if desired, but this may add unnecessary overhead for this type of +resource. For this example it might be prudent to store it at root level and instantiate +it lazily to ensure it's only created once per test run and reused across different test +classes and methods. + +[source,java,indent=0] +.Lazily storing in root context with `Store.getOrComputeIfAbsent` +---- +include::{testDir}/example/extensions/HttpServerExtension.java[tags=user_guide] +---- + +[source,java,indent=0] +.A test case using the `HttpServerExtension` +---- +include::{testDir}/example/HttpServerDemo.java[tags=user_guide] +---- + +[[extensions-conditional-test-execution]] + [[extensions-supported-utilities]] === Supported Utilities in Extensions -The `junit-platform-commons` artifact exposes a package named -`{junit-platform-support-package}` that contains _maintained_ utility methods for working -with annotations, classes, reflection, and classpath scanning tasks. `TestEngine` and -`Extension` authors are encouraged to use these supported methods in order to align with -the behavior of the JUnit Platform. +The `junit-platform-commons` artifact provides _maintained_ utilities for working with +annotations, classes, reflection, classpath scanning, and conversion tasks. These +utilities can be found in the `{junit-platform-support-package}` and its subpackages. +`TestEngine` and `Extension` authors are encouraged to use these supported utilities in +order to align with the behavior of the JUnit Platform and JUnit Jupiter. [[extensions-supported-utilities-annotations]] ==== Annotation Support @@ -692,6 +801,8 @@ and fields in a class or interface. Some of these methods search on implemented interfaces and within class hierarchies to find annotations. Consult the Javadoc for `{AnnotationSupport}` for further details. +NOTE: See also: <> + [[extensions-supported-utilities-classes]] ==== Class Support @@ -708,6 +819,8 @@ class, and to find and invoke methods. Some of these methods traverse class hier to locate matching methods. Consult the Javadoc for `{ReflectionSupport}` for further details. +NOTE: See also: <> + [[extensions-supported-utilities-modifier]] ==== Modifier Support @@ -716,6 +829,46 @@ modifiers -- for example, to determine if a member is declared as `public`, `pri `abstract`, `static`, etc. Consult the Javadoc for `{ModifierSupport}` for further details. +[[extensions-supported-utilities-conversion]] +==== Conversion Support + +`ConversionSupport` (in the `org.junit.platform.commons.support.conversion` package) +provides support for converting from strings to primitive types and their corresponding +wrapper types, date and time types from the `java.time package`, and some additional +common Java types such as `File`, `BigDecimal`, `BigInteger`, `Currency`, `Locale`, `URI`, +`URL`, `UUID`, etc. Consult the Javadoc for `{ConversionSupport}` for further details. + +[[extensions-supported-utilities-search-semantics]] +==== Field and Method Search Semantics + +Various methods in `AnnotationSupport` and `ReflectionSupport` use search algorithms that +traverse type hierarchies to locate matching fields and methods – for example, +`AnnotationSupport.findAnnotatedFields(...)`, `ReflectionSupport.findMethods(...)`, etc. + +As of JUnit 5.11 (JUnit Platform 1.11), field and method search algorithms adhere to +standard Java semantics regarding whether a given field or method is visible or overridden +according to the rules of the Java language. + +Prior to JUnit 5.11, the field and method search algorithms applied what we now refer to +as "legacy semantics". Legacy semantics consider fields and methods to be _hidden_, +_shadowed_, or _superseded_ by fields and methods in super types (superclasses or +interfaces) based solely on the field's name or the method's signature, disregarding the +actual Java language semantics for visibility and the rules that determine if one method +overrides another method. + +Although the JUnit team recommends the use of the standard search semantics, developers +may optionally revert to the legacy semantics via the +`junit.platform.reflection.search.useLegacySemantics` JVM system property. + +For example, to enable legacy search semantics for fields and methods, you can start your +JVM with the following system property. + +`-Djunit.platform.reflection.search.useLegacySemantics=true` + +NOTE: Due to the low-level nature of the feature, the +`junit.platform.reflection.search.useLegacySemantics` flag can only be set via a JVM +system property. It cannot be set via a <>. [[extensions-execution-order]] === Relative Execution Order of User Code and Extensions @@ -842,31 +995,27 @@ callbacks implemented by `Extension2`. `Extension1` is therefore said to _wrap_ JUnit Jupiter also guarantees _wrapping_ behavior within class and interface hierarchies for user-supplied _lifecycle methods_ (see <>). -* `@BeforeAll` methods are inherited from superclasses as long as they are not _hidden_, - _overridden_, or _superseded_ (i.e., replaced based on signature only, irrespective of - Java's visibility rules). Furthermore, `@BeforeAll` methods from superclasses will be - executed **before** `@BeforeAll` methods in subclasses. +* `@BeforeAll` methods are inherited from superclasses as long as they are not + _overridden_. Furthermore, `@BeforeAll` methods from superclasses will be executed + **before** `@BeforeAll` methods in subclasses. ** Similarly, `@BeforeAll` methods declared in an interface are inherited as long as they - are not _hidden_ or _overridden_, and `@BeforeAll` methods from an interface will be - executed **before** `@BeforeAll` methods in the class that implements the interface. -* `@AfterAll` methods are inherited from superclasses as long as they are not _hidden_, - _overridden_, or _superseded_ (i.e., replaced based on signature only, irrespective of - Java's visibility rules). Furthermore, `@AfterAll` methods from superclasses will be - executed **after** `@AfterAll` methods in subclasses. + are not _overridden_, and `@BeforeAll` methods from an interface will be executed + **before** `@BeforeAll` methods in the class that implements the interface. +* `@AfterAll` methods are inherited from superclasses as long as they are not + _overridden_. Furthermore, `@AfterAll` methods from superclasses will be executed + **after** `@AfterAll` methods in subclasses. ** Similarly, `@AfterAll` methods declared in an interface are inherited as long as they - are not _hidden_ or _overridden_, and `@AfterAll` methods from an interface will be - executed **after** `@AfterAll` methods in the class that implements the interface. + are not _overridden_, and `@AfterAll` methods from an interface will be executed + **after** `@AfterAll` methods in the class that implements the interface. * `@BeforeEach` methods are inherited from superclasses as long as they are not - _overridden_ or _superseded_ (i.e., replaced based on signature only, irrespective of - Java's visibility rules). Furthermore, `@BeforeEach` methods from superclasses will be - executed **before** `@BeforeEach` methods in subclasses. + _overridden_. Furthermore, `@BeforeEach` methods from superclasses will be executed + **before** `@BeforeEach` methods in subclasses. ** Similarly, `@BeforeEach` methods declared as interface default methods are inherited as long as they are not _overridden_, and `@BeforeEach` default methods will be executed **before** `@BeforeEach` methods in the class that implements the interface. * `@AfterEach` methods are inherited from superclasses as long as they are not - _overridden_ or _superseded_ (i.e., replaced based on signature only, irrespective of - Java's visibility rules). Furthermore, `@AfterEach` methods from superclasses will be - executed **after** `@AfterEach` methods in subclasses. + _overridden_. Furthermore, `@AfterEach` methods from superclasses will be executed + **after** `@AfterEach` methods in subclasses. ** Similarly, `@AfterEach` methods declared as interface default methods are inherited as long as they are not _overridden_, and `@AfterEach` default methods will be executed **after** `@AfterEach` methods in the class that implements the interface. diff --git a/documentation/src/docs/asciidoc/user-guide/images/writing-tests_execution_mode.svg b/documentation/src/docs/asciidoc/user-guide/images/writing-tests_execution_mode.svg index de0814938cbc..2eb1229ba195 100644 --- a/documentation/src/docs/asciidoc/user-guide/images/writing-tests_execution_mode.svg +++ b/documentation/src/docs/asciidoc/user-guide/images/writing-tests_execution_mode.svg @@ -1,386 +1 @@ -2019-01-012019-01-012019-01-022019-01-022019-01-032019-01-032019-01-042019-01-042019-01-05A.test1() A.test2() B.test1() B.test2() A.test1() A.test2() B.test1() B.test2() A.test1() A.test2() B.test1() B.test2() A.test1() A.test2() B.test1() B.test2() (same_thread, same_thread)(same_thread, concurrent)(concurrent, same_thread)(concurrent, concurrent) \ No newline at end of file +001122334A.test1() A.test1() B.test1() A.test1() A.test2() A.test1() A.test2() B.test1() B.test2() A.test2() A.test2() B.test2() B.test1() B.test2() B.test1() B.test2() (same_thread, same_thread)(same_thread, concurrent)(concurrent, same_thread)(concurrent, concurrent)↓ threads | time → diff --git a/documentation/src/docs/asciidoc/user-guide/index.adoc b/documentation/src/docs/asciidoc/user-guide/index.adoc index 57e0f30948e0..9c34f8d33739 100644 --- a/documentation/src/docs/asciidoc/user-guide/index.adoc +++ b/documentation/src/docs/asciidoc/user-guide/index.adoc @@ -16,6 +16,7 @@ ifdef::backend-pdf[:imagesdir: {imagesoutdir}] // :sectnums: :toclevels: 4 +:last-update-label!: // include::{includedir}/link-attributes.adoc[] diff --git a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc index 1900545858fc..734071e821cb 100644 --- a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc +++ b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc @@ -7,13 +7,11 @@ [[running-tests-ide-intellij-idea]] ==== IntelliJ IDEA -IntelliJ IDEA supports running tests on the JUnit Platform since version 2016.2. For -details please see the -https://blog.jetbrains.com/idea/2016/08/using-junit-5-in-intellij-idea/[post on the -IntelliJ IDEA blog]. Note, however, that it is recommended to use IDEA 2017.3 or newer -since these newer versions of IDEA will download the following JARs automatically based -on the API version used in the project: `junit-platform-launcher`, -`junit-jupiter-engine`, and `junit-vintage-engine`. +IntelliJ IDEA supports running tests on the JUnit Platform since version 2016.2. For more +information, please consult this https://jb.gg/junit-idea/[IntelliJ IDEA resource]. Note, +however, that it is recommended to use IDEA 2017.3 or newer since more recent versions of +IDEA download the following JARs automatically based on the API version used in the +project: `junit-platform-launcher`, `junit-jupiter-engine`, and `junit-vintage-engine`. WARNING: IntelliJ IDEA releases prior to IDEA 2017.3 bundle specific versions of JUnit 5. Thus, if you want to use a newer version of JUnit Jupiter, execution of tests within the @@ -243,6 +241,8 @@ details). ---- test { systemProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager") + // Avoid overhead (see https://logging.apache.org/log4j/2.x/manual/jmx.html#enabling-jmx) + systemProperty("log4j2.disableJmx", "true") } ---- @@ -621,13 +621,32 @@ The `{ConsoleLauncher}` is a command-line Java application that lets you launch Platform from the console. For example, it can be used to run JUnit Vintage and JUnit Jupiter tests and print test execution results to the console. -An executable `junit-platform-console-standalone-{platform-version}.jar` with all -dependencies included is published in the {Maven_Central} repository under the +An executable _Fat JAR_ (`junit-platform-console-standalone-{platform-version}.jar`) that +contains the contents of all of its dependencies is published in the {Maven_Central} +repository under the https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone[junit-platform-console-standalone] -directory. It includes the following dependencies: +directory. It contains the contents of the following artifacts: include::{standaloneConsoleLauncherShadowedArtifactsFile}[] +[NOTE] +==== +Since the `junit-platform-console-standalone` JAR contains the contents of all of its +dependencies, its Maven POM does not declare any dependencies. + +Furthermore, it is not very likely that you would need to include a dependency on the +`junit-platform-console-standalone` artifact in your project's Maven POM or Gradle build +script. On the contrary, the executable `junit-platform-console-standalone` JAR is +typically invoked directly from the command line or a shell script without a build script. + +If you need to declare dependencies in your build script on some of the artifacts +contained in the `junit-platform-console-standalone` artifact, you should declare +dependencies only on the JUnit artifacts that are used in your project. To simplify +dependency management of JUnit artifacts in your build, you may wish to use the +`junit-jupiter` aggregator artifact or `junit-bom`. See <> for +details. +==== + You can https://docs.oracle.com/javase/tutorial/deployment/jar/run.html[run] the standalone `ConsoleLauncher` as shown below. @@ -685,18 +704,21 @@ The `{ConsoleLauncher}` provides the following subcommands: include::{consoleLauncherOptionsFile}[] ---- +[[running-tests-console-launcher-options-discovering-tests]] ===== Discovering tests ---- include::{consoleLauncherDiscoverOptionsFile}[] ---- +[[running-tests-console-launcher-options-executing-tests]] ===== Executing tests ---- include::{consoleLauncherExecuteOptionsFile}[] ---- +[[running-tests-console-launcher-options-listing-test-engines]] ===== Listing test engines ---- @@ -868,6 +890,38 @@ WARNING: Test classes and suites annotated with `@RunWith(JUnitPlatform.class)` documented in some IDEs). Such classes and suites can only be executed using JUnit 4 infrastructure. +[[running-tests-discovery-selectors]] +=== Discovery Selectors + +The JUnit Platform provides a rich set of discovery selectors that can be used to specify +which tests should be discovered or executed. + +Discovery selectors can be created programmatically using the factory methods in the +`{DiscoverySelectors}` class, specified declaratively via annotations when using the +<>, via options of the <>, or +generically as strings via their identifiers. + +The following discovery selectors are provided out of the box: + +|=== +| Java Type | API | Annotation | Console Launcher | Identifier + +| `{ClasspathResourceSelector}` | `{DiscoverySelectors_selectClasspathResource}` | `{SelectClasspathResource}` | `--select-resource /foo.csv` | `resource:/foo.csv` +| `{ClasspathRootSelector}` | `{DiscoverySelectors_selectClasspathRoots}` | -- | `--scan-classpath bin` | `classpath-root:bin` +| `{ClassSelector}` | `{DiscoverySelectors_selectClass}` | `{SelectClasses}` | `--select-class com.acme.Foo` | `class:com.acme.Foo` +| `{DirectorySelector}` | `{DiscoverySelectors_selectDirectory}` | `{SelectDirectories}` | `--select-directory foo/bar` | `directory:foo/bar` +| `{FileSelector}` | `{DiscoverySelectors_selectFile}` | `{SelectFile}` | `--select-file dir/foo.txt` | `file:dir/foo.txt` +| `{IterationSelector}` | `{DiscoverySelectors_selectIteration}` | `{Select}("")` | `--select-iteration method=com.acme.Foo#m[1..2]` | `iteration:method:com.acme.Foo#m[1..2]` +| `{MethodSelector}` | `{DiscoverySelectors_selectMethod}` | `{SelectMethod}` | `--select-method com.acme.Foo#m` | `method:com.acme.Foo#m` +| `{ModuleSelector}` | `{DiscoverySelectors_selectModule}` | `{SelectModules}` | `--select-module com.acme` | `module:com.acme` +| `{NestedClassSelector}` | `{DiscoverySelectors_selectNestedClass}` | `{Select}("")` | `--select ` | `nested-class:com.acme.Foo/Bar` +| `{NestedMethodSelector}` | `{DiscoverySelectors_selectNestedMethod}` | `{Select}("")` | `--select ` | `nested-method:com.acme.Foo/Bar#m` +| `{PackageSelector}` | `{DiscoverySelectors_selectPackage}` | `{SelectPackages}` | `--select-package com.acme.foo` | `package:com.acme.foo` +| `{UniqueIdSelector}` | `{DiscoverySelectors_selectUniqueId}` | `{Select}("")` | `--select ` | `uid:...` +| `{UriSelector}` | `{DiscoverySelectors_selectUri}` | `{SelectUris}` | `--select-uri \file:///foo.txt` | `uri:file:///foo.txt` +|=== + + [[running-tests-config-params]] === Configuration Parameters @@ -885,25 +939,32 @@ parameters_ for the following use cases. _Configuration Parameters_ are text-based key-value pairs that can be supplied to test engines running on the JUnit Platform via one of the following mechanisms. -1. The `configurationParameter()` and `configurationParameters()` methods in the - `LauncherDiscoveryRequestBuilder` which is used to build a request supplied to the - <>. When running tests via one of the tools provided - by the JUnit Platform you can specify configuration parameters as follows: - * <>: use the `--config` - command-line option. - * <>: use the - `systemProperty` or `systemProperties` DSL. - * <>: use the - `configurationParameters` property. -2. JVM system properties. -3. The JUnit Platform configuration file: a file named `junit-platform.properties` in the - root of the class path that follows the syntax rules for a Java `Properties` file. +1. The `configurationParameter()` and `configurationParameters()` methods in + `LauncherDiscoveryRequestBuilder` which + is used to build a request supplied to the <>. + + + When running tests via one of the tools provided by the JUnit Platform you can specify + configuration parameters as follows: + * <>: use the `--config` command-line + option. + * <>: use the `systemProperty` or + `systemProperties` DSL. + * <>: use the + `configurationParameters` property. +2. The `configurationParametersResources()` method in `LauncherDiscoveryRequestBuilder`. + + + When running tests via the <> you can + specify custom configuration files using the `--config-resource` command-line option. +3. JVM system properties. +4. The JUnit Platform default configuration file: a file named `junit-platform.properties` + in the root of the class path that follows the syntax rules for Java `Properties` + files. NOTE: Configuration parameters are looked up in the exact order defined above. Consequently, configuration parameters supplied directly to the `Launcher` take -precedence over those supplied via system properties and the configuration file. -Similarly, configuration parameters supplied via system properties take precedence over -those supplied via the configuration file. +precedence over those supplied via custom configuration files, system properties, and the +default configuration file. Similarly, configuration parameters supplied via system +properties take precedence over those supplied via the default configuration file. [[running-tests-config-params-deactivation-pattern]] ==== Pattern Matching Syntax diff --git a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc index 0a0862b6f2b8..463faad468c8 100644 --- a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc +++ b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc @@ -1,3 +1,7 @@ +:testDir: ../../../../src/test/java +:testRelease21Dir: ../../../../src/test/java21 +:kotlinTestDir: ../../../../src/test/kotlin + [[writing-tests]] == Writing Tests @@ -24,27 +28,28 @@ in the `junit-jupiter-api` module. |=== | Annotation | Description -| `@Test` | Denotes that a method is a test method. Unlike JUnit 4's `@Test` annotation, this annotation does not declare any attributes, since test extensions in JUnit Jupiter operate based on their own dedicated annotations. Such methods are _inherited_ unless they are _overridden_. -| `@ParameterizedTest` | Denotes that a method is a <>. Such methods are _inherited_ unless they are _overridden_. -| `@RepeatedTest` | Denotes that a method is a test template for a <>. Such methods are _inherited_ unless they are _overridden_. -| `@TestFactory` | Denotes that a method is a test factory for <>. Such methods are _inherited_ unless they are _overridden_. -| `@TestTemplate` | Denotes that a method is a <> designed to be invoked multiple times depending on the number of invocation contexts returned by the registered <>. Such methods are _inherited_ unless they are _overridden_. -| `@TestClassOrder` | Used to configure the <> for `@Nested` test classes in the annotated test class. Such annotations are _inherited_. -| `@TestMethodOrder` | Used to configure the <> for the annotated test class; similar to JUnit 4's `@FixMethodOrder`. Such annotations are _inherited_. -| `@TestInstance` | Used to configure the <> for the annotated test class. Such annotations are _inherited_. -| `@DisplayName` | Declares a custom <> for the test class or test method. Such annotations are not _inherited_. -| `@DisplayNameGeneration` | Declares a custom <> for the test class. Such annotations are _inherited_. -| `@BeforeEach` | Denotes that the annotated method should be executed _before_ *each* `@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current class; analogous to JUnit 4's `@Before`. Such methods are _inherited_ – unless they are _overridden_ or _superseded_ (i.e., replaced based on signature only, irrespective of Java's visibility rules). -| `@AfterEach` | Denotes that the annotated method should be executed _after_ *each* `@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current class; analogous to JUnit 4's `@After`. Such methods are _inherited_ – unless they are _overridden_ or _superseded_ (i.e., replaced based on signature only, irrespective of Java's visibility rules). -| `@BeforeAll` | Denotes that the annotated method should be executed _before_ *all* `@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current class; analogous to JUnit 4's `@BeforeClass`. Such methods are _inherited_ – unless they are _hidden_, _overridden_, or _superseded_, (i.e., replaced based on signature only, irrespective of Java's visibility rules) – and must be `static` unless the "per-class" <> is used. -| `@AfterAll` | Denotes that the annotated method should be executed _after_ *all* `@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current class; analogous to JUnit 4's `@AfterClass`. Such methods are _inherited_ – unless they are _hidden_, _overridden_, or _superseded_, (i.e., replaced based on signature only, irrespective of Java's visibility rules) – and must be `static` unless the "per-class" <> is used. -| `@Nested` | Denotes that the annotated class is a non-static <>. On Java 8 through Java 15, `@BeforeAll` and `@AfterAll` methods cannot be used directly in a `@Nested` test class unless the "per-class" <> is used. Beginning with Java 16, `@BeforeAll` and `@AfterAll` methods can be declared as `static` in a `@Nested` test class with either test instance lifecycle mode. Such annotations are not _inherited_. -| `@Tag` | Used to declare <>, either at the class or method level; analogous to test groups in TestNG or Categories in JUnit 4. Such annotations are _inherited_ at the class level but not at the method level. -| `@Disabled` | Used to <> a test class or test method; analogous to JUnit 4's `@Ignore`. Such annotations are not _inherited_. -| `@Timeout` | Used to fail a test, test factory, test template, or lifecycle method if its execution exceeds a given duration. Such annotations are _inherited_. -| `@ExtendWith` | Used to <>. Such annotations are _inherited_. -| `@RegisterExtension` | Used to <> via fields. Such fields are _inherited_ unless they are _shadowed_. -| `@TempDir` | Used to supply a <> via field injection or parameter injection in a lifecycle method or test method; located in the `org.junit.jupiter.api.io` package. +| `@Test` | Denotes that a method is a test method. Unlike JUnit 4's `@Test` annotation, this annotation does not declare any attributes, since test extensions in JUnit Jupiter operate based on their own dedicated annotations. Such methods are inherited unless they are overridden. +| `@ParameterizedTest` | Denotes that a method is a <>. Such methods are inherited unless they are overridden. +| `@RepeatedTest` | Denotes that a method is a test template for a <>. Such methods are inherited unless they are overridden. +| `@TestFactory` | Denotes that a method is a test factory for <>. Such methods are inherited unless they are overridden. +| `@TestTemplate` | Denotes that a method is a <> designed to be invoked multiple times depending on the number of invocation contexts returned by the registered <>. Such methods are inherited unless they are overridden. +| `@TestClassOrder` | Used to configure the <> for `@Nested` test classes in the annotated test class. Such annotations are inherited. +| `@TestMethodOrder` | Used to configure the <> for the annotated test class; similar to JUnit 4's `@FixMethodOrder`. Such annotations are inherited. +| `@TestInstance` | Used to configure the <> for the annotated test class. Such annotations are inherited. +| `@DisplayName` | Declares a custom <> for the test class or test method. Such annotations are not inherited. +| `@DisplayNameGeneration` | Declares a custom <> for the test class. Such annotations are inherited. +| `@BeforeEach` | Denotes that the annotated method should be executed _before_ *each* `@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current class; analogous to JUnit 4's `@Before`. Such methods are inherited unless they are overridden. +| `@AfterEach` | Denotes that the annotated method should be executed _after_ *each* `@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current class; analogous to JUnit 4's `@After`. Such methods are inherited unless they are overridden. +| `@BeforeAll` | Denotes that the annotated method should be executed _before_ *all* `@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current class; analogous to JUnit 4's `@BeforeClass`. Such methods are inherited unless they are overridden and must be `static` unless the "per-class" <> is used. +| `@AfterAll` | Denotes that the annotated method should be executed _after_ *all* `@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current class; analogous to JUnit 4's `@AfterClass`. Such methods are inherited unless they are overridden and must be `static` unless the "per-class" <> is used. +| `@Nested` | Denotes that the annotated class is a non-static <>. On Java 8 through Java 15, `@BeforeAll` and `@AfterAll` methods cannot be used directly in a `@Nested` test class unless the "per-class" <> is used. Beginning with Java 16, `@BeforeAll` and `@AfterAll` methods can be declared as `static` in a `@Nested` test class with either test instance lifecycle mode. Such annotations are not inherited. +| `@Tag` | Used to declare <>, either at the class or method level; analogous to test groups in TestNG or Categories in JUnit 4. Such annotations are inherited at the class level but not at the method level. +| `@Disabled` | Used to <> a test class or test method; analogous to JUnit 4's `@Ignore`. Such annotations are not inherited. +| `@AutoClose` | Denotes that the annotated field represents a resource that will be <> after test execution. +| `@Timeout` | Used to fail a test, test factory, test template, or lifecycle method if its execution exceeds a given duration. Such annotations are inherited. +| `@TempDir` | Used to supply a <> via field injection or parameter injection in a lifecycle method or test method; located in the `org.junit.jupiter.api.io` package. Such fields are inherited. +| `@ExtendWith` | Used to <>. Such annotations are inherited. +| `@RegisterExtension` | Used to <> via fields. Such fields are inherited. |=== WARNING: Some annotations may currently be _experimental_. Consult the table in @@ -119,6 +124,7 @@ Test Class:: any top-level class, `static` member class, or <> that contains at least one _test method_, i.e. a _container_. Test classes must not be `abstract` and must have a single constructor. +Java `record` classes are supported as well. Test Method:: any instance method that is directly annotated or meta-annotated with @@ -149,6 +155,23 @@ making classes and methods `public` is to simplify testing on the module path wh the Java Module System. ==== +[NOTE] +.Field and method inheritance +==== +Fields in test classes are inherited. For example, a `@TempDir` field from a superclass +will always be applied in a subclass. + +Test methods and lifecycle methods are inherited unless they are overridden according to +the visibility rules of the Java language. For example, a `@Test` method from a superclass +will always be applied in a subclass unless the subclass explicitly overrides the method. +Similarly, if a package-private `@Test` method is declared in a superclass that resides in +a different package than the subclass, that `@Test` method will always be applied in the +subclass since the subclass cannot override a package-private method from a superclass in +a different package. + +See also: <> +==== + The following test class demonstrates the use of `@Test` methods and all supported lifecycle methods. For further information on runtime semantics, see <> and @@ -160,6 +183,15 @@ lifecycle methods. For further information on runtime semantics, see include::{testDir}/example/StandardTests.java[tags=user_guide] ---- +It is also possible to use Java `record` classes as test classes as illustrated by the +following example. + +[source,java,indent=0] +.A test class written as a Java record +---- +include::{testRelease21Dir}/example/MyFirstJUnitJupiterRecordTests.java[tags=user_guide] +---- + [[writing-tests-display-names]] === Display Names @@ -261,6 +293,13 @@ JUnit Jupiter comes with many of the assertion methods that JUnit 4 has and adds that lend themselves well to being used with Java 8 lambdas. All JUnit Jupiter assertions are `static` methods in the `{Assertions}` class. +Assertion methods optionally accept the assertion message as their third parameter, which +can be either a `String` or a `Supplier`. + +When using a `Supplier` (e.g., a lambda expression), the message is evaluated +lazily. This can provide a performance benefit, especially if message construction is +complex or time-consuming, as it is only evaluated when the assertion fails. + [source,java,indent=0] ---- include::{testDir}/example/AssertionsDemo.java[tags=user_guide] @@ -334,20 +373,151 @@ Naturally, legacy tests based on the JUnit 4 programming model can continue usin [[writing-tests-assumptions]] === Assumptions -JUnit Jupiter comes with a subset of the assumption methods that JUnit 4 provides and +Assumptions are typically used whenever it does not make sense to continue execution of a +given test — for example, if the test depends on something that does not exist in the +current runtime environment. + +* When an assumption is valid, the assumption method does not throw an exception, and + execution of the test continues as usual. +* When an assumption is invalid, the assumption method throws an exception of type + `org.opentest4j.TestAbortedException` to signal that the test should be aborted instead + of marked as a failure. + +JUnit Jupiter comes with a subset of the _assumption_ methods that JUnit 4 provides and adds a few that lend themselves well to being used with Java 8 lambda expressions and -method references. All JUnit Jupiter assumptions are static methods in the -`{Assumptions}` class. +method references. + +All JUnit Jupiter assumptions are static methods in the `{Assumptions}` class. [source,java,indent=0] ---- include::{testDir}/example/AssumptionsDemo.java[tags=user_guide] ---- -NOTE: As of JUnit Jupiter 5.4, it is also possible to use methods from JUnit 4's -`org.junit.Assume` class for assumptions. Specifically, JUnit Jupiter supports JUnit 4's -`AssumptionViolatedException` to signal that a test should be aborted instead of marked -as a failure. +NOTE: It is also possible to use methods from JUnit 4's `org.junit.Assume` class for +assumptions. Specifically, JUnit Jupiter supports JUnit 4's `AssumptionViolatedException` +to signal that a test should be aborted instead of marked as a failure. + +[[writing-tests-exceptions]] +=== Exception Handling + +JUnit Jupiter provides robust support for handling test exceptions. This includes the +built-in mechanisms for managing test failures due to exceptions, the role of exceptions +in implementing assertions and assumptions, and how to specifically assert non-throwing +conditions in code. + +[[writing-tests-exceptions-uncaught]] +==== Uncaught Exceptions + +In JUnit Jupiter, if an exception is thrown from a test method, a lifecycle method, or an +extension and not caught within that test method, lifecycle method, or extension, the +framework will mark the test or test class as failed. + +[TIP] +==== +Failed assumptions deviate from this general rule. + +In contrast to failed assertions, failed assumptions do not result in a test failure; +rather, a failed assumption results in a test being aborted. + +See <> for further details and examples. +==== + +In the following example, the `failsDueToUncaughtException()` method throws an +`ArithmeticException`. Since the exception is not caught within the test method, JUnit +Jupiter will mark the test as failed. + +[source,java,indent=0] +---- +include::{testDir}/example/exception/UncaughtExceptionHandlingDemo.java[tags=user_guide] +---- + +NOTE: It's important to note that specifying a `throws` clause in the test method has +no effect on the outcome of the test. JUnit Jupiter does not interpret a `throws` clause +as an expectation or assertion about what exceptions the test method should throw. A test +fails only if an exception is thrown unexpectedly or if an assertion fails. + +[[writing-tests-exceptions-failed-assertions]] +==== Failed Assertions + +Assertions in JUnit Jupiter are implemented using exceptions. The framework provides a set +of assertion methods in the `org.junit.jupiter.api.Assertions` class, which throw +`AssertionError` when an assertion fails. This mechanism is a core aspect of how JUnit +handles assertion failures as exceptions. See the <> section for +further information about JUnit Jupiter's assertion support. + +NOTE: Third-party assertion libraries may choose to throw an `AssertionError` to signal a +failed assertion; however, they may also choose to throw different types of exceptions to +signal failures. See also: <>. + +TIP: JUnit Jupiter itself does not differentiate between failed assertions +(`AssertionError`) and other types of exceptions. All uncaught exceptions lead to a test +failure. However, Integrated Development Environments (IDEs) and other tools may +distinguish between these two types of failures by checking whether the thrown exception +is an instance of `AssertionError`. + +In the following example, the `failsDueToUncaughtAssertionError()` method throws an +`AssertionError`. Since the exception is not caught within the test method, JUnit Jupiter +will mark the test as failed. + +[source,java,indent=0] +---- +include::{testDir}/example/exception/FailedAssertionDemo.java[tags=user_guide] +---- + +[[writing-tests-exceptions-expected]] +==== Asserting Expected Exceptions + +JUnit Jupiter offers specialized assertions for testing that specific exceptions are +thrown under expected conditions. The `assertThrows()` and `assertThrowsExactly()` +assertions are critical tools for validating that your code responds correctly to error +conditions by throwing the appropriate exceptions. + +[[writing-tests-exceptions-expected-assertThrows]] +===== Using `assertThrows()` + +The `assertThrows()` method is used to verify that a particular type of exception is +thrown during the execution of a provided executable block. It not only checks for the +type of the thrown exception but also its subclasses, making it suitable for more +generalized exception handling tests. The `assertThrows()` assertion method returns the +thrown exception object to allow performing additional assertions on it. + +[source,java,indent=0] +---- +include::{testDir}/example/exception/ExceptionAssertionDemo.java[tags=user_guide] +---- + +[[writing-tests-exceptions-expected-assertThrowsExactly]] +===== Using `assertThrowsExactly()` + +The `assertThrowsExactly()` method is used when you need to assert that the exception +thrown is exactly of a specific type, not allowing for subclasses of the expected +exception type. This is useful when precise exception handling behavior needs to be +validated. Similar to `assertThrows()`, the `assertThrowsExactly()` assertion method also +returns the thrown exception object to allow performing additional assertions on it. + +[source,java,indent=0] +---- +include::{testDir}/example/exception/ExceptionAssertionExactDemo.java[tags=user_guide] +---- + +[[writing-tests-exceptions-not-expected]] +==== Asserting That no Exception is Expected + +Although any exception thrown from a test method will cause the test to fail, there are +certain use cases where it can be beneficial to explicitly assert that an exception is +_not_ thrown for a given code block within a test method. The `assertDoesNotThrow()` +assertion can be used when you want to verify that a particular piece of code does not +throw any exceptions. + +[source,java,indent=0] +---- +include::{testDir}/example/exception/AssertDoesNotThrowExceptionDemo.java[tags=user_guide] +---- + +NOTE: Third-party assertion libraries often provide similar support. For example, AssertJ +has `assertThatNoException().isThrownBy(() -> ...)`. See also: +<>. [[writing-tests-disabling]] === Disabling Tests @@ -357,6 +527,15 @@ annotation, via one of the annotations discussed in <>, or via a custom <>. +When `@Disabled` is applied at the class level, all test methods within that class are +automatically disabled as well. + +If a test method is disabled via `@Disabled`, that prevents execution of the test method +and method-level lifecycle callbacks such as `@BeforeEach` methods, `@AfterEach` methods, +and corresponding extension APIs. However, that does not prevent the test class from being +instantiated, and it does not prevent the execution of class-level lifecycle callbacks +such as `@BeforeAll` methods, `@AfterAll` methods, and corresponding extension APIs. + Here's a `@Disabled` test class. [source,java,indent=0] @@ -371,28 +550,47 @@ And here's a test class that contains a `@Disabled` test method. include::{testDir}/example/DisabledTestsDemo.java[tags=user_guide] ---- -NOTE: `@Disabled` may be declared without providing a _reason_; however, the JUnit team +[TIP] +==== +`@Disabled` may be declared without providing a _reason_; however, the JUnit team recommends that developers provide a short explanation for why a test class or test method has been disabled. Consequently, the above examples both show the use of a reason -- for example, `@Disabled("Disabled until bug #42 has been resolved")`. Some development teams even require the presence of issue tracking numbers in the _reason_ for automated traceability, etc. +==== + +[NOTE] +==== +`@Disabled` is not `@Inherited`. Consequently, if you wish to disable a class whose +superclass is `@Disabled`, you must redeclare `@Disabled` on the subclass. +==== + [[writing-tests-conditional-execution]] === Conditional Test Execution The <> extension API in JUnit Jupiter allows -developers to either _enable_ or _disable_ a container or test based on certain +developers to either _enable_ or _disable_ a test class or test method based on certain conditions _programmatically_. The simplest example of such a condition is the built-in `{DisabledCondition}` which supports the `{Disabled}` annotation (see -<>). In addition to `@Disabled`, JUnit Jupiter also supports -several other annotation-based conditions in the `org.junit.jupiter.api.condition` -package that allow developers to enable or disable containers and tests _declaratively_. -When multiple `ExecutionCondition` extensions are registered, a container or test is -disabled as soon as one of the conditions returns _disabled_. If you wish to provide +<>). + +In addition to `@Disabled`, JUnit Jupiter also supports several other annotation-based +conditions in the `org.junit.jupiter.api.condition` package that allow developers to +enable or disable test classes and test methods _declaratively_. If you wish to provide details about why they might be disabled, every annotation associated with these built-in conditions has a `disabledReason` attribute available for that purpose. +When multiple `ExecutionCondition` extensions are registered, a test class or test method +is disabled as soon as one of the conditions returns _disabled_. If a test class is +disabled, all test methods within that class are automatically disabled as well. If a test +method is disabled, that prevents execution of the test method and method-level lifecycle +callbacks such as `@BeforeEach` methods, `@AfterEach` methods, and corresponding extension +APIs. However, that does not prevent the test class from being instantiated, and it does +not prevent the execution of class-level lifecycle callbacks such as `@BeforeAll` methods, +`@AfterAll` methods, and corresponding extension APIs. + See <> and the following sections for details. @@ -406,6 +604,13 @@ example, the `@TestOnMac` annotation in the combine `@Test` and `@EnabledOnOs` in a single, reusable annotation. ==== +[NOTE] +==== +_Conditional_ annotations in JUnit Jupiter are not `@Inherited`. Consequently, if you wish +to apply the same semantics to subclasses, each conditional annotation must be redeclared +on each subclass. +==== + [WARNING] ==== Unless otherwise stated, each of the _conditional_ annotations listed in the following @@ -1459,6 +1664,119 @@ can be referenced by its fully qualified method name, e.g. include::{testDir}/example/MethodSourceParameterResolutionDemo.java[tags=parameter_resolution_MethodSource_example] ---- +[[writing-tests-parameterized-tests-sources-FieldSource]] +===== @FieldSource + +`{FieldSource}` allows you to refer to one or more fields of the test class or external +classes. + +Fields within the test class must be `static` unless the test class is annotated with +`@TestInstance(Lifecycle.PER_CLASS)`; whereas, fields in external classes must always be +`static`. + +Each field must be able to supply a _stream_ of arguments, and each set of "arguments" +within the "stream" will be provided as the physical arguments for individual invocations +of the annotated `@ParameterizedTest` method. + +In this context, a "stream" is anything that JUnit can reliably convert to a `Stream`; +however, the actual concrete field type can take on many forms. Generally speaking this +translates to a `Collection`, an `Iterable`, a `Supplier` of a stream (`Stream`, +`DoubleStream`, `LongStream`, or `IntStream`), a `Supplier` of an `Iterator`, an array of +objects, or an array of primitives. Each set of "arguments" within the "stream" can be +supplied as an instance of `Arguments`, an array of objects (for example, `Object[]`, +`String[]`, etc.), or a single value if the parameterized test method accepts a single +argument. + +[WARNING] +==== +In contrast to the supported return types for +<> factory +methods, the value of a `@FieldSource` field cannot be an instance of `Stream`, +`DoubleStream`, `LongStream`, `IntStream`, or `Iterator`, since the values of such types +are _consumed_ the first time they are processed. However, if you wish to use one of +these types, you can wrap it in a `Supplier` — for example, `Supplier`. +==== + +Please note that a one-dimensional array of objects supplied as a set of "arguments" will +be handled differently than other types of arguments. Specifically, all of the elements +of a one-dimensional array of objects will be passed as individual physical arguments to +the `@ParameterizedTest` method. See the Javadoc for `{FieldSource}` for further details. + +If you do not explicitly provide a field name via `@FieldSource`, JUnit Jupiter will +search in the test class for a field that has the same name as the current +`@ParameterizedTest` method by convention. This is demonstrated in the following example. +This parameterized test method will be invoked twice: with the values `"apple"` and +`"banana"`. + +[source,java,indent=0] +---- +include::{testDir}/example/ParameterizedTestDemo.java[tags=default_field_FieldSource_example] +---- + +The following example demonstrates how to provide a single explicit field name via +`@FieldSource`. This parameterized test method will be invoked twice: with the values +`"apple"` and `"banana"`. + +[source,java,indent=0] +---- +include::{testDir}/example/ParameterizedTestDemo.java[tags=explicit_field_FieldSource_example] +---- + +The following example demonstrates how to provide multiple explicit field names via +`@FieldSource`. This example uses the `listOfFruits` field from the previous example as +well as the `additionalFruits` field. Consequently, this parameterized test method will +be invoked four times: with the values `"apple"`, `"banana"`, `"cherry"`, and +`"dewberry"`. + +[source,java,indent=0] +---- +include::{testDir}/example/ParameterizedTestDemo.java[tags=multiple_fields_FieldSource_example] +---- + +It is also possible to provide a `Stream`, `DoubleStream`, `IntStream`, `LongStream`, or +`Iterator` as the source of arguments via a `@FieldSource` field as long as the stream or +iterator is wrapped in a `java.util.function.Supplier`. The following example demonstrates +how to provide a `Supplier` of a `Stream` of named arguments. This parameterized test +method will be invoked twice: with the values `"apple"` and `"banana"` and with display +names `Apple` and `Banana`, respectively. + +[source,java,indent=0] +---- +include::{testDir}/example/ParameterizedTestDemo.java[tags=named_arguments_FieldSource_example] +---- + +[NOTE] +==== +Note that `arguments(Object...)` is a static factory method defined in the +`org.junit.jupiter.params.provider.Arguments` interface. + +Similarly, `named(String, Object)` is a static factory method defined in the +`org.junit.jupiter.api.Named` interface. +==== + +If a parameterized test method declares multiple parameters, the corresponding +`@FieldSource` field must be able to provide a collection, stream supplier, or array of +`Arguments` instances or object arrays as shown below (see the Javadoc for +`{FieldSource}` for further details on supported types). + +[source,java,indent=0] +---- +include::{testDir}/example/ParameterizedTestDemo.java[tags=multi_arg_FieldSource_example] +---- + +[NOTE] +==== +Note that `arguments(Object...)` is a static factory method defined in the +`org.junit.jupiter.params.provider.Arguments` interface. +==== + +An external, `static` `@FieldSource` field can be referenced by providing its +_fully qualified field name_ as demonstrated in the following example. + +[source,java,indent=0] +---- +include::{testDir}/example/ExternalFieldSourceDemo.java[tags=external_field_FieldSource_example] +---- [[writing-tests-parameterized-tests-sources-CsvSource]] ===== @CsvSource @@ -1666,6 +1984,34 @@ If you wish to implement a custom `ArgumentsProvider` that also consumes an anno (like built-in providers such as `{ValueArgumentsProvider}` or `{CsvArgumentsProvider}`), you have the possibility to extend the `{AnnotationBasedArgumentsProvider}` class. +[[writing-tests-parameterized-repeatable-sources]] +===== Multiple sources using repeatable annotations +Repeatable annotations provide a convenient way to specify multiple sources from +different providers. + +[source,java,indent=0] +---- +include::{testDir}/example/ParameterizedTestDemo.java[tags=repeatable_annotations] +---- + +Following the above parameterized test, a test case will run for each argument: + +---- +[1] foo +[2] bar +---- + +The following annotations are repeatable: + +* `@ValueSource` +* `@EnumSource` +* `@MethodSource` +* `@FieldSource` +* `@CsvSource` +* `@CsvFileSource` +* `@ArgumentsSource` + + [[writing-tests-parameterized-tests-argument-conversion]] ==== Argument Conversion @@ -1884,14 +2230,15 @@ include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_w ==== Customizing Display Names By default, the display name of a parameterized test invocation contains the invocation -index and the `String` representation of all arguments for that specific invocation. -Each of them is preceded by the parameter name (unless the argument is only available via -an `ArgumentsAccessor` or `ArgumentAggregator`), if present in the bytecode (for Java, -test code must be compiled with the `-parameters` compiler flag). +index and the `String` representation of all arguments for that specific invocation. Each +argument is preceded by its parameter name (unless the argument is only available via an +`ArgumentsAccessor` or `ArgumentAggregator`), if the parameter name is present in the +bytecode (for Java, test code must be compiled with the `-parameters` compiler flag). However, you can customize invocation display names via the `name` attribute of the `@ParameterizedTest` annotation like in the following example. +====== [source,java,indent=0] ---- include::{testDir}/example/ParameterizedTestDemo.java[tags=custom_display_names] @@ -1906,21 +2253,27 @@ Display name of container ✔ ├─ 2 ==> the rank of 'banana' is 2 ✔ └─ 3 ==> the rank of 'lemon, lime' is 3 ✔ .... +====== +[NOTE] +==== Please note that `name` is a `MessageFormat` pattern. Thus, a single quote (`'`) needs to be represented as a doubled single quote (`''`) in order to be displayed. +==== The following placeholders are supported within custom display names. [cols="20,80"] |=== -| Placeholder | Description - -| `{displayName}` | the display name of the method -| `{index}` | the current invocation index (1-based) -| `{arguments}` | the complete, comma-separated arguments list -| `{argumentsWithNames}` | the complete, comma-separated arguments list with parameter names -| `{0}`, `{1}`, ... | an individual argument +| Placeholder | Description + +| `{displayName}` | the display name of the method +| `{index}` | the current invocation index (1-based) +| `{arguments}` | the complete, comma-separated arguments list +| `{argumentsWithNames}` | the complete, comma-separated arguments list with parameter names +| `{argumentSetName}` | the name of the argument set +| `{argumentSetNameOrArgumentsWithNames}` | `{argumentSetName}` or `{argumentsWithNames}`, depending on how the arguments are supplied +| `{0}`, `{1}`, ... | an individual argument |=== NOTE: When including arguments in display names, their string representations are truncated @@ -1928,20 +2281,62 @@ if they exceed the configured maximum length. The limit is configurable via the `junit.jupiter.params.displayname.argument.maxlength` configuration parameter and defaults to 512 characters. -When using `@MethodSource` or `@ArgumentsSource`, you can provide custom names for -arguments using the `{Named}` API. A custom name will be used if the argument is included -in the invocation display name, like in the example below. +When using `@MethodSource`, `@FieldSource`, or `@ArgumentsSource`, you can provide custom +names for individual arguments or custom names for entire sets of arguments. + +Use the `{Named}` API to provide a custom name for an individual argument, and the custom +name will be used if the argument is included in the invocation display name, like in the +example below. +====== [source,java,indent=0] ---- include::{testDir}/example/ParameterizedTestDemo.java[tags=named_arguments] ---- +When executing the above method using the `ConsoleLauncher` you will see output similar to +the following. + .... A parameterized test with named arguments ✔ ├─ 1: An important file ✔ └─ 2: Another file ✔ .... +====== + +[NOTE] +==== +Note that `arguments(Object...)` is a static factory method defined in the +`org.junit.jupiter.params.provider.Arguments` interface. + +Similarly, `named(String, Object)` is a static factory method defined in the +`org.junit.jupiter.api.Named` interface. +==== + +Use the `ArgumentSet` API to provide a custom name for the entire set of arguments, and +the custom name will be used as the display name, like in the example below. + +====== +[source,java,indent=0] +---- +include::{testDir}/example/ParameterizedTestDemo.java[tags=named_argument_set] +---- + +When executing the above method using the `ConsoleLauncher` you will see output similar to +the following. + +.... +A parameterized test with named argument sets ✔ +├─ [1] Important files ✔ +└─ [2] Other files ✔ +.... +====== + +[NOTE] +==== +Note that `argumentSet(String, Object...)` is a static factory method defined in the +`org.junit.jupiter.params.provider.Arguments` interface. +==== If you'd like to set a default name pattern for all parameterized tests in your project, you can declare the `junit.jupiter.params.displayname.default` configuration parameter in @@ -2040,10 +2435,6 @@ lambda expression for a dynamic test, those fields will not be reset by callback or extensions between the execution of individual dynamic tests generated by the same `@TestFactory` method. -As of JUnit Jupiter {jupiter-version}, dynamic tests must always be created by factory -methods; however, this might be complemented by a registration facility in a later -release. - [[writing-tests-dynamic-tests-examples]] ==== Dynamic Test Examples @@ -2079,6 +2470,20 @@ a nested hierarchy of dynamic tests utilizing `DynamicContainer`. include::{testDir}/example/DynamicTestsDemo.java[tags=user_guide] ---- +[[writing-tests-dynamic-tests-named-support]] +==== Dynamic Tests and Named + +In some cases, it can be more natural to specify inputs together with a descriptive name +using the {Named} API and the corresponding `stream()` factory methods on `DynamicTest` as +shown in the first example below. The second example takes it one step further and allows +to provide the code block that should be executed by implementing the `Executable` +interface along with `Named` via the `NamedExecutable` base class. + +[source,java] +---- +include::{testRelease21Dir}/example/DynamicTestsNamedDemo.java[tags=user_guide] +---- + [[writing-tests-dynamic-tests-uri-test-source]] ==== URI Test Sources for Dynamic Tests @@ -2104,8 +2509,8 @@ implementations. `MethodSource` :: If the `URI` contains the `method` scheme and the fully qualified method name (FQMN) -- for example, `method:org.junit.Foo#bar(java.lang.String, java.lang.String[])`. Please - refer to the Javadoc for `DiscoverySelectors.selectMethod(String)` for the supported - formats for a FQMN. + refer to the Javadoc for `{DiscoverySelectors}.{DiscoverySelectors_selectMethod}` for the + supported formats for a FQMN. `ClassSource` :: If the `URI` contains the `class` scheme and the fully qualified class name -- @@ -2296,11 +2701,11 @@ junit.jupiter.execution.parallel.mode.default = concurrent The default execution mode is applied to all nodes of the test tree with a few notable exceptions, namely test classes that use the `Lifecycle.PER_CLASS` mode or a -`{MethodOrderer}` (except for `{MethodOrderer_Random}`). In the former case, test authors -have to ensure that the test class is thread-safe; in the latter, concurrent execution -might conflict with the configured execution order. Thus, in both cases, test methods in -such test classes are only executed concurrently if the `@Execution(CONCURRENT)` -annotation is present on the test class or method. +`{MethodOrderer}`. In the former case, test authors have to ensure that the test class is +thread-safe; in the latter, concurrent execution might conflict with the configured +execution order. Thus, in both cases, test methods in such test classes are only executed +concurrently if the `@Execution(CONCURRENT)` annotation is present on the test class or +method. When parallel execution is enabled and a default `{ClassOrderer}` is registered (see <> for details), top-level test classes will @@ -2345,34 +2750,41 @@ The following diagram illustrates how the execution of two top-level test classe `junit.jupiter.execution.parallel.mode.classes.default` (see labels in first column). //// -Source: https://mermaidjs.github.io/mermaid-live-editor/#/view/eyJjb2RlIjoiZ2FudHRcbiAgICBkYXRlRm9ybWF0ICBZWVlZLU1NLUREXG5cbiAgICBzZWN0aW9uIChzYW1lX3RocmVhZCwgc2FtZV90aHJlYWQpXG4gICAgQS50ZXN0MSgpIDphc3MxLCAyMDE5LTAxLTAxLCAxZFxuICAgIEEudGVzdDIoKSA6YXNzMiwgYWZ0ZXIgYXNzMSwgMWRcbiAgICBCLnRlc3QxKCkgOmJzczEsIGFmdGVyIGFzczIsIDFkXG4gICAgQi50ZXN0MigpIDpic3MyLCBhZnRlciBic3MxLCAxZFxuXG4gICAgc2VjdGlvbiAoc2FtZV90aHJlYWQsIGNvbmN1cnJlbnQpXG4gICAgQS50ZXN0MSgpIDphc2MxLCAyMDE5LTAxLTAxLCAxZFxuICAgIEEudGVzdDIoKSA6YXNjMiwgYWZ0ZXIgYXNjMSwgMWRcbiAgICBCLnRlc3QxKCkgOmJzYzEsIDIwMTktMDEtMDEsIDFkXG4gICAgQi50ZXN0MigpIDpic2MyLCBhZnRlciBic2MxLCAxZFxuXG4gICAgc2VjdGlvbiAoY29uY3VycmVudCwgc2FtZV90aHJlYWQpXG4gICAgQS50ZXN0MSgpIDphY3MxLCAyMDE5LTAxLTAxLCAxZFxuICAgIEEudGVzdDIoKSA6YWNzMiwgMjAxOS0wMS0wMSwgMWRcbiAgICBCLnRlc3QxKCkgOmJjczEsIGFmdGVyIGFjczIsIDFkXG4gICAgQi50ZXN0MigpIDpiY3MyLCBhZnRlciBhY3MyLCAxZFxuXG4gICAgc2VjdGlvbiAoY29uY3VycmVudCwgY29uY3VycmVudClcbiAgICBBLnRlc3QxKCkgOmFjYzEsIDIwMTktMDEtMDEsIDFkXG4gICAgQS50ZXN0MigpIDphY2MyLCAyMDE5LTAxLTAxLCAxZFxuICAgIEIudGVzdDEoKSA6YmNjMSwgMjAxOS0wMS0wMSwgMWRcbiAgICBCLnRlc3QyKCkgOmJjYzIsIDIwMTktMDEtMDEsIDFkXG4iLCJtZXJtYWlkIjp7InRoZW1lIjoibmV1dHJhbCIsImdhbnR0Ijp7ImxlZnRQYWRkaW5nIjoyMjUsImJhckdhcCI6NSwiZ3JpZExpbmVTdGFydFBhZGRpbmciOjEwLCJiYXJIZWlnaHQiOjMwLCJmb250U2l6ZSI6MTV9LCJ0aGVtZUNTUyI6Ii50YXNrVGV4dCwgLnNlY3Rpb25UaXRsZSB7IGZvbnQtZmFtaWx5OiAnT3BlbiBTYW5zJzsgZm9udC1zaXplOjE1cHggfSAuZ3JpZCAudGljayB0ZXh0IHsgZGlzcGxheTpub25lIH0gLmdyaWQgLnRpY2s6bnRoLWNoaWxkKDJuKzEpIHsgZGlzcGxheTpub25lIH0ifX0 +Source: 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 + +--- +displayMode: compact +--- gantt - dateFormat YYYY-MM-DD + dateFormat X + axisFormat %s + tickInterval 1 + title ↓ threads | time → section (same_thread, same_thread) - A.test1() :ass1, 2019-01-01, 1d - A.test2() :ass2, after ass1, 1d - B.test1() :bss1, after ass2, 1d - B.test2() :bss2, after bss1, 1d + A.test1() :ass1, 0, 1 + A.test2() :ass2, after ass1, 2 + B.test1() :bss1, after ass2, 3 + B.test2() :bss2, after bss1, 4 section (same_thread, concurrent) - A.test1() :asc1, 2019-01-01, 1d - A.test2() :asc2, after asc1, 1d - B.test1() :bsc1, 2019-01-01, 1d - B.test2() :bsc2, after bsc1, 1d + A.test1() :asc1, 0, 1 + A.test2() :asc2, after asc1, 2 + B.test1() :bsc1, 0, 1 + B.test2() :bsc2, after bsc1, 2 section (concurrent, same_thread) - A.test1() :acs1, 2019-01-01, 1d - A.test2() :acs2, 2019-01-01, 1d - B.test1() :bcs1, after acs2, 1d - B.test2() :bcs2, after acs2, 1d + A.test1() :acs1, 0, 1 + A.test2() :acs2, 0, 1 + B.test1() :bcs1, after acs1, 2 + B.test2() :bcs2, after acs2, 2 section (concurrent, concurrent) - A.test1() :acc1, 2019-01-01, 1d - A.test2() :acc2, 2019-01-01, 1d - B.test1() :bcc1, 2019-01-01, 1d - B.test2() :bcc2, 2019-01-01, 1d + A.test1() :acc1, 0, 1 + A.test2() :acc2, 0, 1 + B.test1() :bcc1, 0, 1 + B.test2() :bcc2, 0, 1 //// image::writing-tests_execution_mode.svg[caption='',title='Default execution mode configuration combinations'] @@ -2529,7 +2941,10 @@ System Property. When access to shared resources is declared using the `{ResourceLock}` annotation, the JUnit Jupiter engine uses this information to ensure that no conflicting tests are run in -parallel. +parallel. This guarantee extends to lifecycle methods of a test class or method. For +example, if a test method is annotated with a `{ResourceLock}` annotation, the "lock" will +be acquired before any `@BeforeEach` methods are executed and released after all +`@AfterEach` methods have been executed. [NOTE] .Running tests in isolation @@ -2555,12 +2970,12 @@ include::{testDir}/example/SharedResourcesDemo.java[tags=user_guide] === Built-in Extensions While the JUnit team encourages reusable extensions to be packaged and maintained in -separate libraries, the JUnit Jupiter API artifact includes a few user-facing extension -implementations that are considered so generally useful that users shouldn't have to add -another dependency. +separate libraries, JUnit Jupiter includes a few user-facing extension implementations +that are considered so generally useful that users shouldn't have to add another +dependency. [[writing-tests-built-in-extensions-TempDirectory]] -==== The TempDirectory Extension +==== The @TempDir Extension The built-in `{TempDirectory}` extension is used to create and clean up a temporary directory for an individual test or all tests in a test class. It is registered by @@ -2631,9 +3046,10 @@ they are instantiated, but they can assume that their `createTempDirectory(...)` `close()` methods will both be called once per instance, in this order, and from the same thread. -The default implementation available in Jupiter delegates the directory creation to -`java.nio.file.Files::createTempDirectory`, passing `junit` as the prefix string to be -used in generating the directory's name. +The default implementation available in Jupiter delegates directory creation to +`java.nio.file.Files::createTempDirectory` which uses the default file system and the +system's temporary directory as the parent directory. It passes `junit-` as the prefix +string of the generated directory name to help identify it as a created by JUnit. The following example defines a factory that uses the test name as the directory name prefix instead of the `junit` constant value. @@ -2692,3 +3108,52 @@ following precedence rules: 2. The default `TempDirFactory` configured via the configuration parameter, if present 3. Otherwise, `org.junit.jupiter.api.io.TempDirFactory$Standard` will be used. + +[[writing-tests-built-in-extensions-AutoClose]] +==== The @AutoClose Extension + +The built-in `{AutoCloseExtension}` automatically closes resources associated with fields. +It is registered by default. To use it, annotate a field in a test class with +`{AutoClose}`. + +`@AutoClose` fields may be either `static` or non-static. If the value of an `@AutoClose` +field is `null` when it is evaluated the field will be ignored, but a warning message will +be logged to inform you. + +By default, `@AutoClose` expects the value of the annotated field to implement a `close()` +method that will be invoked to close the resource. However, developers can customize the +name of the close method via the `value` attribute. For example, `@AutoClose("shutdown")` +instructs JUnit to look for a `shutdown()` method to close the resource. + +`@AutoClose` fields are inherited from superclasses. Furthermore, `@AutoClose` fields from +subclasses will be closed before `@AutoClose` fields in superclasses. + +When multiple `@AutoClose` fields exist within a given test class, the order in which the +resources are closed depends on an algorithm that is deterministic but intentionally +nonobvious. This ensures that subsequent runs of a test suite close resources in the same +order, thereby allowing for repeatable builds. + +The `AutoCloseExtension` implements the `AfterAllCallback` and +`TestInstancePreDestroyCallback` extension APIs. Consequently, a `static` `@AutoClose` +field will be closed after all tests in the current test class have completed, effectively +after `@AfterAll` methods have executed for the test class. A non-static `@AutoClose` +field will be closed before the current test class instance is destroyed. Specifically, if +the test class is configured with `@TestInstance(Lifecycle.PER_METHOD)` semantics, a +non-static `@AutoClose` field will be closed after the execution of each test method, test +factory method, or test template method. However, if the test class is configured with +`@TestInstance(Lifecycle.PER_CLASS)` semantics, a non-static `@AutoClose` field will not +be closed until the current test class instance is no longer needed, which means after +`@AfterAll` methods and after all `static` `@AutoClose` fields have been closed. + +The following example demonstrates how to annotate an instance field with `@AutoClose` so +that the resource is automatically closed after test execution. In this example, we assume +that the default `@TestInstance(Lifecycle.PER_METHOD)` semantics apply. + +[source,java,indent=0] +.A test class using `@AutoClose` to close a resource +---- +include::{testDir}/example/AutoCloseDemo.java[tags=user_guide_example] +---- +<1> Annotate an instance field with `@AutoClose`. +<2> `WebClient` implements `java.lang.AutoCloseable` which defines a `close()` method that + will be invoked after each `@Test` method. diff --git a/documentation/src/main/java/example/domain/Person.java b/documentation/src/main/java/example/domain/Person.java index b628febd36cf..00e376dabef8 100644 --- a/documentation/src/main/java/example/domain/Person.java +++ b/documentation/src/main/java/example/domain/Person.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/main/java/example/registration/WebClient.java b/documentation/src/main/java/example/registration/WebClient.java index b907c2c58e95..857259d85895 100644 --- a/documentation/src/main/java/example/registration/WebClient.java +++ b/documentation/src/main/java/example/registration/WebClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,10 +10,15 @@ package example.registration; -public class WebClient { +public class WebClient implements AutoCloseable { public WebResponse get(String string) { return new WebResponse(); } + @Override + public void close() { + /* no-op for demo */ + } + } diff --git a/documentation/src/main/java/example/registration/WebResponse.java b/documentation/src/main/java/example/registration/WebResponse.java index 598239f44c24..329cd7445d6b 100644 --- a/documentation/src/main/java/example/registration/WebResponse.java +++ b/documentation/src/main/java/example/registration/WebResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/main/java/example/registration/WebServerExtension.java b/documentation/src/main/java/example/registration/WebServerExtension.java index 80fefe787b89..0fded5e34735 100644 --- a/documentation/src/main/java/example/registration/WebServerExtension.java +++ b/documentation/src/main/java/example/registration/WebServerExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/main/java/example/util/Calculator.java b/documentation/src/main/java/example/util/Calculator.java index 98291f6a78fe..37cffa666ef6 100644 --- a/documentation/src/main/java/example/util/Calculator.java +++ b/documentation/src/main/java/example/util/Calculator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/main/java/example/util/ListWriter.java b/documentation/src/main/java/example/util/ListWriter.java index 88fb73137ff6..84af14834e79 100644 --- a/documentation/src/main/java/example/util/ListWriter.java +++ b/documentation/src/main/java/example/util/ListWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/main/java/example/util/StringUtils.java b/documentation/src/main/java/example/util/StringUtils.java index b622aa3efb90..dba77b1c48ae 100644 --- a/documentation/src/main/java/example/util/StringUtils.java +++ b/documentation/src/main/java/example/util/StringUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/plantuml/component-diagram.puml b/documentation/src/plantuml/component-diagram.puml new file mode 100644 index 000000000000..4874f5e1abb8 --- /dev/null +++ b/documentation/src/plantuml/component-diagram.puml @@ -0,0 +1,98 @@ +@startuml + +skinparam { + defaultFontName sans-serif +} + +package org.junit.jupiter { + [junit-jupiter] as jupiter + [junit-jupiter-api] as jupiter_api + [junit-jupiter-engine] as jupiter_engine + [junit-jupiter-params] as jupiter_params + [junit-jupiter-migrationsupport] as jupiter_migration_support +} + +package org.junit.vintage { + [junit-vintage-engine] as vintage_engine +} + +package org.junit.platform { + [junit-platform-commons] as commons + [junit-platform-console] as console + [junit-platform-engine] as engine + [junit-platform-jfr] as jfr + [junit-platform-launcher] as launcher + [junit-platform-reporting] as reporting + [junit-platform-runner] as runner + [junit-platform-suite] as suite + [junit-platform-suite-api] as suite_api + [junit-platform-suite-commons] as suite_commons + [junit-platform-suite-engine] as suite_engine + [junit-platform-testkit] as testkit +} + +package "JUnit 4" { + [junit:junit] as junit4 +} + +package org.opentest4j { + [opentest4j] +} + +package org.apiguardian { + [apiguardian-api] as apiguardian + note bottom of apiguardian #white + All artifacts except + opentest4j and junit:junit + have a dependency on this + artifact. The edges have + been omitted from this + diagram for the sake of + readability. + endnote +} + +jupiter ..> jupiter_api +jupiter ..> jupiter_params +jupiter ..> jupiter_engine + +jupiter_api ....> opentest4j +jupiter_api ...> commons + +jupiter_engine ...> engine +jupiter_engine ..> jupiter_api + +jupiter_params ..> jupiter_api +jupiter_migration_support ..> jupiter_api +jupiter_migration_support ...> junit4 + +console ..> launcher +console ..> reporting + +launcher ..> engine + +jfr ..> launcher + +engine ....> opentest4j +engine ..> commons + +reporting ..> launcher + +runner ..> suite_commons +runner ...> junit4 + +suite ..> suite_api +suite ..> suite_engine + +suite_engine ..> suite_commons + +suite_commons ..> launcher +suite_commons ..> suite_api + +testkit ....> opentest4j +testkit ..> launcher + +vintage_engine ...> engine +vintage_engine ..> junit4 + +@enduml diff --git a/documentation/src/test/java/example/AssertionsDemo.java b/documentation/src/test/java/example/AssertionsDemo.java index 476434da44f6..c1b1c47873d9 100644 --- a/documentation/src/test/java/example/AssertionsDemo.java +++ b/documentation/src/test/java/example/AssertionsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -41,8 +41,9 @@ void standardAssertions() { assertEquals(2, calculator.add(1, 1)); assertEquals(4, calculator.multiply(2, 2), "The optional failure message is now the last parameter"); - assertTrue('a' < 'b', () -> "Assertion messages can be lazily evaluated -- " - + "to avoid constructing complex messages unnecessarily."); + + // Lazily evaluates generateFailureMessage('a','b'). + assertTrue('a' < 'b', () -> generateFailureMessage('a','b')); } @Test @@ -85,6 +86,9 @@ void dependentAssertions() { ); } + // end::user_guide[] + @extensions.DisabledOnOpenJ9 + // tag::user_guide[] @Test void exceptionTesting() { Exception exception = assertThrows(ArithmeticException.class, () -> @@ -157,6 +161,10 @@ private static String greeting() { return "Hello, World!"; } + private static String generateFailureMessage(char a, char b) { + return "Assertion messages can be lazily evaluated -- " + + "to avoid constructing complex messages unnecessarily." + (a < b); + } } // end::user_guide[] // @formatter:on diff --git a/documentation/src/test/java/example/AssumptionsDemo.java b/documentation/src/test/java/example/AssumptionsDemo.java index 41438feabb2f..e9b106fc993e 100644 --- a/documentation/src/test/java/example/AssumptionsDemo.java +++ b/documentation/src/test/java/example/AssumptionsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/AutoCloseDemo.java b/documentation/src/test/java/example/AutoCloseDemo.java new file mode 100644 index 000000000000..054b1b1bcf0c --- /dev/null +++ b/documentation/src/test/java/example/AutoCloseDemo.java @@ -0,0 +1,38 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import example.registration.WebClient; + +import org.junit.jupiter.api.AutoClose; +import org.junit.jupiter.api.Test; + +// tag::user_guide_example[] +class AutoCloseDemo { + + @AutoClose // <1> + WebClient webClient = new WebClient(); // <2> + + String serverUrl = // specify server URL ... + // end::user_guide_example[] + "https://localhost"; + // tag::user_guide_example[] + + @Test + void getProductList() { + // Use WebClient to connect to web server and verify response + assertEquals(200, webClient.get(serverUrl + "/products").getResponseStatus()); + } + +} +// end::user_guide_example[] diff --git a/documentation/src/test/java/example/BeforeAndAfterSuiteDemo.java b/documentation/src/test/java/example/BeforeAndAfterSuiteDemo.java new file mode 100644 index 000000000000..c461417361bc --- /dev/null +++ b/documentation/src/test/java/example/BeforeAndAfterSuiteDemo.java @@ -0,0 +1,34 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example; + +import org.junit.platform.suite.api.AfterSuite; +import org.junit.platform.suite.api.BeforeSuite; +import org.junit.platform.suite.api.SelectPackages; +import org.junit.platform.suite.api.Suite; + +//tag::user_guide[] +@Suite +@SelectPackages("example") +class BeforeAndAfterSuiteDemo { + + @BeforeSuite + static void beforeSuite() { + // executes before the test suite + } + + @AfterSuite + static void afterSuite() { + // executes after the test suite + } + +} +//end::user_guide[] diff --git a/documentation/src/test/java/example/ConditionalTestExecutionDemo.java b/documentation/src/test/java/example/ConditionalTestExecutionDemo.java index 146443c260c1..c2011653287a 100644 --- a/documentation/src/test/java/example/ConditionalTestExecutionDemo.java +++ b/documentation/src/test/java/example/ConditionalTestExecutionDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/CustomLauncherInterceptor.java b/documentation/src/test/java/example/CustomLauncherInterceptor.java index 149cf7e45440..a18653087f20 100644 --- a/documentation/src/test/java/example/CustomLauncherInterceptor.java +++ b/documentation/src/test/java/example/CustomLauncherInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/CustomTestEngine.java b/documentation/src/test/java/example/CustomTestEngine.java index a1d0eb31bb47..7809d4c3ad3e 100644 --- a/documentation/src/test/java/example/CustomTestEngine.java +++ b/documentation/src/test/java/example/CustomTestEngine.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/DisabledClassDemo.java b/documentation/src/test/java/example/DisabledClassDemo.java index a2453a2aac27..545dd5b4e152 100644 --- a/documentation/src/test/java/example/DisabledClassDemo.java +++ b/documentation/src/test/java/example/DisabledClassDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/DisabledTestsDemo.java b/documentation/src/test/java/example/DisabledTestsDemo.java index e1a7f6c0ad68..7f8c87f565e2 100644 --- a/documentation/src/test/java/example/DisabledTestsDemo.java +++ b/documentation/src/test/java/example/DisabledTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/DisplayNameDemo.java b/documentation/src/test/java/example/DisplayNameDemo.java index c9ee6fed5ddc..41ba41622ac0 100644 --- a/documentation/src/test/java/example/DisplayNameDemo.java +++ b/documentation/src/test/java/example/DisplayNameDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/DisplayNameGeneratorDemo.java b/documentation/src/test/java/example/DisplayNameGeneratorDemo.java index 0ccfb762e24b..79f2ab985038 100644 --- a/documentation/src/test/java/example/DisplayNameGeneratorDemo.java +++ b/documentation/src/test/java/example/DisplayNameGeneratorDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/DocumentationTestSuite.java b/documentation/src/test/java/example/DocumentationTestSuite.java index b3bc6dd0029b..6bd7ef87b81f 100644 --- a/documentation/src/test/java/example/DocumentationTestSuite.java +++ b/documentation/src/test/java/example/DocumentationTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/DynamicTestsDemo.java b/documentation/src/test/java/example/DynamicTestsDemo.java index 8a8d9bef9ac0..0bb2a5149cdc 100644 --- a/documentation/src/test/java/example/DynamicTestsDemo.java +++ b/documentation/src/test/java/example/DynamicTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,7 +11,6 @@ package example; // tag::user_guide[] - import static example.util.StringUtils.isPalindrome; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -19,7 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; import static org.junit.jupiter.api.DynamicTest.dynamicTest; -import static org.junit.jupiter.api.Named.named; import java.util.Arrays; import java.util.Collection; @@ -34,7 +32,6 @@ import org.junit.jupiter.api.DynamicNode; import org.junit.jupiter.api.DynamicTest; -import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.function.ThrowingConsumer; @@ -97,11 +94,11 @@ Stream dynamicTestsFromStream() { Stream dynamicTestsFromIntStream() { // Generates tests for the first 10 even integers. return IntStream.iterate(0, n -> n + 2).limit(10) - .mapToObj(n -> dynamicTest("test" + n, () -> assertTrue(n % 2 == 0))); + .mapToObj(n -> dynamicTest("test" + n, () -> assertEquals(0, n % 2))); } @TestFactory - Stream generateRandomNumberOfTestsFromIterator() { + Stream generateRandomNumberOfTests() { // Generates random positive integers between 0 and 100 until // a number evenly divisible by 7 is encountered. @@ -153,21 +150,6 @@ Stream dynamicTestsFromStreamFactoryMethod() { return DynamicTest.stream(inputStream, displayNameGenerator, testExecutor); } - @TestFactory - Stream dynamicTestsFromStreamFactoryMethodWithNames() { - // Stream of palindromes to check - Stream> inputStream = Stream.of( - named("racecar is a palindrome", "racecar"), - named("radar is also a palindrome", "radar"), - named("mom also seems to be a palindrome", "mom"), - named("dad is yet another palindrome", "dad") - ); - - // Returns a stream of dynamic tests. - return DynamicTest.stream(inputStream, - text -> assertTrue(isPalindrome(text))); - } - @TestFactory Stream dynamicTestsWithContainers() { return Stream.of("A", "B", "C") @@ -192,6 +174,5 @@ DynamicNode dynamicNodeSingleContainer() { .map(text -> dynamicTest(text, () -> assertTrue(isPalindrome(text))) )); } - } // end::user_guide[] diff --git a/documentation/src/test/java/example/ExampleTestCase.java b/documentation/src/test/java/example/ExampleTestCase.java index 2a898b33e08b..fd9b7cf07ac8 100644 --- a/documentation/src/test/java/example/ExampleTestCase.java +++ b/documentation/src/test/java/example/ExampleTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/ExternalCustomConditionDemo.java b/documentation/src/test/java/example/ExternalCustomConditionDemo.java index 024b07d27854..1f83a26d3b30 100644 --- a/documentation/src/test/java/example/ExternalCustomConditionDemo.java +++ b/documentation/src/test/java/example/ExternalCustomConditionDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/ExternalFieldSourceDemo.java b/documentation/src/test/java/example/ExternalFieldSourceDemo.java new file mode 100644 index 000000000000..7918e5e5db02 --- /dev/null +++ b/documentation/src/test/java/example/ExternalFieldSourceDemo.java @@ -0,0 +1,35 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.FieldSource; + +class ExternalFieldSourceDemo { + + // tag::external_field_FieldSource_example[] + @ParameterizedTest + @FieldSource("example.FruitUtils#tropicalFruits") + void testWithExternalFieldSource(String tropicalFruit) { + // test with tropicalFruit + } + // end::external_field_FieldSource_example[] +} + +class FruitUtils { + + public static final List tropicalFruits = Collections.unmodifiableList(Arrays.asList("pineapple", "kiwi")); + +} diff --git a/documentation/src/test/java/example/ExternalMethodSourceDemo.java b/documentation/src/test/java/example/ExternalMethodSourceDemo.java index 1d8299367e9b..9cc1a15ff7bf 100644 --- a/documentation/src/test/java/example/ExternalMethodSourceDemo.java +++ b/documentation/src/test/java/example/ExternalMethodSourceDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/Fast.java b/documentation/src/test/java/example/Fast.java index 1e7c78e6f6b6..226d76949828 100644 --- a/documentation/src/test/java/example/Fast.java +++ b/documentation/src/test/java/example/Fast.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/FastTest.java b/documentation/src/test/java/example/FastTest.java index af31b49d5699..00b5b28459c1 100644 --- a/documentation/src/test/java/example/FastTest.java +++ b/documentation/src/test/java/example/FastTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/HamcrestAssertionsDemo.java b/documentation/src/test/java/example/HamcrestAssertionsDemo.java index 8fa219829ce3..4bfa2a05c9a1 100644 --- a/documentation/src/test/java/example/HamcrestAssertionsDemo.java +++ b/documentation/src/test/java/example/HamcrestAssertionsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/HttpServerDemo.java b/documentation/src/test/java/example/HttpServerDemo.java new file mode 100644 index 000000000000..d961793db5a0 --- /dev/null +++ b/documentation/src/test/java/example/HttpServerDemo.java @@ -0,0 +1,58 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; + +import com.sun.net.httpserver.HttpServer; + +import example.extensions.HttpServerExtension; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +// tag::user_guide[] +@ExtendWith(HttpServerExtension.class) +public class HttpServerDemo { + + // end::user_guide[] + @SuppressWarnings("HttpUrlsUsage") + // tag::user_guide[] + @Test + void httpCall(HttpServer server) throws Exception { + String hostName = server.getAddress().getHostName(); + int port = server.getAddress().getPort(); + String rawUrl = String.format("http://%s:%d/example", hostName, port); + URL requestUrl = URI.create(rawUrl).toURL(); + + String responseBody = sendRequest(requestUrl); + + assertEquals("This is a test", responseBody); + } + + private static String sendRequest(URL url) throws IOException { + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + int contentLength = connection.getContentLength(); + try (InputStream response = url.openStream()) { + byte[] content = new byte[contentLength]; + assertEquals(contentLength, response.read(content)); + return new String(content, UTF_8); + } + } +} +// end::user_guide[] diff --git a/documentation/src/test/java/example/IgnoredTestsDemo.java b/documentation/src/test/java/example/IgnoredTestsDemo.java index 96fca01c8ade..4e559ea1b9e5 100644 --- a/documentation/src/test/java/example/IgnoredTestsDemo.java +++ b/documentation/src/test/java/example/IgnoredTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/JUnit4Tests.java b/documentation/src/test/java/example/JUnit4Tests.java index d229e639fa2d..e9ed6ed9581a 100644 --- a/documentation/src/test/java/example/JUnit4Tests.java +++ b/documentation/src/test/java/example/JUnit4Tests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/JUnitPlatformClassDemo.java b/documentation/src/test/java/example/JUnitPlatformClassDemo.java index 0bf0aeb06220..82e945d6c04b 100644 --- a/documentation/src/test/java/example/JUnitPlatformClassDemo.java +++ b/documentation/src/test/java/example/JUnitPlatformClassDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java b/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java index b563204fff2b..4e09c0fba9a5 100644 --- a/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java +++ b/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/MethodSourceParameterResolutionDemo.java b/documentation/src/test/java/example/MethodSourceParameterResolutionDemo.java index 45b35dbcc964..98b555230b75 100644 --- a/documentation/src/test/java/example/MethodSourceParameterResolutionDemo.java +++ b/documentation/src/test/java/example/MethodSourceParameterResolutionDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java b/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java index 77632abc3be5..6566d55acc83 100644 --- a/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java +++ b/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/OrderedNestedTestClassesDemo.java b/documentation/src/test/java/example/OrderedNestedTestClassesDemo.java index b5e987daca65..b7a98414ae49 100644 --- a/documentation/src/test/java/example/OrderedNestedTestClassesDemo.java +++ b/documentation/src/test/java/example/OrderedNestedTestClassesDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/OrderedTestsDemo.java b/documentation/src/test/java/example/OrderedTestsDemo.java index 2ee01d3335cf..db90b9c6da80 100644 --- a/documentation/src/test/java/example/OrderedTestsDemo.java +++ b/documentation/src/test/java/example/OrderedTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/ParameterizedTestDemo.java b/documentation/src/test/java/example/ParameterizedTestDemo.java index 3afd887265e2..4d99f22c9311 100644 --- a/documentation/src/test/java/example/ParameterizedTestDemo.java +++ b/documentation/src/test/java/example/ParameterizedTestDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -16,6 +16,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Named.named; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; +import static org.junit.jupiter.params.provider.Arguments.argumentSet; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE; import static org.junit.jupiter.params.provider.EnumSource.Mode.MATCH_ALL; @@ -29,8 +31,10 @@ import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalUnit; import java.util.Arrays; +import java.util.Collection; import java.util.EnumSet; import java.util.List; +import java.util.function.Supplier; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -46,6 +50,7 @@ import org.junit.jupiter.api.TestReporter; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.aggregator.AggregateWith; import org.junit.jupiter.params.aggregator.ArgumentsAccessor; @@ -61,13 +66,20 @@ import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.FieldSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; +@Execution(SAME_THREAD) class ParameterizedTestDemo { + @BeforeEach + void printDisplayName(TestInfo testInfo) { + System.out.println(testInfo.getDisplayName()); + } + // tag::first_example[] @ParameterizedTest @ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" }) @@ -206,6 +218,78 @@ static Stream stringIntAndListProvider() { // end::multi_arg_MethodSource_example[] // @formatter:on + // @formatter:off + // tag::default_field_FieldSource_example[] + @ParameterizedTest + @FieldSource + void arrayOfFruits(String fruit) { + assertFruit(fruit); + } + + static final String[] arrayOfFruits = { "apple", "banana" }; + // end::default_field_FieldSource_example[] + // @formatter:on + + // @formatter:off + // tag::explicit_field_FieldSource_example[] + @ParameterizedTest + @FieldSource("listOfFruits") + void singleFieldSource(String fruit) { + assertFruit(fruit); + } + + static final List listOfFruits = Arrays.asList("apple", "banana"); + // end::explicit_field_FieldSource_example[] + // @formatter:on + + // @formatter:off + // tag::multiple_fields_FieldSource_example[] + @ParameterizedTest + @FieldSource({ "listOfFruits", "additionalFruits" }) + void multipleFieldSources(String fruit) { + assertFruit(fruit); + } + + static final Collection additionalFruits = Arrays.asList("cherry", "dewberry"); + // end::multiple_fields_FieldSource_example[] + // @formatter:on + + // @formatter:off + // tag::named_arguments_FieldSource_example[] + @ParameterizedTest + @FieldSource + void namedArgumentsSupplier(String fruit) { + assertFruit(fruit); + } + + static final Supplier> namedArgumentsSupplier = () -> Stream.of( + arguments(named("Apple", "apple")), + arguments(named("Banana", "banana")) + ); + // end::named_arguments_FieldSource_example[] + // @formatter:on + + private static void assertFruit(String fruit) { + assertTrue(Arrays.asList("apple", "banana", "cherry", "dewberry").contains(fruit)); + } + + // @formatter:off + // tag::multi_arg_FieldSource_example[] + @ParameterizedTest + @FieldSource("stringIntAndListArguments") + void testWithMultiArgFieldSource(String str, int num, List list) { + assertEquals(5, str.length()); + assertTrue(num >=1 && num <=2); + assertEquals(2, list.size()); + } + + static List stringIntAndListArguments = Arrays.asList( + arguments("apple", 1, Arrays.asList("a", "b")), + arguments("lemon", 2, Arrays.asList("x", "y")) + ); + // end::multi_arg_FieldSource_example[] + // @formatter:on + // @formatter:off // tag::CsvSource_example[] @ParameterizedTest @@ -467,4 +551,37 @@ static Stream namedArguments() { } // end::named_arguments[] // @formatter:on + + // @formatter:off + // tag::named_argument_set[] + @DisplayName("A parameterized test with named argument sets") + @ParameterizedTest + @FieldSource("argumentSets") + void testWithArgumentSets(File file1, File file2) { + } + + static List argumentSets = Arrays.asList( + argumentSet("Important files", new File("path1"), new File("path2")), + argumentSet("Other files", new File("path3"), new File("path4")) + ); + // end::named_argument_set[] + // @formatter:on + + // tag::repeatable_annotations[] + @DisplayName("A parameterized test that makes use of repeatable annotations") + @ParameterizedTest + @MethodSource("someProvider") + @MethodSource("otherProvider") + void testWithRepeatedAnnotation(String argument) { + assertNotNull(argument); + } + + static Stream someProvider() { + return Stream.of("foo"); + } + + static Stream otherProvider() { + return Stream.of("bar"); + } + // end::repeatable_annotations[] } diff --git a/documentation/src/test/java/example/PollingTimeoutDemo.java b/documentation/src/test/java/example/PollingTimeoutDemo.java index a10b7cec8edd..3f0b825369c9 100644 --- a/documentation/src/test/java/example/PollingTimeoutDemo.java +++ b/documentation/src/test/java/example/PollingTimeoutDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/RepeatedTestsDemo.java b/documentation/src/test/java/example/RepeatedTestsDemo.java index 49bf59274aa7..f11f7a5521b0 100644 --- a/documentation/src/test/java/example/RepeatedTestsDemo.java +++ b/documentation/src/test/java/example/RepeatedTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -23,7 +23,7 @@ import org.junit.jupiter.api.TestInfo; // end::user_guide[] -// Use fully-qualified names to avoid having them show up in the imports. +// Use fully qualified names to avoid having them show up in the imports. @org.junit.jupiter.api.parallel.Execution(org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD) // tag::user_guide[] class RepeatedTestsDemo { @@ -53,7 +53,7 @@ void repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) { } // end::user_guide[] - // Use fully-qualified name to avoid having it show up in the imports. + // Use fully qualified name to avoid having it show up in the imports. @org.junit.jupiter.api.Disabled("intentional failures would break the build") // tag::user_guide[] @RepeatedTest(value = 8, failureThreshold = 2) diff --git a/documentation/src/test/java/example/SharedResourcesDemo.java b/documentation/src/test/java/example/SharedResourcesDemo.java index be3d12b3ce99..d52248892356 100644 --- a/documentation/src/test/java/example/SharedResourcesDemo.java +++ b/documentation/src/test/java/example/SharedResourcesDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/SlowTests.java b/documentation/src/test/java/example/SlowTests.java index 6fef77843145..0c3b6a22c4cf 100644 --- a/documentation/src/test/java/example/SlowTests.java +++ b/documentation/src/test/java/example/SlowTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/StandardTests.java b/documentation/src/test/java/example/StandardTests.java index 4a6a660ea932..98e6a9cf35cf 100644 --- a/documentation/src/test/java/example/StandardTests.java +++ b/documentation/src/test/java/example/StandardTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/SuiteDemo.java b/documentation/src/test/java/example/SuiteDemo.java index 617806b3d035..af7a1d386047 100644 --- a/documentation/src/test/java/example/SuiteDemo.java +++ b/documentation/src/test/java/example/SuiteDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/TaggingDemo.java b/documentation/src/test/java/example/TaggingDemo.java index ebe9594b1c2d..5517bc2910e6 100644 --- a/documentation/src/test/java/example/TaggingDemo.java +++ b/documentation/src/test/java/example/TaggingDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/TempDirectoryDemo.java b/documentation/src/test/java/example/TempDirectoryDemo.java index 027242993e58..758710000fb3 100644 --- a/documentation/src/test/java/example/TempDirectoryDemo.java +++ b/documentation/src/test/java/example/TempDirectoryDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -137,7 +137,7 @@ static class JimfsTempDirFactory implements TempDirFactory { @Override public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) throws IOException { - return Files.createTempDirectory(fileSystem.getPath("/"), "junit"); + return Files.createTempDirectory(fileSystem.getPath("/"), "junit-"); } @Override diff --git a/documentation/src/test/java/example/TestInfoDemo.java b/documentation/src/test/java/example/TestInfoDemo.java index ac8f044edf17..eae99ae63afc 100644 --- a/documentation/src/test/java/example/TestInfoDemo.java +++ b/documentation/src/test/java/example/TestInfoDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/TestReporterDemo.java b/documentation/src/test/java/example/TestReporterDemo.java index dbd78d94aa94..7d9a3f35c206 100644 --- a/documentation/src/test/java/example/TestReporterDemo.java +++ b/documentation/src/test/java/example/TestReporterDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/TestTemplateDemo.java b/documentation/src/test/java/example/TestTemplateDemo.java index 5f0c56a24a37..35c3a9a8d6cf 100644 --- a/documentation/src/test/java/example/TestTemplateDemo.java +++ b/documentation/src/test/java/example/TestTemplateDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/TestingAStackDemo.java b/documentation/src/test/java/example/TestingAStackDemo.java index ab7b8339c989..d4f4a381eac6 100644 --- a/documentation/src/test/java/example/TestingAStackDemo.java +++ b/documentation/src/test/java/example/TestingAStackDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/TimeoutDemo.java b/documentation/src/test/java/example/TimeoutDemo.java index 82e89ce48479..7be021c0982a 100644 --- a/documentation/src/test/java/example/TimeoutDemo.java +++ b/documentation/src/test/java/example/TimeoutDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/UsingTheLauncherDemo.java b/documentation/src/test/java/example/UsingTheLauncherDemo.java index 403e9084c610..3d0b06bb7995 100644 --- a/documentation/src/test/java/example/UsingTheLauncherDemo.java +++ b/documentation/src/test/java/example/UsingTheLauncherDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -55,6 +55,9 @@ void discovery() { .filters( includeClassNamePatterns(".*Tests") ) + // end::discovery[] + .configurationParameter("enableHttpServer", "false") + // tag::discovery[] .build(); try (LauncherSession session = LauncherFactory.openSession()) { @@ -80,6 +83,9 @@ void execution() { .filters( includeClassNamePatterns(".*Tests") ) + // end::execution[] + .configurationParameter("enableHttpServer", "false") + // tag::execution[] .build(); SummaryGeneratingListener listener = new SummaryGeneratingListener(); diff --git a/documentation/src/test/java/example/callbacks/AbstractDatabaseTests.java b/documentation/src/test/java/example/callbacks/AbstractDatabaseTests.java index a6eef4489d07..cae7cc799975 100644 --- a/documentation/src/test/java/example/callbacks/AbstractDatabaseTests.java +++ b/documentation/src/test/java/example/callbacks/AbstractDatabaseTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/callbacks/BrokenLifecycleMethodConfigDemo.java b/documentation/src/test/java/example/callbacks/BrokenLifecycleMethodConfigDemo.java index 03da5ff8f086..ad4a13076536 100644 --- a/documentation/src/test/java/example/callbacks/BrokenLifecycleMethodConfigDemo.java +++ b/documentation/src/test/java/example/callbacks/BrokenLifecycleMethodConfigDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java b/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java index 792c778810c3..6cf730d1d2e1 100644 --- a/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java +++ b/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/callbacks/Extension1.java b/documentation/src/test/java/example/callbacks/Extension1.java index f0f5e697ba8a..e258e6cde952 100644 --- a/documentation/src/test/java/example/callbacks/Extension1.java +++ b/documentation/src/test/java/example/callbacks/Extension1.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/callbacks/Extension2.java b/documentation/src/test/java/example/callbacks/Extension2.java index a7cc878f1f76..132234139389 100644 --- a/documentation/src/test/java/example/callbacks/Extension2.java +++ b/documentation/src/test/java/example/callbacks/Extension2.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/callbacks/Logger.java b/documentation/src/test/java/example/callbacks/Logger.java index ab2271256b3f..a77224d68ea0 100644 --- a/documentation/src/test/java/example/callbacks/Logger.java +++ b/documentation/src/test/java/example/callbacks/Logger.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/defaultmethods/ComparableContract.java b/documentation/src/test/java/example/defaultmethods/ComparableContract.java index f72d80f8894c..7e0a0435b1fc 100644 --- a/documentation/src/test/java/example/defaultmethods/ComparableContract.java +++ b/documentation/src/test/java/example/defaultmethods/ComparableContract.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/defaultmethods/EqualsContract.java b/documentation/src/test/java/example/defaultmethods/EqualsContract.java index 36e30258ca92..c6c64ec8a9a6 100644 --- a/documentation/src/test/java/example/defaultmethods/EqualsContract.java +++ b/documentation/src/test/java/example/defaultmethods/EqualsContract.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,7 +11,6 @@ package example.defaultmethods; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import org.junit.jupiter.api.Test; @@ -30,7 +29,7 @@ default void valueEqualsItself() { @Test default void valueDoesNotEqualNull() { T value = createValue(); - assertFalse(value.equals(null)); + assertNotEquals(null, value); } @Test diff --git a/documentation/src/test/java/example/defaultmethods/StringTests.java b/documentation/src/test/java/example/defaultmethods/StringTests.java index 1449f28f8dbd..128683a33ec0 100644 --- a/documentation/src/test/java/example/defaultmethods/StringTests.java +++ b/documentation/src/test/java/example/defaultmethods/StringTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/defaultmethods/Testable.java b/documentation/src/test/java/example/defaultmethods/Testable.java index 84bf397b87eb..cf0a6a308fc5 100644 --- a/documentation/src/test/java/example/defaultmethods/Testable.java +++ b/documentation/src/test/java/example/defaultmethods/Testable.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/exception/AssertDoesNotThrowExceptionDemo.java b/documentation/src/test/java/example/exception/AssertDoesNotThrowExceptionDemo.java new file mode 100644 index 000000000000..0c14624cc62f --- /dev/null +++ b/documentation/src/test/java/example/exception/AssertDoesNotThrowExceptionDemo.java @@ -0,0 +1,31 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.exception; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.Test; + +class AssertDoesNotThrowExceptionDemo { + + // tag::user_guide[] + @Test + void testExceptionIsNotThrown() { + assertDoesNotThrow(() -> { + shouldNotThrowException(); + }); + } + + void shouldNotThrowException() { + } + // end::user_guide[] + +} diff --git a/documentation/src/test/java/example/exception/ExceptionAssertionDemo.java b/documentation/src/test/java/example/exception/ExceptionAssertionDemo.java new file mode 100644 index 000000000000..c99776ea53a0 --- /dev/null +++ b/documentation/src/test/java/example/exception/ExceptionAssertionDemo.java @@ -0,0 +1,43 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.exception; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class ExceptionAssertionDemo { + + // @formatter:off + // tag::user_guide[] + @Test + void testExpectedExceptionIsThrown() { + // The following assertion succeeds because the code under assertion + // throws the expected IllegalArgumentException. + // The assertion also returns the thrown exception which can be used for + // further assertions like asserting the exception message. + IllegalArgumentException exception = + assertThrows(IllegalArgumentException.class, () -> { + throw new IllegalArgumentException("expected message"); + }); + assertEquals("expected message", exception.getMessage()); + + // The following assertion also succeeds because the code under assertion + // throws IllegalArgumentException which is a subclass of RuntimeException. + assertThrows(RuntimeException.class, () -> { + throw new IllegalArgumentException("expected message"); + }); + } + // end::user_guide[] + // @formatter:on + +} diff --git a/documentation/src/test/java/example/exception/ExceptionAssertionExactDemo.java b/documentation/src/test/java/example/exception/ExceptionAssertionExactDemo.java new file mode 100644 index 000000000000..8ef8dfe7c404 --- /dev/null +++ b/documentation/src/test/java/example/exception/ExceptionAssertionExactDemo.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.exception; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +import extensions.ExpectToFail; + +import org.junit.jupiter.api.Test; + +public class ExceptionAssertionExactDemo { + + @ExpectToFail + // @formatter:off + // tag::user_guide[] + @Test + void testExpectedExceptionIsThrown() { + // The following assertion succeeds because the code under assertion throws + // IllegalArgumentException which is exactly equal to the expected type. + // The assertion also returns the thrown exception which can be used for + // further assertions like asserting the exception message. + IllegalArgumentException exception = + assertThrowsExactly(IllegalArgumentException.class, () -> { + throw new IllegalArgumentException("expected message"); + }); + assertEquals("expected message", exception.getMessage()); + + // The following assertion fails because the assertion expects exactly + // RuntimeException to be thrown, not subclasses of RuntimeException. + assertThrowsExactly(RuntimeException.class, () -> { + throw new IllegalArgumentException("expected message"); + }); + } + // end::user_guide[] + // @formatter:on + +} diff --git a/documentation/src/test/java/example/exception/FailedAssertionDemo.java b/documentation/src/test/java/example/exception/FailedAssertionDemo.java new file mode 100644 index 000000000000..f12bba305e19 --- /dev/null +++ b/documentation/src/test/java/example/exception/FailedAssertionDemo.java @@ -0,0 +1,38 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.exception; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import example.util.Calculator; + +import extensions.ExpectToFail; + +import org.junit.jupiter.api.Test; + +class FailedAssertionDemo { + + // tag::user_guide[] + private final Calculator calculator = new Calculator(); + + // end::user_guide[] + + @ExpectToFail + // tag::user_guide[] + @Test + void failsDueToUncaughtAssertionError() { + // The following incorrect assertion will cause a test failure. + // The expected value should be 2 instead of 99. + assertEquals(99, calculator.add(1, 1)); + } + // end::user_guide[] + +} diff --git a/documentation/src/test/java/example/exception/IgnoreIOExceptionExtension.java b/documentation/src/test/java/example/exception/IgnoreIOExceptionExtension.java index f2010d1b8efb..3546702086e6 100644 --- a/documentation/src/test/java/example/exception/IgnoreIOExceptionExtension.java +++ b/documentation/src/test/java/example/exception/IgnoreIOExceptionExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/exception/IgnoreIOExceptionTests.java b/documentation/src/test/java/example/exception/IgnoreIOExceptionTests.java index ca706586e858..55ae4d88b9b5 100644 --- a/documentation/src/test/java/example/exception/IgnoreIOExceptionTests.java +++ b/documentation/src/test/java/example/exception/IgnoreIOExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/exception/MultipleHandlersTestCase.java b/documentation/src/test/java/example/exception/MultipleHandlersTestCase.java index 3db997fe97a0..81b7ed2b12db 100644 --- a/documentation/src/test/java/example/exception/MultipleHandlersTestCase.java +++ b/documentation/src/test/java/example/exception/MultipleHandlersTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/exception/RecordStateOnErrorExtension.java b/documentation/src/test/java/example/exception/RecordStateOnErrorExtension.java index ed4c76979b32..8e13f000a491 100644 --- a/documentation/src/test/java/example/exception/RecordStateOnErrorExtension.java +++ b/documentation/src/test/java/example/exception/RecordStateOnErrorExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/exception/UncaughtExceptionHandlingDemo.java b/documentation/src/test/java/example/exception/UncaughtExceptionHandlingDemo.java new file mode 100644 index 000000000000..ccc23d012c4d --- /dev/null +++ b/documentation/src/test/java/example/exception/UncaughtExceptionHandlingDemo.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.exception; + +import example.util.Calculator; + +import extensions.ExpectToFail; + +import org.junit.jupiter.api.Test; + +class UncaughtExceptionHandlingDemo { + + // tag::user_guide[] + private final Calculator calculator = new Calculator(); + + // end::user_guide[] + + @ExpectToFail + // tag::user_guide[] + @Test + void failsDueToUncaughtException() { + // The following throws an ArithmeticException due to division by + // zero, which causes a test failure. + calculator.divide(1, 0); + } + // end::user_guide[] + +} diff --git a/documentation/src/test/java/example/extensions/HttpServerExtension.java b/documentation/src/test/java/example/extensions/HttpServerExtension.java new file mode 100644 index 000000000000..29318152228f --- /dev/null +++ b/documentation/src/test/java/example/extensions/HttpServerExtension.java @@ -0,0 +1,50 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.extensions; + +import java.io.IOException; +import java.io.UncheckedIOException; + +import com.sun.net.httpserver.HttpServer; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; + +// tag::user_guide[] +public class HttpServerExtension implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return HttpServer.class.equals(parameterContext.getParameter().getType()); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + + ExtensionContext rootContext = extensionContext.getRoot(); + ExtensionContext.Store store = rootContext.getStore(Namespace.GLOBAL); + String key = HttpServerResource.class.getName(); + HttpServerResource resource = store.getOrComputeIfAbsent(key, __ -> { + try { + HttpServerResource serverResource = new HttpServerResource(0); + serverResource.start(); + return serverResource; + } + catch (IOException e) { + throw new UncheckedIOException("Failed to create HttpServerResource", e); + } + }, HttpServerResource.class); + return resource.getHttpServer(); + } +} +// end::user_guide[] diff --git a/documentation/src/test/java/example/extensions/HttpServerResource.java b/documentation/src/test/java/example/extensions/HttpServerResource.java new file mode 100644 index 000000000000..2f4e2e52afc3 --- /dev/null +++ b/documentation/src/test/java/example/extensions/HttpServerResource.java @@ -0,0 +1,74 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.extensions; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; + +import com.sun.net.httpserver.HttpServer; + +import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; + +/** + * Demonstrates an implementation of {@link CloseableResource} using an {@link HttpServer}. + */ +// tag::user_guide[] +class HttpServerResource implements CloseableResource { + + private final HttpServer httpServer; + + // end::user_guide[] + + /** + * Initializes the Http server resource, using the given port. + * + * @param port (int) The port number for the server, must be in the range 0-65535. + * @throws IOException if an IOException occurs during initialization. + */ + // tag::user_guide[] + HttpServerResource(int port) throws IOException { + InetAddress loopbackAddress = InetAddress.getLoopbackAddress(); + this.httpServer = HttpServer.create(new InetSocketAddress(loopbackAddress, port), 0); + } + + HttpServer getHttpServer() { + return httpServer; + } + + // end::user_guide[] + + /** + * Starts the Http server with an example handler. + */ + // tag::user_guide[] + void start() { + // Example handler + httpServer.createContext("/example", exchange -> { + String body = "This is a test"; + exchange.sendResponseHeaders(200, body.length()); + try (OutputStream os = exchange.getResponseBody()) { + os.write(body.getBytes(UTF_8)); + } + }); + httpServer.setExecutor(null); + httpServer.start(); + } + + @Override + public void close() { + httpServer.stop(0); + } +} +// end::user_guide[] diff --git a/documentation/src/test/java/example/extensions/ParameterResolverConflictDemo.java b/documentation/src/test/java/example/extensions/ParameterResolverConflictDemo.java new file mode 100644 index 000000000000..c20c17ce9d86 --- /dev/null +++ b/documentation/src/test/java/example/extensions/ParameterResolverConflictDemo.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.extensions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import extensions.ExpectToFail; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; + +// tag::user_guide[] +public class ParameterResolverConflictDemo { + + // end::user_guide[] + @ExpectToFail + // tag::user_guide[] + @Test + @ExtendWith({ FirstIntegerResolver.class, SecondIntegerResolver.class }) + void testInt(int i) { + // Test will not run due to ParameterResolutionException + assertEquals(1, i); + } + + static class FirstIntegerResolver implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.getParameter().getType() == int.class; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return 1; + } + } + + static class SecondIntegerResolver implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.getParameter().getType() == int.class; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return 2; + } + } +} +// end::user_guide[] diff --git a/documentation/src/test/java/example/extensions/ParameterResolverCustomAnnotationDemo.java b/documentation/src/test/java/example/extensions/ParameterResolverCustomAnnotationDemo.java new file mode 100644 index 000000000000..8e68516ce659 --- /dev/null +++ b/documentation/src/test/java/example/extensions/ParameterResolverCustomAnnotationDemo.java @@ -0,0 +1,74 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.extensions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; + +// tag::user_guide[] +public class ParameterResolverCustomAnnotationDemo { + + @Test + void testInt(@FirstInteger Integer first, @SecondInteger Integer second) { + assertEquals(1, first); + assertEquals(2, second); + } + + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.RUNTIME) + @ExtendWith(FirstInteger.Extension.class) + public @interface FirstInteger { + + class Extension implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.getParameter().getType().equals(Integer.class) + && !parameterContext.isAnnotated(SecondInteger.class); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return 1; + } + } + } + + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.RUNTIME) + @ExtendWith(SecondInteger.Extension.class) + public @interface SecondInteger { + + class Extension implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.isAnnotated(SecondInteger.class); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return 2; + } + } + } +} +// end::user_guide[] diff --git a/documentation/src/test/java/example/extensions/ParameterResolverCustomTypeDemo.java b/documentation/src/test/java/example/extensions/ParameterResolverCustomTypeDemo.java new file mode 100644 index 000000000000..c0b5b374c45d --- /dev/null +++ b/documentation/src/test/java/example/extensions/ParameterResolverCustomTypeDemo.java @@ -0,0 +1,67 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.extensions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; + +// tag::user_guide[] +public class ParameterResolverCustomTypeDemo { + + @Test + @ExtendWith({ FirstIntegerResolver.class, SecondIntegerResolver.class }) + void testInt(Integer i, WrappedInteger wrappedInteger) { + assertEquals(1, i); + assertEquals(2, wrappedInteger.value); + } + + static class FirstIntegerResolver implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.getParameter().getType().equals(Integer.class); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return 1; + } + } + + static class SecondIntegerResolver implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.getParameter().getType().equals(WrappedInteger.class); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return new WrappedInteger(2); + } + } + + static class WrappedInteger { + + private final int value; + + public WrappedInteger(int value) { + this.value = value; + } + + } +} +// end::user_guide[] diff --git a/documentation/src/test/java/example/extensions/ParameterResolverNoConflictDemo.java b/documentation/src/test/java/example/extensions/ParameterResolverNoConflictDemo.java new file mode 100644 index 000000000000..8f306a728632 --- /dev/null +++ b/documentation/src/test/java/example/extensions/ParameterResolverNoConflictDemo.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.extensions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; + +// tag::user_guide[] +public class ParameterResolverNoConflictDemo { + + @Test + @ExtendWith(FirstIntegerResolver.class) + void firstResolution(int i) { + assertEquals(1, i); + } + + @Test + @ExtendWith(SecondIntegerResolver.class) + void secondResolution(int i) { + assertEquals(2, i); + } + + static class FirstIntegerResolver implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.getParameter().getType() == int.class; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return 1; + } + } + + static class SecondIntegerResolver implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.getParameter().getType() == int.class; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return 2; + } + } +} +// end::user_guide[] diff --git a/documentation/src/test/java/example/extensions/Random.java b/documentation/src/test/java/example/extensions/Random.java index 4b9a904d0bb1..ee4e32322630 100644 --- a/documentation/src/test/java/example/extensions/Random.java +++ b/documentation/src/test/java/example/extensions/Random.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/extensions/RandomNumberDemo.java b/documentation/src/test/java/example/extensions/RandomNumberDemo.java index 8dd274bda73c..8ace575739c3 100644 --- a/documentation/src/test/java/example/extensions/RandomNumberDemo.java +++ b/documentation/src/test/java/example/extensions/RandomNumberDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -27,7 +27,7 @@ class RandomNumberDemo { private int randomNumber1; RandomNumberDemo(@Random int randomNumber2) { - // Use randomNumber2 in constructor + // Use randomNumber2 in constructor. } @BeforeEach diff --git a/documentation/src/test/java/example/extensions/RandomNumberExtension.java b/documentation/src/test/java/example/extensions/RandomNumberExtension.java index 3bbece08e206..2317997eb8c3 100644 --- a/documentation/src/test/java/example/extensions/RandomNumberExtension.java +++ b/documentation/src/test/java/example/extensions/RandomNumberExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -86,7 +86,7 @@ private void injectFields(Class testClass, Object testInstance, } private static boolean isInteger(Class type) { - return int.class.isAssignableFrom(type); + return type == Integer.class || type == int.class; } } diff --git a/documentation/src/test/java/example/interceptor/SwingEdtInterceptor.java b/documentation/src/test/java/example/interceptor/SwingEdtInterceptor.java index b12094f382b9..326d3e02fdc4 100644 --- a/documentation/src/test/java/example/interceptor/SwingEdtInterceptor.java +++ b/documentation/src/test/java/example/interceptor/SwingEdtInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/registration/DocumentationDemo.java b/documentation/src/test/java/example/registration/DocumentationDemo.java index 973b4f1967e3..15971c249f1f 100644 --- a/documentation/src/test/java/example/registration/DocumentationDemo.java +++ b/documentation/src/test/java/example/registration/DocumentationDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/registration/WebServerDemo.java b/documentation/src/test/java/example/registration/WebServerDemo.java index 25a6b2797305..bd0f32587fdc 100644 --- a/documentation/src/test/java/example/registration/WebServerDemo.java +++ b/documentation/src/test/java/example/registration/WebServerDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -31,6 +31,9 @@ class WebServerDemo { @Test void getProductList() { + // end::user_guide[] + @SuppressWarnings("resource") + // tag::user_guide[] WebClient webClient = new WebClient(); String serverUrl = server.getServerUrl(); // Use WebClient to connect to web server using serverUrl and verify response diff --git a/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java b/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java index 595be12b4717..bbf367cee897 100644 --- a/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java +++ b/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -37,7 +37,7 @@ public void launcherSessionOpened(LauncherSession session) { @Override public void testPlanExecutionStarted(TestPlan testPlan) { //end::user_guide[] - if (!testPlan.getConfigurationParameters().getBoolean("enableHttpServer").orElse(false)) { + if (!testPlan.getConfigurationParameters().getBoolean("enableHttpServer").orElse(true)) { // avoid starting multiple HTTP servers unnecessarily from UsingTheLauncherDemo return; } diff --git a/documentation/src/test/java/example/session/HttpTests.java b/documentation/src/test/java/example/session/HttpTests.java index cbdf5367cdfd..564834f8260a 100644 --- a/documentation/src/test/java/example/session/HttpTests.java +++ b/documentation/src/test/java/example/session/HttpTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/testinterface/TestInterfaceDemo.java b/documentation/src/test/java/example/testinterface/TestInterfaceDemo.java index b24ab3413733..bfb8b9938fcb 100644 --- a/documentation/src/test/java/example/testinterface/TestInterfaceDemo.java +++ b/documentation/src/test/java/example/testinterface/TestInterfaceDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/testinterface/TestInterfaceDynamicTestsDemo.java b/documentation/src/test/java/example/testinterface/TestInterfaceDynamicTestsDemo.java index 694192dbabf0..0b78dabc2660 100644 --- a/documentation/src/test/java/example/testinterface/TestInterfaceDynamicTestsDemo.java +++ b/documentation/src/test/java/example/testinterface/TestInterfaceDynamicTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/testinterface/TestLifecycleLogger.java b/documentation/src/test/java/example/testinterface/TestLifecycleLogger.java index b269bfb69a1b..c7741aad38d6 100644 --- a/documentation/src/test/java/example/testinterface/TestLifecycleLogger.java +++ b/documentation/src/test/java/example/testinterface/TestLifecycleLogger.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/testinterface/TimeExecutionLogger.java b/documentation/src/test/java/example/testinterface/TimeExecutionLogger.java index b3a53feae91a..57bd61ec4651 100644 --- a/documentation/src/test/java/example/testinterface/TimeExecutionLogger.java +++ b/documentation/src/test/java/example/testinterface/TimeExecutionLogger.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/testkit/EngineTestKitAllEventsDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitAllEventsDemo.java index 5df68019e433..d7d5a4896057 100644 --- a/documentation/src/test/java/example/testkit/EngineTestKitAllEventsDemo.java +++ b/documentation/src/test/java/example/testkit/EngineTestKitAllEventsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -62,7 +62,7 @@ void verifyAllJupiterEvents() { message(m -> m.contains("abc does not contain Z")))), event(test("failingTest"), started()), event(test("failingTest"), finishedWithFailure( - instanceOf(ArithmeticException.class), message("/ by zero"))), + instanceOf(ArithmeticException.class), message(it -> it.endsWith("by zero")))), event(container(ExampleTestCase.class), finishedSuccessfully()), event(engine(), finishedSuccessfully())); } diff --git a/documentation/src/test/java/example/testkit/EngineTestKitFailedMethodDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitFailedMethodDemo.java index 40ea51ba8a80..18b719fab536 100644 --- a/documentation/src/test/java/example/testkit/EngineTestKitFailedMethodDemo.java +++ b/documentation/src/test/java/example/testkit/EngineTestKitFailedMethodDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -36,7 +36,7 @@ void verifyJupiterMethodFailed() { .assertThatEvents().haveExactly(1, // <5> event(test("failingTest"), finishedWithFailure( - instanceOf(ArithmeticException.class), message("/ by zero")))); + instanceOf(ArithmeticException.class), message(it -> it.endsWith("by zero"))))); } } diff --git a/documentation/src/test/java/example/testkit/EngineTestKitSkippedMethodDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitSkippedMethodDemo.java index 1d06770f1a25..009d591c7e78 100644 --- a/documentation/src/test/java/example/testkit/EngineTestKitSkippedMethodDemo.java +++ b/documentation/src/test/java/example/testkit/EngineTestKitSkippedMethodDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/testkit/EngineTestKitStatisticsDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitStatisticsDemo.java index b9576352389b..44d41feeb244 100644 --- a/documentation/src/test/java/example/testkit/EngineTestKitStatisticsDemo.java +++ b/documentation/src/test/java/example/testkit/EngineTestKitStatisticsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/timing/TimingExtension.java b/documentation/src/test/java/example/timing/TimingExtension.java index b6b51d61ce99..ff1c9079abf9 100644 --- a/documentation/src/test/java/example/timing/TimingExtension.java +++ b/documentation/src/test/java/example/timing/TimingExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/example/timing/TimingExtensionTests.java b/documentation/src/test/java/example/timing/TimingExtensionTests.java index ee84b196b066..fd04a5d60448 100644 --- a/documentation/src/test/java/example/timing/TimingExtensionTests.java +++ b/documentation/src/test/java/example/timing/TimingExtensionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/extensions/DisabledOnOpenJ9.java b/documentation/src/test/java/extensions/DisabledOnOpenJ9.java new file mode 100644 index 000000000000..f0a2a6206bd4 --- /dev/null +++ b/documentation/src/test/java/extensions/DisabledOnOpenJ9.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package extensions; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DisabledIfSystemProperty(named = "java.vm.vendor", matches = ".*OpenJ9.*") +public @interface DisabledOnOpenJ9 { +} diff --git a/documentation/src/test/java/extensions/ExpectToFail.java b/documentation/src/test/java/extensions/ExpectToFail.java index 8dd21a717caa..adea1dcaeffa 100644 --- a/documentation/src/test/java/extensions/ExpectToFail.java +++ b/documentation/src/test/java/extensions/ExpectToFail.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/documentation/src/test/java/org/junit/api/tools/ApiReport.java b/documentation/src/test/java/org/junit/api/tools/ApiReport.java deleted file mode 100644 index b21e63e8face..000000000000 --- a/documentation/src/test/java/org/junit/api/tools/ApiReport.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2015-2023 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.api.tools; - -import java.util.List; -import java.util.Map; - -import org.apiguardian.api.API.Status; - -/** - * @since 1.0 - */ -class ApiReport { - - private final List> types; - - private final Map>> declarationsMap; - - ApiReport(List> types, Map>> declarationsMap) { - this.types = types; - this.declarationsMap = declarationsMap; - } - - List> getTypes() { - return this.types; - } - - Map>> getDeclarationsMap() { - return this.declarationsMap; - } - -} diff --git a/documentation/src/test/java/org/junit/api/tools/ApiReportGenerator.java b/documentation/src/test/java/org/junit/api/tools/ApiReportGenerator.java deleted file mode 100644 index 7bdb6c21b40d..000000000000 --- a/documentation/src/test/java/org/junit/api/tools/ApiReportGenerator.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2015-2023 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.api.tools; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; - -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ClassInfoList; -import io.github.classgraph.ScanResult; - -import org.apiguardian.api.API; -import org.apiguardian.api.API.Status; -import org.junit.platform.commons.logging.Logger; -import org.junit.platform.commons.logging.LoggerFactory; - -/** - * @since 1.0 - */ -class ApiReportGenerator { - - public static void main(String... args) { - - // CAUTION: The output produced by this method is used to - // generate a table in the User Guide. - - PrintWriter writer = new PrintWriter(System.out, true); - ApiReportGenerator reportGenerator = new ApiReportGenerator(); - - // scan all types below "org.junit" package - ApiReport apiReport = reportGenerator.generateReport("org.junit"); - - // ApiReportWriter reportWriter = new MarkdownApiReportWriter(apiReport); - ApiReportWriter reportWriter = new AsciidocApiReportWriter(apiReport); - // ApiReportWriter reportWriter = new HtmlApiReportWriter(apiReport); - - // reportWriter.printReportHeader(writer); - - // Print report for all Usage enum constants - // reportWriter.printDeclarationInfo(writer, EnumSet.allOf(Usage.class)); - - // Print report only for a specific Status constant, defaults to EXPERIMENTAL - Status status = Status.EXPERIMENTAL; - if (args.length == 1) { - status = Status.valueOf(args[0]); - } - reportWriter.printDeclarationInfo(writer, EnumSet.of(status)); - } - - // ------------------------------------------------------------------------- - - ApiReport generateReport(String... packages) { - Logger logger = LoggerFactory.getLogger(ApiReportGenerator.class); - String EOL = System.lineSeparator(); - ClassGraph classGraph = new ClassGraph() // - .acceptPackages(packages) // - .disableNestedJarScanning() // - .enableAnnotationInfo(); // - String apiClasspath = System.getProperty("api.classpath"); - if (apiClasspath != null) { - classGraph = classGraph.overrideClasspath(apiClasspath); - } - - // Scan packages - try (ScanResult scanResult = classGraph.scan()) { - - // Collect names - ClassInfoList classesWithApiAnnotation = scanResult.getClassesWithAnnotation(API.class.getCanonicalName()); - List names = classesWithApiAnnotation.getNames(); - - logger.debug(() -> { - StringBuilder builder = new StringBuilder( - names.size() + " @API declarations (including meta) found in class-path:"); - builder.append(EOL); - scanResult.getClasspathURLs().forEach(e -> builder.append(e).append(EOL)); - return builder.toString(); - }); - - // Collect types - List> types = classesWithApiAnnotation.loadClasses(); - // only retain directly annotated types - types.removeIf(c -> !c.isAnnotationPresent(API.class)); - types.sort(Comparator.comparing(Class::getName)); - - logger.debug(() -> { - StringBuilder builder = new StringBuilder("Listing of all " + types.size() + " annotated types:"); - builder.append(EOL); - types.forEach(e -> builder.append(e).append(EOL)); - return builder.toString(); - }); - - // Build map - Map>> declarationsMap = new EnumMap<>(Status.class); - for (Status status : Status.values()) { - declarationsMap.put(status, new ArrayList<>()); - } - types.forEach(type -> declarationsMap.get(type.getAnnotation(API.class).status()).add(type)); - - // Create report - return new ApiReport(types, declarationsMap); - } - } - -} diff --git a/documentation/src/test/java21/example/DynamicTestsNamedDemo.java b/documentation/src/test/java21/example/DynamicTestsNamedDemo.java new file mode 100644 index 000000000000..5453294d451b --- /dev/null +++ b/documentation/src/test/java21/example/DynamicTestsNamedDemo.java @@ -0,0 +1,76 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example; + +// tag::user_guide[] + +import static example.util.StringUtils.isPalindrome; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Named.named; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.NamedExecutable; +import org.junit.jupiter.api.TestFactory; + +public class DynamicTestsNamedDemo { + + @TestFactory + Stream dynamicTestsFromStreamFactoryMethodWithNames() { + // Stream of palindromes to check + // end::user_guide[] + // @formatter:off + // tag::user_guide[] + var inputStream = Stream.of( + named("racecar is a palindrome", "racecar"), + named("radar is also a palindrome", "radar"), + named("mom also seems to be a palindrome", "mom"), + named("dad is yet another palindrome", "dad") + ); + // end::user_guide[] + // @formatter:on + // tag::user_guide[] + + // Returns a stream of dynamic tests. + return DynamicTest.stream(inputStream, text -> assertTrue(isPalindrome(text))); + } + + @TestFactory + Stream dynamicTestsFromStreamFactoryMethodWithNamedExecutables() { + // Stream of palindromes to check + // end::user_guide[] + // @formatter:off + // tag::user_guide[] + var inputStream = Stream.of("racecar", "radar", "mom", "dad") + .map(PalindromeNamedExecutable::new); + // end::user_guide[] + // @formatter:on + // tag::user_guide[] + + // Returns a stream of dynamic tests based on NamedExecutables. + return DynamicTest.stream(inputStream); + } + + record PalindromeNamedExecutable(String text) implements NamedExecutable { + + @Override + public String getName() { + return String.format("'%s' is a palindrome", text); + } + + @Override + public void execute() { + assertTrue(isPalindrome(text)); + } + } +} +// end::user_guide[] diff --git a/documentation/src/test/java21/example/MyFirstJUnitJupiterRecordTests.java b/documentation/src/test/java21/example/MyFirstJUnitJupiterRecordTests.java new file mode 100644 index 000000000000..d7f213b81451 --- /dev/null +++ b/documentation/src/test/java21/example/MyFirstJUnitJupiterRecordTests.java @@ -0,0 +1,28 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example; + +// tag::user_guide[] +import static org.junit.jupiter.api.Assertions.assertEquals; + +import example.util.Calculator; + +import org.junit.jupiter.api.Test; + +record MyFirstJUnitJupiterRecordTests() { + + @Test + void addition() { + assertEquals(2, new Calculator().add(1, 1)); + } + +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/FibonacciCalculator.kt b/documentation/src/test/kotlin/example/FibonacciCalculator.kt index 33ddbe089ff1..bcf2158f90de 100644 --- a/documentation/src/test/kotlin/example/FibonacciCalculator.kt +++ b/documentation/src/test/kotlin/example/FibonacciCalculator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,20 +10,19 @@ package example class FibonacciCalculator { - - private val fibonacci = sequence { - yield(0) // 0th Fibonacci number - yield(1) // first Fibonacci number - var cur = 1 - var next = 1 - while (true) { - yield(next) // next Fibonacci number - val tmp = cur + next - cur = next - next = tmp + private val fibonacci = + sequence { + yield(0) // 0th Fibonacci number + yield(1) // first Fibonacci number + var cur = 1 + var next = 1 + while (true) { + yield(next) // next Fibonacci number + val tmp = cur + next + cur = next + next = tmp + } } - } - fun fib(fibonacciNumber: Int) = - fibonacci.elementAt(fibonacciNumber) + fun fib(fibonacciNumber: Int) = fibonacci.elementAt(fibonacciNumber) } diff --git a/documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt b/documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt index 0ea73f7f99d8..efd0566ac0d8 100644 --- a/documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt +++ b/documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -25,25 +25,29 @@ import org.junit.jupiter.api.assertTimeoutPreemptively import java.time.Duration class KotlinAssertionsDemo { - private val person = Person("Jane", "Doe") private val people = setOf(person, Person("John", "Doe")) @Test fun `exception absence testing`() { val calculator = Calculator() - val result = assertDoesNotThrow("Should not throw an exception") { - calculator.divide(0, 1) - } + val result = + assertDoesNotThrow("Should not throw an exception") { + calculator.divide(0, 1) + } assertEquals(0, result) } + // end::user_guide[] + @extensions.DisabledOnOpenJ9 + // tag::user_guide[] @Test fun `expected exception testing`() { val calculator = Calculator() - val exception = assertThrows ("Should throw an exception") { - calculator.divide(1, 0) - } + val exception = + assertThrows ("Should throw an exception") { + calculator.divide(1, 0) + } assertEquals("/ by zero", exception.message) } @@ -83,9 +87,10 @@ class KotlinAssertionsDemo { @Test fun `timeout not exceeded testing`() { val fibonacciCalculator = FibonacciCalculator() - val result = assertTimeout(Duration.ofMillis(1000)) { - fibonacciCalculator.fib(14) - } + val result = + assertTimeout(Duration.ofMillis(1000)) { + fibonacciCalculator.fib(14) + } assertEquals(377, result) } diff --git a/documentation/src/test/kotlin/example/registration/KotlinWebServerDemo.kt b/documentation/src/test/kotlin/example/registration/KotlinWebServerDemo.kt index 52a390fe7e0f..65b729d7b2dd 100644 --- a/documentation/src/test/kotlin/example/registration/KotlinWebServerDemo.kt +++ b/documentation/src/test/kotlin/example/registration/KotlinWebServerDemo.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -15,13 +15,14 @@ import org.junit.jupiter.api.extension.RegisterExtension // tag::user_guide[] class KotlinWebServerDemo { - companion object { - @JvmStatic + @JvmField @RegisterExtension - val server = WebServerExtension.builder() - .enableSecurity(false) - .build() + val server = + WebServerExtension + .builder() + .enableSecurity(false) + .build()!! } @Test diff --git a/documentation/src/test/java/org/junit/api/tools/AbstractApiReportWriter.java b/documentation/src/tools/java/org/junit/api/tools/AbstractApiReportWriter.java similarity index 57% rename from documentation/src/test/java/org/junit/api/tools/AbstractApiReportWriter.java rename to documentation/src/tools/java/org/junit/api/tools/AbstractApiReportWriter.java index 6b9a143ca463..f822e6f8e935 100644 --- a/documentation/src/test/java/org/junit/api/tools/AbstractApiReportWriter.java +++ b/documentation/src/tools/java/org/junit/api/tools/AbstractApiReportWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,8 +13,8 @@ import static java.lang.String.format; import java.io.PrintWriter; -import java.util.EnumSet; import java.util.List; +import java.util.Set; import org.apiguardian.api.API.Status; @@ -23,6 +23,8 @@ */ abstract class AbstractApiReportWriter implements ApiReportWriter { + protected static final int NAME_COLUMN_WIDTH = 128; + private final ApiReport apiReport; AbstractApiReportWriter(ApiReport apiReport) { @@ -34,31 +36,28 @@ public void printReportHeader(PrintWriter out) { out.println(h1("@API Declarations")); out.println(); out.println(paragraph( - format("Discovered %d types with %s declarations.", this.apiReport.getTypes().size(), code("@API")))); + format("Discovered %d types with %s declarations.", this.apiReport.types().size(), code("@API")))); out.println(); } @Override - public void printDeclarationInfo(PrintWriter out, EnumSet statuses) { - // @formatter:off - this.apiReport.getDeclarationsMap().entrySet().stream() - .filter(e -> statuses.contains(e.getKey())) - .forEach(e -> printDeclarationSection(statuses, e.getKey(), e.getValue(), out)); - // @formatter:on + public void printDeclarationInfo(PrintWriter out, Set statuses) { + statuses.forEach( + status -> printDeclarationSection(statuses, status, this.apiReport.declarations().get(status), out)); } - protected void printDeclarationSection(EnumSet statuses, Status status, List> types, + protected void printDeclarationSection(Set statuses, Status status, List declarations, PrintWriter out) { - printDeclarationSectionHeader(statuses, status, types, out); - if (types.size() > 0) { + printDeclarationSectionHeader(statuses, status, declarations, out); + if (!declarations.isEmpty()) { printDeclarationTableHeader(out); - types.forEach(type -> printDeclarationTableRow(type, out)); + declarations.forEach(it -> printDeclarationTableRow(it, out)); printDeclarationTableFooter(out); out.println(); } } - protected void printDeclarationSectionHeader(EnumSet statuses, Status status, List> types, + protected void printDeclarationSectionHeader(Set statuses, Status status, List declarations, PrintWriter out) { if (statuses.size() < 2) { // omit section header when only a single status is printed @@ -66,7 +65,8 @@ protected void printDeclarationSectionHeader(EnumSet statuses, Status st } out.println(h2(format("@API(%s)", status))); out.println(); - out.println(paragraph(format("Discovered %d " + code("@API(%s)") + " declarations.", types.size(), status))); + out.println( + paragraph(format("Discovered %d " + code("@API(%s)") + " declarations.", declarations.size(), status))); out.println(); } @@ -84,21 +84,8 @@ protected String paragraph(String element) { protected abstract void printDeclarationTableHeader(PrintWriter out); - protected abstract void printDeclarationTableRow(Class type, PrintWriter out); + protected abstract void printDeclarationTableRow(Declaration declaration, PrintWriter out); protected abstract void printDeclarationTableFooter(PrintWriter out); - protected String getKind(Class type) { - if (type.isAnnotation()) { - return "annotation"; - } - if (type.isEnum()) { - return "enum"; - } - if (type.isInterface()) { - return "interface"; - } - return "class"; - } - } diff --git a/documentation/src/tools/java/org/junit/api/tools/ApiReport.java b/documentation/src/tools/java/org/junit/api/tools/ApiReport.java new file mode 100644 index 000000000000..6478b10ad25d --- /dev/null +++ b/documentation/src/tools/java/org/junit/api/tools/ApiReport.java @@ -0,0 +1,25 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.api.tools; + +import java.util.List; +import java.util.Map; +import java.util.SortedSet; + +import io.github.classgraph.ClassInfo; + +import org.apiguardian.api.API.Status; + +/** + * @since 1.0 + */ +record ApiReport(SortedSet types, Map> declarations) { +} diff --git a/documentation/src/tools/java/org/junit/api/tools/ApiReportGenerator.java b/documentation/src/tools/java/org/junit/api/tools/ApiReportGenerator.java new file mode 100644 index 000000000000..e8e0df28b74d --- /dev/null +++ b/documentation/src/tools/java/org/junit/api/tools/ApiReportGenerator.java @@ -0,0 +1,168 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.api.tools; + +import static java.util.stream.Collectors.toCollection; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.stream.Stream; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.ScanResult; + +import org.apiguardian.api.API; +import org.apiguardian.api.API.Status; +import org.junit.platform.commons.logging.Logger; +import org.junit.platform.commons.logging.LoggerFactory; + +/** + * @since 1.0 + */ +class ApiReportGenerator { + + private static final Logger LOGGER = LoggerFactory.getLogger(ApiReportGenerator.class); + private static final String EOL = System.lineSeparator(); + + public static void main(String... args) { + + // CAUTION: The output produced by this method is used to + // generate a table in the User Guide. + + var reportGenerator = new ApiReportGenerator(); + + // scan all types below "org.junit" package + var apiReport = reportGenerator.generateReport("org.junit"); + + // ApiReportWriter reportWriter = new MarkdownApiReportWriter(apiReport); + ApiReportWriter reportWriter = new AsciidocApiReportWriter(apiReport); + // ApiReportWriter reportWriter = new HtmlApiReportWriter(apiReport); + + // reportWriter.printReportHeader(new PrintWriter(System.out, true)); + + // Print report for all Usage enum constants + // reportWriter.printDeclarationInfo(new PrintWriter(System.out, true), EnumSet.allOf(Status.class)); + + // Print report only for specific Status constants, defaults to only EXPERIMENTAL + parseArgs(args).forEach((status, opener) -> { + try (var stream = opener.openStream()) { + var writer = new PrintWriter(stream == null ? System.out : stream, true); + reportWriter.printDeclarationInfo(writer, EnumSet.of(status)); + } + catch (IOException e) { + throw new UncheckedIOException("Failed to write report", e); + } + }); + } + + // ------------------------------------------------------------------------- + + private static Map parseArgs(String[] args) { + Map outputByStatus = new EnumMap<>(Status.class); + if (args.length == 0) { + outputByStatus.put(Status.EXPERIMENTAL, () -> null); + } + else { + Arrays.stream(args) // + .map(arg -> arg.split("=", 2)) // + .forEach(parts -> outputByStatus.put(// + Status.valueOf(parts[0]), // + () -> parts.length < 2 // + ? null // + : new BufferedOutputStream(Files.newOutputStream(Path.of(parts[1]))) // + )); + } + return outputByStatus; + } + + private interface StreamOpener { + OutputStream openStream() throws IOException; + } + + ApiReport generateReport(String... packages) { + Map> declarations = new EnumMap<>(Status.class); + for (var status : Status.values()) { + declarations.put(status, new ArrayList<>()); + } + + try (var scanResult = scanClasspath(packages)) { + + var types = collectTypes(scanResult); + types.stream() // + .map(Declaration.Type::new) // + .forEach(type -> declarations.get(type.status()).add(type)); + + collectMethods(scanResult) // + .map(Declaration.Method::new) // + .filter(method -> !declarations.get(method.status()) // + .contains(new Declaration.Type(method.classInfo()))) // + .forEach(method -> { + types.add(method.classInfo()); + declarations.get(method.status()).add(method); + }); + + declarations.values().forEach(list -> list.sort(null)); + + return new ApiReport(types, declarations); + } + } + + private static ScanResult scanClasspath(String[] packages) { + var classGraph = new ClassGraph() // + .acceptPackages(packages) // + .disableNestedJarScanning() // + .enableClassInfo() // + .enableMethodInfo() // + .enableAnnotationInfo(); // + var apiClasspath = System.getProperty("api.classpath"); + if (apiClasspath != null) { + classGraph = classGraph.overrideClasspath(apiClasspath); + } + return classGraph.scan(); + } + + private static SortedSet collectTypes(ScanResult scanResult) { + var types = scanResult.getClassesWithAnnotation(API.class).stream() // + .filter(it -> !it.getAnnotationInfo(API.class).isInherited()) // + .collect(toCollection(TreeSet::new)); + + LOGGER.debug(() -> { + var builder = new StringBuilder("Listing of all " + types.size() + " annotated types:"); + builder.append(EOL); + types.forEach(e -> builder.append(e.getName()).append(EOL)); + return builder.toString(); + }); + + return types; + } + + private static Stream collectMethods(ScanResult scanResult) { + return scanResult.getClassesWithMethodAnnotation(API.class).stream() // + .flatMap(type -> type.getDeclaredMethodAndConstructorInfo().stream()) // + .filter(m -> m.getAnnotationInfo(API.class) != null); + } + +} diff --git a/documentation/src/test/java/org/junit/api/tools/ApiReportWriter.java b/documentation/src/tools/java/org/junit/api/tools/ApiReportWriter.java similarity index 75% rename from documentation/src/test/java/org/junit/api/tools/ApiReportWriter.java rename to documentation/src/tools/java/org/junit/api/tools/ApiReportWriter.java index c231d1b4ba43..65487727f694 100644 --- a/documentation/src/test/java/org/junit/api/tools/ApiReportWriter.java +++ b/documentation/src/tools/java/org/junit/api/tools/ApiReportWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,7 +11,7 @@ package org.junit.api.tools; import java.io.PrintWriter; -import java.util.EnumSet; +import java.util.Set; import org.apiguardian.api.API.Status; @@ -22,6 +22,6 @@ interface ApiReportWriter { void printReportHeader(PrintWriter out); - void printDeclarationInfo(PrintWriter out, EnumSet statuses); + void printDeclarationInfo(PrintWriter out, Set statuses); } diff --git a/documentation/src/test/java/org/junit/api/tools/AsciidocApiReportWriter.java b/documentation/src/tools/java/org/junit/api/tools/AsciidocApiReportWriter.java similarity index 62% rename from documentation/src/test/java/org/junit/api/tools/AsciidocApiReportWriter.java rename to documentation/src/tools/java/org/junit/api/tools/AsciidocApiReportWriter.java index 05748d36aee3..3c4f35fc624b 100644 --- a/documentation/src/test/java/org/junit/api/tools/AsciidocApiReportWriter.java +++ b/documentation/src/tools/java/org/junit/api/tools/AsciidocApiReportWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,14 +12,12 @@ import java.io.PrintWriter; -import org.apiguardian.api.API; - /** * @since 1.0 */ class AsciidocApiReportWriter extends AbstractApiReportWriter { - private static final String ASCIIDOC_FORMAT = "| %-52s | %-42s | %-12s%n"; + private static final String ASCIIDOC_FORMAT = "| %-52s | %-" + NAME_COLUMN_WIDTH + "s | %-12s%n"; AsciidocApiReportWriter(ApiReport apiReport) { super(apiReport); @@ -48,21 +46,16 @@ protected String italic(String element) { @Override protected void printDeclarationTableHeader(PrintWriter out) { out.println("|==="); - out.printf(ASCIIDOC_FORMAT, "Package Name", "Type Name", "Since"); + out.printf(ASCIIDOC_FORMAT, "Package Name", "Name", "Since"); out.println(); } @Override - protected void printDeclarationTableRow(Class type, PrintWriter out) { - String packageName = type.getPackage().getName(); - String typeName = type.getCanonicalName(); - if (typeName.startsWith(packageName + '.')) { - typeName = typeName.substring(packageName.length() + 1); - } + protected void printDeclarationTableRow(Declaration declaration, PrintWriter out) { out.printf(ASCIIDOC_FORMAT, // - code(packageName), // - code(typeName) + " " + italic("(" + getKind(type) + ")"), // - code(type.getAnnotation(API.class).since()) // + code(declaration.packageName()), // + code(declaration.name()) + " " + italic("(" + declaration.kind() + ")"), // + code(declaration.since()) // ); } diff --git a/documentation/src/tools/java/org/junit/api/tools/Declaration.java b/documentation/src/tools/java/org/junit/api/tools/Declaration.java new file mode 100644 index 000000000000..8cde00674801 --- /dev/null +++ b/documentation/src/tools/java/org/junit/api/tools/Declaration.java @@ -0,0 +1,157 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.api.tools; + +import static java.util.stream.Collectors.joining; + +import java.util.Arrays; + +import io.github.classgraph.AnnotationEnumValue; +import io.github.classgraph.AnnotationParameterValueList; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfo; + +import org.apiguardian.api.API; +import org.apiguardian.api.API.Status; + +sealed interface Declaration extends Comparable { + + String packageName(); + + String fullName(); + + String name(); + + String kind(); + + Status status(); + + String since(); + + @Override + default int compareTo(Declaration o) { + return fullName().compareTo(o.fullName()); + } + + record Type(ClassInfo classInfo) implements Declaration { + + @Override + public String packageName() { + return classInfo.getPackageName(); + } + + @Override + public String fullName() { + return classInfo.getName(); + } + + @Override + public String name() { + return getShortClassName(classInfo); + } + + @Override + public String kind() { + return switch (classInfo) { + case ClassInfo ignored when classInfo.isRecord() -> "record"; + case ClassInfo ignored when classInfo.isAnnotation() -> "annotation"; + case ClassInfo ignored when classInfo.isEnum() -> "enum"; + case ClassInfo ignored when classInfo.isInterface() -> "interface"; + default -> "class"; + }; + } + + @Override + public Status status() { + return readStatus(getParameterValues()); + } + + @Override + public String since() { + return readSince(getParameterValues()); + } + + private AnnotationParameterValueList getParameterValues() { + return classInfo.getAnnotationInfo(API.class).getParameterValues(); + } + } + + record Method(MethodInfo methodInfo) implements Declaration { + + @Override + public String packageName() { + return classInfo().getPackageName(); + } + + @Override + public String fullName() { + return "%s.%s".formatted(classInfo().getName(), methodSignature()); + } + + @Override + public String name() { + return "%s.%s".formatted(getShortClassName(classInfo()), methodSignature()); + } + + private String methodSignature() { + var parameters = Arrays.stream(methodInfo.getParameterInfo()) // + .map(parameterInfo -> parameterInfo.getTypeSignatureOrTypeDescriptor().toStringWithSimpleNames()) // + .collect(joining(", ", "(", ")")); + return methodInfo.getName() + parameters; + } + + @Override + public String kind() { + if (methodInfo.isConstructor()) { + return "constructor"; + } + if (classInfo().isAnnotation()) { + return "annotation attribute"; + } + return "method"; + } + + @Override + public Status status() { + return readStatus(getParameterValues()); + } + + @Override + public String since() { + return readSince(getParameterValues()); + } + + private AnnotationParameterValueList getParameterValues() { + return methodInfo.getAnnotationInfo(API.class).getParameterValues(); + } + + public ClassInfo classInfo() { + return methodInfo.getClassInfo(); + } + } + + private static Status readStatus(AnnotationParameterValueList parameterValues) { + return Status.valueOf(((AnnotationEnumValue) parameterValues.getValue("status")).getValueName()); + } + + private static String readSince(AnnotationParameterValueList parameterValues) { + return (String) parameterValues.getValue("since"); + } + + private static String getShortClassName(ClassInfo classInfo) { + var typeName = classInfo.getName(); + var packageName = classInfo.getPackageName(); + if (typeName.startsWith(packageName + '.')) { + typeName = typeName.substring(packageName.length() + 1); + } + return typeName; + } +} diff --git a/documentation/src/test/java/org/junit/api/tools/HtmlApiReportWriter.java b/documentation/src/tools/java/org/junit/api/tools/HtmlApiReportWriter.java similarity index 77% rename from documentation/src/test/java/org/junit/api/tools/HtmlApiReportWriter.java rename to documentation/src/tools/java/org/junit/api/tools/HtmlApiReportWriter.java index 48193368370e..dc042e183b78 100644 --- a/documentation/src/test/java/org/junit/api/tools/HtmlApiReportWriter.java +++ b/documentation/src/tools/java/org/junit/api/tools/HtmlApiReportWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,8 +12,6 @@ import java.io.PrintWriter; -import org.apiguardian.api.API; - /** * @since 1.0 */ @@ -54,15 +52,15 @@ protected String paragraph(String element) { @Override protected void printDeclarationTableHeader(PrintWriter out) { out.println(""); - out.printf(HTML_HEADER_FORMAT, "Package Name", "Type Name", "Since"); + out.printf(HTML_HEADER_FORMAT, "Package Name", "Name", "Since"); } @Override - protected void printDeclarationTableRow(Class type, PrintWriter out) { + protected void printDeclarationTableRow(Declaration declaration, PrintWriter out) { out.printf(HTML_ROW_FORMAT, // - code(type.getPackage().getName()), // - code(type.getSimpleName()) + " " + italic("(" + getKind(type) + ")"), // - code(type.getAnnotation(API.class).since()) // + code(declaration.packageName()), // + code(declaration.name()) + " " + italic("(" + declaration.kind() + ")"), // + code(declaration.since()) // ); } diff --git a/documentation/src/test/java/org/junit/api/tools/MarkdownApiReportWriter.java b/documentation/src/tools/java/org/junit/api/tools/MarkdownApiReportWriter.java similarity index 67% rename from documentation/src/test/java/org/junit/api/tools/MarkdownApiReportWriter.java rename to documentation/src/tools/java/org/junit/api/tools/MarkdownApiReportWriter.java index 6294ac58179b..75798266fc3c 100644 --- a/documentation/src/test/java/org/junit/api/tools/MarkdownApiReportWriter.java +++ b/documentation/src/tools/java/org/junit/api/tools/MarkdownApiReportWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,14 +13,12 @@ import java.io.PrintWriter; import java.nio.CharBuffer; -import org.apiguardian.api.API; - /** * @since 1.0 */ class MarkdownApiReportWriter extends AbstractApiReportWriter { - private static final String MARKDOWN_FORMAT = "%-52s | %-42s | %-12s | %-27s%n"; + private static final String MARKDOWN_FORMAT = "%-52s | %-" + NAME_COLUMN_WIDTH + "s | %-12s%n"; MarkdownApiReportWriter(ApiReport apiReport) { super(apiReport); @@ -48,8 +46,8 @@ protected String italic(String element) { @Override protected void printDeclarationTableHeader(PrintWriter out) { - out.printf(MARKDOWN_FORMAT, "Package Name", "Type Name", "Since"); - out.printf(MARKDOWN_FORMAT, dashes(52), dashes(42), dashes(12), dashes(27)); + out.printf(MARKDOWN_FORMAT, "Package Name", "Name", "Since"); + out.printf(MARKDOWN_FORMAT, dashes(52), dashes(NAME_COLUMN_WIDTH), dashes(12)); } private String dashes(int length) { @@ -57,11 +55,11 @@ private String dashes(int length) { } @Override - protected void printDeclarationTableRow(Class type, PrintWriter out) { + protected void printDeclarationTableRow(Declaration declaration, PrintWriter out) { out.printf(MARKDOWN_FORMAT, // - code(type.getPackage().getName()), // - code(type.getSimpleName()) + " " + italic("(" + getKind(type) + ")"), // - code(type.getAnnotation(API.class).since()) // + code(declaration.packageName()), // + code(declaration.name()) + " " + italic("(" + declaration.kind() + ")"), // + code(declaration.since()) // ); } diff --git a/documentation/src/tools/java/org/junit/api/tools/package-info.java b/documentation/src/tools/java/org/junit/api/tools/package-info.java new file mode 100644 index 000000000000..480a8851218d --- /dev/null +++ b/documentation/src/tools/java/org/junit/api/tools/package-info.java @@ -0,0 +1,5 @@ +/** + * Tools to generate reports based on {@link org.apiguardian.api.API} annotations. + */ + +package org.junit.api.tools; diff --git a/gradle.properties b/gradle.properties index 8bbcba21a037..2c411150793d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,15 +1,13 @@ group = org.junit -version = 5.10.0 +version = 5.11.3 jupiterGroup = org.junit.jupiter platformGroup = org.junit.platform -platformVersion = 1.10.0 +platformVersion = 1.11.3 vintageGroup = org.junit.vintage -vintageVersion = 5.10.0 - -defaultBuiltBy = JUnit Team +vintageVersion = 5.11.3 # We need more metaspace due to apparent memory leak in Asciidoctor/JRuby # The exports are needed due to https://github.com/diffplug/spotless/issues/834 @@ -21,11 +19,11 @@ org.gradle.jvmargs=-Xmx1g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryEr --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED org.gradle.caching=true org.gradle.parallel=true -org.gradle.java.installations.fromEnv=JDK8,JDK18,JDK19,JDK20,JDK21 +org.gradle.java.installations.fromEnv=JDK8,JDK18,JDK19,JDK20,JDK21,JDK22,JDK23,JDK24 org.gradle.kotlin.dsl.allWarningsAsErrors=true # Test Distribution -gradle.internal.testdistribution.writeTraceFile=true +develocity.internal.testdistribution.writeTraceFile=true # Omit automatic compile dependency on kotlin-stdlib # https://kotlinlang.org/docs/gradle.html#dependency-on-the-standard-library diff --git a/gradle/base/code-generator-model/build.gradle.kts b/gradle/base/code-generator-model/build.gradle.kts new file mode 100644 index 000000000000..aa3ba93c438a --- /dev/null +++ b/gradle/base/code-generator-model/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + `kotlin-dsl` +} + +group = "junitbuild.base" + +repositories { + gradlePluginPortal() +} diff --git a/gradle/base/code-generator-model/src/main/kotlin/junitbuild/generator/model/JRE.kt b/gradle/base/code-generator-model/src/main/kotlin/junitbuild/generator/model/JRE.kt new file mode 100644 index 000000000000..a4bc8b4c4606 --- /dev/null +++ b/gradle/base/code-generator-model/src/main/kotlin/junitbuild/generator/model/JRE.kt @@ -0,0 +1,3 @@ +package junitbuild.generator.model + +data class JRE(val version: Int, val since: String?) diff --git a/gradle/base/code-generator-model/src/main/resources/jre.yaml b/gradle/base/code-generator-model/src/main/resources/jre.yaml new file mode 100644 index 000000000000..ed1bdd3d59fc --- /dev/null +++ b/gradle/base/code-generator-model/src/main/resources/jre.yaml @@ -0,0 +1,30 @@ +- version: 8 +- version: 9 +- version: 10 +- version: 11 +- version: 12 + since: '5.4' +- version: 13 + since: '5.4' +- version: 14 + since: '5.5' +- version: 15 + since: '5.6' +- version: 16 + since: '5.7' +- version: 17 + since: '5.7.1' +- version: 18 + since: '5.8.1' +- version: 19 + since: '5.9' +- version: 20 + since: '5.9' +- version: 21 + since: '5.9.2' +- version: 22 + since: '5.10' +- version: 23 + since: '5.11' +- version: 24 + since: '5.11' diff --git a/gradle/base/settings.gradle.kts b/gradle/base/settings.gradle.kts new file mode 100644 index 000000000000..66f6697452eb --- /dev/null +++ b/gradle/base/settings.gradle.kts @@ -0,0 +1,3 @@ +rootProject.name = "base" + +include("code-generator-model") diff --git a/gradle/config/checkstyle/checkstyleMain.xml b/gradle/config/checkstyle/checkstyleMain.xml index 447d02df9c62..27ff53749020 100644 --- a/gradle/config/checkstyle/checkstyleMain.xml +++ b/gradle/config/checkstyle/checkstyleMain.xml @@ -20,6 +20,13 @@ + + + + + + + diff --git a/gradle/config/checkstyle/checkstyleTest.xml b/gradle/config/checkstyle/checkstyleTest.xml index 6ac9b5d89eb5..b795207938ad 100644 --- a/gradle/config/checkstyle/checkstyleTest.xml +++ b/gradle/config/checkstyle/checkstyleTest.xml @@ -12,6 +12,13 @@ + + + + + + + diff --git a/gradle/config/checkstyle/suppressions.xml b/gradle/config/checkstyle/suppressions.xml index 05801fccce21..e858ede7e5b1 100644 --- a/gradle/config/checkstyle/suppressions.xml +++ b/gradle/config/checkstyle/suppressions.xml @@ -4,4 +4,6 @@ files="junit-platform-commons[\\/]src[\\/]main[\\/]java.+?[\\/]org[\\/]junit[\\/]platform[\\/]commons[\\/]util[\\/]*"/> + diff --git a/gradle/config/eclipse/junit-eclipse-formatter-settings.xml b/gradle/config/eclipse/junit-eclipse-formatter-settings.xml index 029e5df187bb..34f4f9dda2b1 100644 --- a/gradle/config/eclipse/junit-eclipse-formatter-settings.xml +++ b/gradle/config/eclipse/junit-eclipse-formatter-settings.xml @@ -1,295 +1,404 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gradle/config/spotless/eclipse-public-license-2.0.java b/gradle/config/spotless/eclipse-public-license-2.0.java index 9bbea1d478a3..010f99b9800a 100644 --- a/gradle/config/spotless/eclipse-public-license-2.0.java +++ b/gradle/config/spotless/eclipse-public-license-2.0.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 000000000000..63e5bbdf4845 --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,2 @@ +#This file is generated by updateDaemonJvm +toolchainVersion=21 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d69b3f911c10..a54336afebdc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,69 +1,74 @@ [versions] -ant = "1.10.13" +ant = "1.10.14" apiguardian = "1.1.2" -asciidoctor-pdf = "1.5.3" -asciidoctor-plugins = "4.0.0-alpha.1" # Check if workaround in documentation.gradle.kts can be removed when upgrading -assertj = "3.24.2" -bnd = "6.4.0" -checkstyle = "10.12.1" -gradleVersionsPlugin = "0.47.0" -jacoco = "0.8.7" -jmh = "1.36" +asciidoctorj-pdf = "2.3.18" +asciidoctor-plugins = "4.0.3" # Check if workaround in documentation.gradle.kts can be removed when upgrading +assertj = "3.26.3" +bnd = "7.0.0" +checkstyle = "10.17.0" +eclipse = "4.32.0" +jackson = "2.17.2" +jacoco = "0.8.12" +jmh = "1.37" junit4 = "4.13.2" -junit4Osgi = "4.13.2_1" junit4Min = "4.12" -ktlint = "0.48.2" -log4j = "2.20.0" +ktlint = "1.3.1" +log4j = "2.23.1" opentest4j = "1.3.0" -openTestReporting = "0.1.0-M1" -surefire = "3.1.2" -xmlunit = "2.9.1" +openTestReporting = "0.1.0-M2" +surefire = "3.3.1" +xmlunit = "2.10.0" [libraries] ant = { module = "org.apache.ant:ant", version.ref = "ant" } ant-junit = { module = "org.apache.ant:ant-junit", version.ref = "ant" } ant-junitlauncher = { module = "org.apache.ant:ant-junitlauncher", version.ref = "ant" } apiguardian = { module = "org.apiguardian:apiguardian-api", version.ref = "apiguardian" } -archunit = { module = "com.tngtech.archunit:archunit-junit5", version = "1.0.1" } +archunit = { module = "com.tngtech.archunit:archunit-junit5", version = "1.3.0" } assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" } bartholdy = { module = "de.sormuras:bartholdy", version = "0.2.3" } bndlib = { module = "biz.aQute.bnd:biz.aQute.bndlib", version.ref = "bnd" } checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" } -classgraph = { module = "io.github.classgraph:classgraph", version = "4.8.161" } -commons-io = { module = "commons-io:commons-io", version = "2.13.0" } -gradle-commonCustomUserData = { module = "com.gradle:common-custom-user-data-gradle-plugin", version = "1.11" } -gradle-foojayResolver = { module = "org.gradle.toolchains:foojay-resolver", version = "0.6.0" } -gradle-enterprise = { module = "com.gradle:gradle-enterprise-gradle-plugin", version = "3.14" } -gradle-bnd = { module = "biz.aQute.bnd:biz.aQute.bnd.gradle", version.ref = "bnd" } -gradle-shadow = { module = "com.github.johnrengelman:shadow", version = "8.1.1" } -gradle-spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version = "6.19.0" } -gradle-versions = { module = "com.github.ben-manes:gradle-versions-plugin", version.ref = "gradleVersionsPlugin" } -groovy4 = { module = "org.apache.groovy:groovy", version = "4.0.13" } +classgraph = { module = "io.github.classgraph:classgraph", version = "4.8.174" } +commons-io = { module = "commons-io:commons-io", version = "2.16.1" } +groovy4 = { module = "org.apache.groovy:groovy", version = "4.0.22" } groovy2-bom = { module = "org.codehaus.groovy:groovy-bom", version = "2.5.21" } -hamcrest = { module = "org.hamcrest:hamcrest", version = "2.2" } +hamcrest = { module = "org.hamcrest:hamcrest", version = "3.0" } +jackson-dataformat-yaml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml", version.ref = "jackson" } +jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" } +jfrPolyfill = { module = "org.gradle.jfr.polyfill:jfr-polyfill", version = "1.0.2" } jfrunit = { module = "org.moditect.jfrunit:jfrunit-core", version = "1.0.0.Alpha2" } jimfs = { module = "com.google.jimfs:jimfs", version = "1.3.0" } jmh-core = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh" } jmh-generator-annprocess = { module = "org.openjdk.jmh:jmh-generator-annprocess", version.ref = "jmh" } -joox = { module = "org.jooq:joox", version = "2.0.0" } +joox = { module = "org.jooq:joox", version = "2.0.1" } +jte = { module = "gg.jte:jte", version = "3.1.12" } junit4 = { module = "junit:junit", version = { require = "[4.12,)", prefer = "4.13.2" } } -kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.7.2" } +kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.8.1" } log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" } log4j-jul = { module = "org.apache.logging.log4j:log4j-jul", version.ref = "log4j" } -maven = { module = "org.apache.maven:apache-maven", version = "3.9.3" } +maven = { module = "org.apache.maven:apache-maven", version = "3.9.8" } mavenSurefirePlugin = { module = "org.apache.maven.plugins:maven-surefire-plugin", version.ref = "surefire" } -memoryfilesystem = { module = "com.github.marschall:memoryfilesystem", version = "2.6.1" } -mockito = { module = "org.mockito:mockito-junit-jupiter", version = "5.4.0" } +memoryfilesystem = { module = "com.github.marschall:memoryfilesystem", version = "2.8.0" } +mockito = { module = "org.mockito:mockito-junit-jupiter", version = "5.12.0" } opentest4j = { module = "org.opentest4j:opentest4j", version.ref = "opentest4j" } openTestReporting-events = { module = "org.opentest4j.reporting:open-test-reporting-events", version.ref = "openTestReporting" } openTestReporting-tooling = { module = "org.opentest4j.reporting:open-test-reporting-tooling", version.ref = "openTestReporting" } -picocli = { module = "info.picocli:picocli", version = "4.7.4" } -slf4j-julBinding = { module = "org.slf4j:slf4j-jdk14", version = "2.0.7" } +picocli = { module = "info.picocli:picocli", version = "4.7.6" } +slf4j-julBinding = { module = "org.slf4j:slf4j-jdk14", version = "2.0.13" } spock1 = { module = "org.spockframework:spock-core", version = "1.3-groovy-2.5" } univocity-parsers = { module = "com.univocity:univocity-parsers", version = "2.9.1" } xmlunit-assertj = { module = "org.xmlunit:xmlunit-assertj3", version.ref = "xmlunit" } xmlunit-placeholders = { module = "org.xmlunit:xmlunit-placeholders", version.ref = "xmlunit" } -testingAnnotations = { module = "com.gradle:gradle-enterprise-testing-annotations", version = "1.1" } +testingAnnotations = { module = "com.gradle:develocity-testing-annotations", version = "2.0.1" } + +# Only declared here so Dependabot knows when to update the referenced versions +asciidoctorj-pdf = { module = "org.asciidoctor:asciidoctorj-pdf", version.ref = "asciidoctorj-pdf" } +eclipse-platform = { module = "org.eclipse.platform:org.eclipse.platform", version.ref = "eclipse" } +jacoco = { module = "org.jacoco:jacoco", version.ref = "jacoco" } +junit4-latest = { module = "junit:junit", version.ref = "junit4" } +junit4-bundle = { module = "org.apache.servicemix.bundles:org.apache.servicemix.bundles.junit", version = "4.13.2_1" } +ktlint-cli = { module = "com.pinterest.ktlint:ktlint-cli", version.ref = "ktlint" } [bundles] ant = ["ant", "ant-junit", "ant-junitlauncher"] @@ -73,9 +78,16 @@ xmlunit = ["xmlunit-assertj", "xmlunit-placeholders"] [plugins] asciidoctorConvert = { id = "org.asciidoctor.jvm.convert", version.ref = "asciidoctor-plugins" } asciidoctorPdf = { id = "org.asciidoctor.jvm.pdf", version.ref = "asciidoctor-plugins" } -buildParameters = { id = "org.gradlex.build-parameters", version = "1.4.3" } -gitPublish = { id = "org.ajoberstar.git-publish", version = "4.2.0" } -jmh = { id = "me.champeau.jmh", version = "0.7.1" } +bnd = { id = "biz.aQute.bnd", version.ref = "bnd" } +buildParameters = { id = "org.gradlex.build-parameters", version = "1.4.4" } +commonCustomUserData = { id = "com.gradle.common-custom-user-data-gradle-plugin", version = "2.0.2" } +develocity = { id = "com.gradle.develocity", version = "3.17.6" } +foojayResolver = { id = "org.gradle.toolchains.foojay-resolver", version = "0.8.0" } +gitPublish = { id = "org.ajoberstar.git-publish", version = "4.2.2" } +jmh = { id = "me.champeau.jmh", version = "0.7.2" } +nexusPublish = { id = "io.github.gradle-nexus.publish-plugin", version = "2.0.0" } nohttp = { id = "io.spring.nohttp", version = "0.0.11" } -nexusPublish = { id = "io.github.gradle-nexus.publish-plugin", version = "2.0.0-rc-1" } -versions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersionsPlugin" } +plantuml = { id = "io.freefair.plantuml", version = "8.6" } +shadow = { id = "com.gradleup.shadow", version = "8.3.0" } +spotless = { id = "com.diffplug.spotless", version = "7.0.0.BETA1" } +versions = { id = "com.github.ben-manes.versions", version = "0.51.0" } diff --git a/gradle/plugins/build-parameters/build.gradle.kts b/gradle/plugins/build-parameters/build.gradle.kts index f70f2d8bc062..c9c2fa31ff0a 100644 --- a/gradle/plugins/build-parameters/build.gradle.kts +++ b/gradle/plugins/build-parameters/build.gradle.kts @@ -11,21 +11,13 @@ buildParameters { defaultValue = false fromEnvironment() } - integer("javaToolchainVersion") { - description = "Defines the Java toolchain version to use for compiling code" - } - group("buildCache") { - string("username") { - description = "Username to authenticate with the remote build cache" - fromEnvironment() - } - string("password") { - description = "Password to authenticate with the remote build cache" - fromEnvironment() + group("javaToolchain") { + description = "Parameters controlling the Java toolchain used for compiling code and running tests" + integer("version") { + description = "JDK version" } - string("url") { - description = "URL to the remote build cache" - fromEnvironment() + string("implementation") { + description = "JDK implementation (for example, 'j9')" } } group("documentation") { @@ -35,26 +27,34 @@ buildParameters { defaultValue = false } } - group("enterprise") { - description = "Parameters controlling Gradle Enterprise features" - group("predictiveTestSelection") { - bool("enabled") { - description = "Whether or not to use Predictive Test Selection for selecting tests to execute" - defaultValue = true + group("junit") { + group("develocity") { + description = "Parameters controlling Develocity features" + group("buildCache") { + string("server") { + description = + "Remote build cache server address (protocol and hostname), e.g. https://eu-build-cache-ge.junit.org" + } } - } - group("testDistribution") { - bool("enabled") { - description = "Whether or not to use Test Distribution for executing tests" - defaultValue = false - fromEnvironment() + group("predictiveTestSelection") { + bool("enabled") { + description = "Whether or not to use Predictive Test Selection for selecting tests to execute" + defaultValue = true + } } - integer("maxLocalExecutors") { - description = "How many local executors to use for executing tests" - defaultValue = 1 - } - integer("maxRemoteExecutors") { - description = "How many remote executors to request for executing tests" + group("testDistribution") { + bool("enabled") { + description = "Whether or not to use Test Distribution for executing tests" + defaultValue = false + fromEnvironment() + } + integer("maxLocalExecutors") { + description = "How many local executors to use for executing tests" + defaultValue = 1 + } + integer("maxRemoteExecutors") { + description = "How many remote executors to request for executing tests" + } } } } @@ -72,4 +72,21 @@ buildParameters { description = "Configures the number of times failing test are retried" } } + group("publishing") { + bool("signArtifacts") { + description = "Sign artifacts before publishing them to Maven repos" + } + } + group("manifest") { + string("buildTimestamp") { + description = "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." + fromEnvironment("SOURCE_DATE_EPOCH") // see https://reproducible-builds.org/docs/source-date-epoch/ + } + string("builtBy") { + description = "Overrides the value of the 'Built-By' jar manifest entry" + } + string("createdBy") { + description = "Overrides the value of the 'Created-By' jar manifest entry" + } + } } diff --git a/gradle/plugins/code-generator/build.gradle.kts b/gradle/plugins/code-generator/build.gradle.kts new file mode 100644 index 000000000000..d317808a7142 --- /dev/null +++ b/gradle/plugins/code-generator/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + `kotlin-dsl` +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation("junitbuild.base:code-generator-model") + implementation(projects.common) + implementation(libs.jackson.dataformat.yaml) + implementation(libs.jackson.module.kotlin) + implementation(libs.jte) +} diff --git a/gradle/plugins/code-generator/src/main/kotlin/junitbuild.code-generator.gradle.kts b/gradle/plugins/code-generator/src/main/kotlin/junitbuild.code-generator.gradle.kts new file mode 100644 index 000000000000..59d9fa36ff43 --- /dev/null +++ b/gradle/plugins/code-generator/src/main/kotlin/junitbuild.code-generator.gradle.kts @@ -0,0 +1,35 @@ +import junitbuild.generator.GenerateJreRelatedSourceCode + +plugins { + java +} + +val templates by sourceSets.registering +dependencies { + add(templates.get().compileOnlyConfigurationName, dependencyFromLibs("jte")) + add(templates.get().compileOnlyConfigurationName, "junitbuild.base:code-generator-model") +} + +val license: License by rootProject.extra +val rootTargetDir = layout.buildDirectory.dir("generated/sources/jte") +val generateCode by tasks.registering + +sourceSets.named { it != templates.name }.configureEach { + + val sourceSetName = name + val sourceSetTargetDir = rootTargetDir.map { it.dir(sourceSetName) } + + val task = tasks.register(getTaskName("generateJreRelated", "SourceCode"), GenerateJreRelatedSourceCode::class) { + templateDir.convention(layout.dir(templates.map { + it.resources.srcDirs.single().resolve(sourceSetName) + })) + targetDir.convention(sourceSetTargetDir) + licenseHeaderFile.convention(license.headerFile) + } + + java.srcDir(files(sourceSetTargetDir).builtBy(task)) + + generateCode { + dependsOn(task) + } +} diff --git a/gradle/plugins/code-generator/src/main/kotlin/junitbuild/generator/GenerateJreRelatedSourceCode.kt b/gradle/plugins/code-generator/src/main/kotlin/junitbuild/generator/GenerateJreRelatedSourceCode.kt new file mode 100644 index 000000000000..b76ad39345d6 --- /dev/null +++ b/gradle/plugins/code-generator/src/main/kotlin/junitbuild/generator/GenerateJreRelatedSourceCode.kt @@ -0,0 +1,77 @@ +package junitbuild.generator + +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.module.kotlin.KotlinModule +import gg.jte.ContentType +import gg.jte.TemplateEngine +import gg.jte.output.FileOutput +import gg.jte.resolve.DirectoryCodeResolver +import junitbuild.generator.model.JRE +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.SkipWhenEmpty +import org.gradle.api.tasks.TaskAction + +@CacheableTask +abstract class GenerateJreRelatedSourceCode : DefaultTask() { + + @get:InputDirectory + @get:SkipWhenEmpty + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val templateDir: DirectoryProperty + + @get:OutputDirectory + abstract val targetDir: DirectoryProperty + + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + abstract val licenseHeaderFile: RegularFileProperty + + @TaskAction + fun generateSourceCode() { + val mainTargetDir = targetDir.get().asFile + mainTargetDir.deleteRecursively() + + val templateDir = templateDir.get().asFile + val codeResolver = DirectoryCodeResolver(templateDir.toPath()) + val templateEngine = + TemplateEngine.create(codeResolver, temporaryDir.toPath(), ContentType.Plain, javaClass.classLoader) + + val templates = templateDir.walkTopDown() + .filter { it.extension == "jte" } + .map { it.relativeTo(templateDir) } + .toList() + + if (templates.isNotEmpty()) { + val jres = javaClass.getResourceAsStream("/jre.yaml").use { input -> + val mapper = ObjectMapper(YAMLFactory()) + mapper.registerModule(KotlinModule.Builder().build()) + mapper.readValue(input, object : TypeReference>() {}) + } + val params = mapOf( + "jres" to jres, + "jresSortedByStringValue" to jres.sortedBy { it.version.toString() }, + "licenseHeader" to licenseHeaderFile.asFile.get().readText() + ) + templates.forEach { + val targetFile = mainTargetDir.toPath().resolve(it.resolveSibling(it.nameWithoutExtension).path) + + FileOutput(targetFile).use { output -> + // JTE does not support Windows paths, so we need to replace them + val safePath = it.path.replace('\\', '/') + templateEngine.render(safePath, params, output) + } + } + } + } + +} diff --git a/gradle/plugins/common/build.gradle.kts b/gradle/plugins/common/build.gradle.kts index 0753c6a3099e..0a93c96c0fee 100644 --- a/gradle/plugins/common/build.gradle.kts +++ b/gradle/plugins/common/build.gradle.kts @@ -9,11 +9,16 @@ repositories { dependencies { implementation(projects.buildParameters) implementation(kotlin("gradle-plugin")) - implementation(libs.gradle.bnd) - implementation(libs.gradle.commonCustomUserData) - implementation(libs.gradle.enterprise) - implementation(libs.gradle.foojayResolver) - implementation(libs.gradle.shadow) - implementation(libs.gradle.spotless) - implementation(libs.gradle.versions) + implementation(libs.plugins.bnd.markerCoordinates) + implementation(libs.plugins.commonCustomUserData.markerCoordinates) + implementation(libs.plugins.develocity.markerCoordinates) + implementation(libs.plugins.foojayResolver.markerCoordinates) + implementation(libs.plugins.jmh.markerCoordinates) + implementation(libs.plugins.shadow.markerCoordinates) + implementation(libs.plugins.spotless.markerCoordinates) + implementation(libs.plugins.versions.markerCoordinates) } + +// see https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_markers +val Provider.markerCoordinates: Provider + get() = map { "${it.pluginId}:${it.pluginId}.gradle.plugin:${it.version}" } diff --git a/gradle/plugins/common/src/main/kotlin/ConfigurationContainerExtensions.kt b/gradle/plugins/common/src/main/kotlin/ConfigurationContainerExtensions.kt deleted file mode 100644 index ebe7399e9f15..000000000000 --- a/gradle/plugins/common/src/main/kotlin/ConfigurationContainerExtensions.kt +++ /dev/null @@ -1,14 +0,0 @@ - -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ConfigurationContainer -import org.gradle.kotlin.dsl.NamedDomainObjectContainerCreatingDelegateProvider - -val ConfigurationContainer.creatingResolvable - get() = creatingResolvable {} - -fun ConfigurationContainer.creatingResolvable(configuration: Configuration.() -> Unit) = - NamedDomainObjectContainerCreatingDelegateProvider.of(this) { - isCanBeResolved = true - isCanBeConsumed = false - configuration() - } diff --git a/gradle/plugins/common/src/main/kotlin/JavaLibraryExtension.kt b/gradle/plugins/common/src/main/kotlin/JavaLibraryExtension.kt index 834374bf06c6..64a61da7fb7b 100644 --- a/gradle/plugins/common/src/main/kotlin/JavaLibraryExtension.kt +++ b/gradle/plugins/common/src/main/kotlin/JavaLibraryExtension.kt @@ -2,6 +2,6 @@ import org.gradle.api.JavaVersion open class JavaLibraryExtension { var mainJavaVersion: JavaVersion = JavaVersion.VERSION_1_8 - var testJavaVersion: JavaVersion = JavaVersion.VERSION_17 + var testJavaVersion: JavaVersion = JavaVersion.VERSION_21 var configureRelease: Boolean = true } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.build-metadata.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.build-metadata.gradle.kts index ad58b4898de5..b2795849a49c 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.build-metadata.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.build-metadata.gradle.kts @@ -2,27 +2,33 @@ import java.time.Instant import java.time.OffsetDateTime import java.time.ZoneOffset import java.time.format.DateTimeFormatter +import java.time.format.DateTimeFormatterBuilder -val buildTimeAndDate = - if (System.getenv().containsKey("SOURCE_DATE_EPOCH")) { - - // SOURCE_DATE_EPOCH is a UNIX timestamp for pinning build metadata against - // in order to achieve reproducible builds - // - // More details - https://reproducible-builds.org/docs/source-date-epoch/ - val sourceDateEpoch = System.getenv("SOURCE_DATE_EPOCH").toLong() +plugins { + id("junitbuild.build-parameters") +} - Instant.ofEpochSecond(sourceDateEpoch).atOffset(ZoneOffset.UTC) +val dateFormatter = DateTimeFormatter.ISO_LOCAL_DATE +val timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSSZ") - } else { - OffsetDateTime.now() +val buildTimeAndDate = buildParameters.manifest.buildTimestamp + .map { + it.toLongOrNull() + ?.let { s -> Instant.ofEpochSecond(s).atOffset(ZoneOffset.UTC) } + ?: DateTimeFormatterBuilder() + .append(dateFormatter) + .appendLiteral(' ') + .append(timeFormatter) + .toFormatter() + .parse(it) } + .orNull + ?: OffsetDateTime.now() -val buildDate: String by extra { DateTimeFormatter.ISO_LOCAL_DATE.format(buildTimeAndDate) } -val buildTime: String by extra { DateTimeFormatter.ofPattern("HH:mm:ss.SSSZ").format(buildTimeAndDate) } +val buildDate: String by extra { dateFormatter.format(buildTimeAndDate) } +val buildTime: String by extra { timeFormatter.format(buildTimeAndDate) } val buildRevision: String by extra { providers.exec { commandLine("git", "rev-parse", "--verify", "HEAD") - }.standardOutput.asText.get() + }.standardOutput.asText.get().trim() } -val builtByValue by extra { project.findProperty("builtBy") ?: project.property("defaultBuiltBy") } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-aggregation-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-aggregation-conventions.gradle.kts index 91008c81b184..17a613090463 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-aggregation-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-aggregation-conventions.gradle.kts @@ -3,10 +3,37 @@ plugins { `jacoco-report-aggregation` } -reporting { - reports { - create("jacocoRootReport") { - testType = TestSuiteType.UNIT_TEST +val jacocoRootReport by reporting.reports.creating(JacocoCoverageReport::class) { + testType = TestSuiteType.UNIT_TEST +} + +val classesView = configurations["aggregateCodeCoverageReportResults"].incoming.artifactView { + withVariantReselection() // Required to ensure the transformed classes are selected + componentFilter { it is ProjectComponentIdentifier } + attributes { + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements::class, LibraryElements.CLASSES)) + } +} + +tasks { + val reportTask = named(jacocoRootReport.reportTask.name) + val jacocoRootCoverageVerification by registering(JacocoCoverageVerification::class) { + enabled = !buildParameters.junit.develocity.predictiveTestSelection.enabled + executionData.from(reportTask.map { it.executionData }) + classDirectories.from(reportTask.map { it.classDirectories }) + sourceDirectories.from(reportTask.map { it.sourceDirectories }) + violationRules { + rule { + limit { + // In order to detect problems with coverage aggregation, we require a minimum coverage percentage + minimum = "0.90".toBigDecimal() + } + } } } + reportTask { + // Override to restore behavior of pre-8.2.1 Gradle (see https://github.com/gradle/gradle/issues/25618) + classDirectories.setFrom(classesView.files) + finalizedBy(jacocoRootCoverageVerification) + } } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-java-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-java-conventions.gradle.kts index 804de8388562..5e5f94669204 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-java-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-java-conventions.gradle.kts @@ -22,10 +22,7 @@ val codeCoverageClassesJar by tasks.registering(Jar::class) { duplicatesStrategy = DuplicatesStrategy.INCLUDE } -configurations.create("codeCoverageReportClasses") { - isCanBeResolved = false - isCanBeConsumed = true - isTransitive = false +configurations.consumable("codeCoverageReportClasses") { attributes.attribute(LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements::class, CLASSES)) outgoing.artifact(codeCoverageClassesJar) } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts index 6eb7eb308ec8..08e435be8996 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts @@ -2,6 +2,9 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import junitbuild.java.ModuleCompileOptions import junitbuild.java.ModulePathArgumentProvider import junitbuild.java.PatchModuleArgumentProvider +import org.gradle.plugins.ide.eclipse.model.Classpath +import org.gradle.plugins.ide.eclipse.model.Library +import org.gradle.plugins.ide.eclipse.model.ProjectDependency plugins { `java-library` @@ -9,6 +12,7 @@ plugins { idea checkstyle id("junitbuild.base-conventions") + id("junitbuild.build-parameters") id("junitbuild.jacoco-java-conventions") } @@ -17,14 +21,12 @@ val modularProjects: List by rootProject.extra val buildDate: String by rootProject.extra val buildTime: String by rootProject.extra val buildRevision: Any by rootProject.extra -val builtByValue: String by rootProject.extra val extension = extensions.create("javaLibrary") val moduleSourceDir = layout.projectDirectory.dir("src/module/$javaModuleName") val combinedModuleSourceDir = layout.buildDirectory.dir("module") val moduleOutputDir = layout.buildDirectory.dir("classes/java/module") -val javaVersion = JavaVersion.current() eclipse { jdt { @@ -36,6 +38,16 @@ eclipse { } } } + classpath.file.whenMerged { + this as Classpath + // Remove classpath entries for non-existent libraries added by various + // plugins, such as "junit-jupiter-api/build/classes/kotlin/testFixtures". + entries.removeIf { it is Library && !file(it.path).exists() } + // Remove classpath entries for the code generator model used by the + // Java Template Engine (JTE) which is used to generate the JRE enum and + // dependent tests. + entries.removeIf { it is ProjectDependency && it.path.equals("/code-generator-model") } + } } java { @@ -122,8 +134,12 @@ if (project in mavenizedProjects) { tasks.withType().configureEach { isPreserveFileTimestamps = false isReproducibleFileOrder = true - dirMode = Integer.parseInt("0755", 8) - fileMode = Integer.parseInt("0644", 8) + dirPermissions { + unix("rwxr-xr-x") + } + filePermissions { + unix("rw-r--r--") + } } normalization { @@ -146,15 +162,15 @@ val allMainClasses by tasks.registering { } val prepareModuleSourceDir by tasks.registering(Sync::class) { - from(moduleSourceDir) - from(sourceSets.matching { it.name.startsWith("main") }.map { it.allJava }) - into(combinedModuleSourceDir.map { it.dir(javaModuleName) }) - duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from(moduleSourceDir) + from(sourceSets.named { it.startsWith("main") }.map { it.allJava }) + into(combinedModuleSourceDir.map { it.dir(javaModuleName) }) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } val compileModule by tasks.registering(JavaCompile::class) { dependsOn(allMainClasses) - enabled = project in modularProjects + enabled = project in modularProjects source = fileTree(combinedModuleSourceDir).builtBy(prepareModuleSourceDir) destinationDirectory = moduleOutputDir sourceCompatibility = "9" @@ -168,22 +184,22 @@ val compileModule by tasks.registering(JavaCompile::class) { "--module-version", "${project.version}", )) - val moduleOptions = objects.newInstance(ModuleCompileOptions::class) - extensions.add("moduleOptions", moduleOptions) - moduleOptions.modulePath.from(configurations.compileClasspath) + val moduleOptions = objects.newInstance(ModuleCompileOptions::class) + extensions.add("moduleOptions", moduleOptions) + moduleOptions.modulePath.from(configurations.compileClasspath) options.compilerArgumentProviders.add(objects.newInstance(ModulePathArgumentProvider::class, project, combinedModuleSourceDir, modularProjects).apply { - modulePath.from(moduleOptions.modulePath) - }) + modulePath.from(moduleOptions.modulePath) + }) options.compilerArgumentProviders.addAll(modularProjects.map { objects.newInstance(PatchModuleArgumentProvider::class, project, it) }) modularity.inferModulePath = false - doFirst { - options.allCompilerArgs.forEach { - logger.info(it) - } - } + doFirst { + options.allCompilerArgs.forEach { + logger.info(it) + } + } } tasks.withType().configureEach { @@ -203,8 +219,9 @@ tasks.withType().configureEach { tasks.jar { manifest { attributes( - "Created-By" to "${System.getProperty("java.version")} (${System.getProperty("java.vendor")} ${System.getProperty("java.vm.version")})", - "Built-By" to builtByValue, + "Created-By" to (buildParameters.manifest.createdBy.orNull + ?: "${System.getProperty("java.version")} (${System.getProperty("java.vendor")} ${System.getProperty("java.vm.version")})"), + "Built-By" to buildParameters.manifest.builtBy.orElse("JUnit Team"), "Build-Date" to buildDate, "Build-Time" to buildTime, "Build-Revision" to buildRevision, @@ -230,7 +247,9 @@ tasks.compileJava { // See: https://docs.oracle.com/en/java/javase/12/tools/javac.html options.compilerArgs.addAll(listOf( "-Xlint:all", // Enables all recommended warnings. - "-Werror" // Terminates compilation when warnings occur. + "-Werror", // Terminates compilation when warnings occur. + // Required for compatibility with Java 8's reflection APIs (see https://github.com/junit-team/junit5/issues/3797). + "-parameters", // Generates metadata for reflection on method parameters. )) } @@ -296,16 +315,16 @@ checkstyle { tasks { checkstyleMain { - config = resources.text.fromFile(checkstyle.configDirectory.file("checkstyleMain.xml")) + config = resources.text.fromFile(checkstyle.configDirectory.file("checkstyleMain.xml")) } checkstyleTest { - config = resources.text.fromFile(checkstyle.configDirectory.file("checkstyleTest.xml")) + config = resources.text.fromFile(checkstyle.configDirectory.file("checkstyleTest.xml")) } } pluginManager.withPlugin("java-test-fixtures") { tasks.named("checkstyleTestFixtures") { - config = resources.text.fromFile(checkstyle.configDirectory.file("checkstyleTest.xml")) + config = resources.text.fromFile(checkstyle.configDirectory.file("checkstyleTest.xml")) } tasks.named("compileTestFixturesJava") { options.release = extension.testJavaVersion.majorVersion.toInt() diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.java-multi-release-sources.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.java-multi-release-sources.gradle.kts index d1b05c6e00ce..83b2a8e8e273 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.java-multi-release-sources.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.java-multi-release-sources.gradle.kts @@ -1,4 +1,4 @@ -import org.gradle.configurationcache.extensions.capitalized +import junitbuild.extensions.capitalized plugins { id("junitbuild.java-library-conventions") @@ -30,7 +30,7 @@ listOf(9, 17).forEach { javaVersion -> } named("checkstyle${sourceSet.name.capitalized()}").configure { - config = resources.text.fromFile(checkstyle.configDirectory.file("checkstyleMain.xml")) + config = resources.text.fromFile(checkstyle.configDirectory.file("checkstyleMain.xml")) } if (project in mavenizedProjects) { diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.java-multi-release-test-sources.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.java-multi-release-test-sources.gradle.kts new file mode 100644 index 000000000000..4dd191dbe0b0 --- /dev/null +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.java-multi-release-test-sources.gradle.kts @@ -0,0 +1,42 @@ +import junitbuild.extensions.capitalized + +plugins { + id("junitbuild.java-library-conventions") +} + +listOf(21).forEach { javaVersion -> + val sourceSet = sourceSets.register("testRelease${javaVersion}") { + compileClasspath += sourceSets.main.get().output + runtimeClasspath += sourceSets.main.get().output + java { + setSrcDirs(setOf("src/test/java${javaVersion}")) + } + } + + configurations { + named(sourceSet.get().compileClasspathConfigurationName).configure { + extendsFrom(configurations.testCompileClasspath.get()) + } + named(sourceSet.get().runtimeClasspathConfigurationName).configure { + extendsFrom(configurations.testRuntimeClasspath.get()) + } + } + + tasks { + val testTask = register("testRelease${javaVersion}") { + group = "verification" + description = "Runs the tests for Java ${javaVersion}" + testClassesDirs = sourceSet.get().output.classesDirs + classpath = sourceSet.get().runtimeClasspath + } + check { + dependsOn(testTask) + } + named(sourceSet.get().compileJavaTaskName).configure { + options.release = javaVersion + } + named("checkstyle${sourceSet.name.capitalized()}").configure { + config = resources.text.fromFile(checkstyle.configDirectory.file("checkstyleTest.xml")) + } + } +} diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.java-repackage-jars.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.java-repackage-jars.gradle.kts deleted file mode 100644 index a000989b0327..000000000000 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.java-repackage-jars.gradle.kts +++ /dev/null @@ -1,52 +0,0 @@ -import java.util.jar.JarEntry -import java.util.jar.JarFile -import java.util.jar.JarOutputStream -import org.gradle.api.internal.file.archive.ZipCopyAction -import java.nio.file.Files - -// This registers a `doLast` action to rewrite the timestamps of the project's output JAR -afterEvaluate { - val jarTask = (tasks.findByName("shadowJar") ?: tasks["jar"]) as Jar - - jarTask.doLast { - - val newFile = Files.createTempFile("rewrite-timestamp", null).toFile() - val originalOutput = jarTask.archiveFile.get().asFile - - newFile.outputStream().use { os -> - - val newJarStream = JarOutputStream(os) - val oldJar = JarFile(originalOutput) - - fun sortAlwaysFirst(name: String): Comparator = - Comparator { a, b -> - when { - a.name == name -> -1 - b.name == name -> 1 - else -> 0 - } - } - - oldJar.entries() - .toList() - .distinctBy { it.name } - .sortedWith(sortAlwaysFirst("META-INF/") - .then(sortAlwaysFirst("META-INF/MANIFEST.MF")) - .thenBy { it.name }) - .forEach { entry -> - val jarEntry = JarEntry(entry.name) - - // Use the same constant as the fixed timestamps in normal copy actions - jarEntry.time = ZipCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES - - newJarStream.putNextEntry(jarEntry) - - oldJar.getInputStream(entry).copyTo(newJarStream) - } - - newJarStream.finish() - } - - newFile.renameTo(originalOutput) - } -} diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.java-toolchain-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.java-toolchain-conventions.gradle.kts index 68735c0c9168..c6b7e0120359 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.java-toolchain-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.java-toolchain-conventions.gradle.kts @@ -5,13 +5,22 @@ plugins { } project.pluginManager.withPlugin("java") { - val defaultLanguageVersion = JavaLanguageVersion.of(17) - val javaLanguageVersion = buildParameters.javaToolchainVersion.map { JavaLanguageVersion.of(it) }.getOrElse(defaultLanguageVersion) + val defaultLanguageVersion = JavaLanguageVersion.of(21) + val javaLanguageVersion = buildParameters.javaToolchain.version.map { JavaLanguageVersion.of(it) }.getOrElse(defaultLanguageVersion) + val jvmImplementation = buildParameters.javaToolchain.implementation.map { + when(it) { + "j9" -> JvmImplementation.J9 + else -> throw InvalidUserDataException("Unsupported JDK implementation: $it") + } + }.getOrElse(JvmImplementation.VENDOR_SPECIFIC) val extension = the() val javaToolchainService = the() - extension.toolchain.languageVersion = javaLanguageVersion + extension.toolchain { + languageVersion = javaLanguageVersion + implementation = jvmImplementation + } pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { configure { diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.jmh-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.jmh-conventions.gradle.kts new file mode 100644 index 000000000000..14da74a71014 --- /dev/null +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.jmh-conventions.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("me.champeau.jmh") +} + +jmh { + jmhVersion = requiredVersionFromLibs("jmh") +} + +dependencies { + jmh(dependencyFromLibs("jmh-core")) + jmhAnnotationProcessor(dependencyFromLibs("jmh-generator-annprocess")) +} + +tasks.jmhJar { + notCompatibleWithConfigurationCache("https://github.com/melix/jmh-gradle-plugin/issues/229") +} + +pluginManager.withPlugin("checkstyle") { + tasks.named("checkstyleJmh").configure { + // use same style rules as defined for tests + config = resources.text.fromFile(project.the().configDirectory.file("checkstyleTest.xml")) + } +} diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.junit4-compatibility.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.junit4-compatibility.gradle.kts index d0d2a89ad577..64b1074d345d 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.junit4-compatibility.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.junit4-compatibility.gradle.kts @@ -2,19 +2,22 @@ plugins { `java-library` } -val junit_4_12 by configurations.creatingResolvable { +val junit_4_12 = configurations.dependencyScope("junit_4_12") +val junit_4_12_classpath = configurations.resolvable("junit_4_12_classpath") { extendsFrom(configurations.testRuntimeClasspath.get()) + extendsFrom(junit_4_12.get()) } dependencies { - junit_4_12("junit:junit") { - version { - strictly("4.12") + constraints { + junit_4_12("junit:junit") { + version { + strictly("4.12") + } } } pluginManager.withPlugin("junitbuild.osgi-conventions") { - val junit4Osgi = requiredVersionFromLibs("junit4Osgi") - "osgiVerification"("org.apache.servicemix.bundles:org.apache.servicemix.bundles.junit:${junit4Osgi}") + "osgiVerification"(dependencyFromLibs("junit4-bundle")) } } @@ -22,7 +25,7 @@ tasks { val test_4_12 by registering(Test::class) { val test by testing.suites.existing(JvmTestSuite::class) testClassesDirs = files(test.map { it.sources.output.classesDirs }) - classpath = files(test.map { it.sources.runtimeClasspath }) + junit_4_12 + classpath = files(sourceSets.main.map { it.output }) + files(test.map { it.sources.output }) + junit_4_12_classpath.get() } check { dependsOn(test_4_12) diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.kotlin-library-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.kotlin-library-conventions.gradle.kts index bfb33224a709..0dcf7bf13282 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.kotlin-library-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.kotlin-library-conventions.gradle.kts @@ -5,11 +5,18 @@ plugins { kotlin("jvm") } +tasks.named("kotlinSourcesJar") { + enabled = false +} + tasks.withType().configureEach { kotlinOptions { - apiVersion = "1.3" - languageVersion = "1.3" + apiVersion = "1.6" + languageVersion = "1.6" allWarningsAsErrors = false + // Compiler arg is required for Kotlin 1.6 and below + // see https://kotlinlang.org/docs/whatsnew17.html#stable-opt-in-requirements + freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" } } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.osgi-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.osgi-conventions.gradle.kts index 9cd51cd4b56f..d1adaa09ff68 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.osgi-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.osgi-conventions.gradle.kts @@ -12,8 +12,8 @@ val projectDescription = objects.property().convention(provider { projec // This task enhances `jar` and `shadowJar` tasks with the bnd // `BundleTaskExtension` extension which allows for generating OSGi // metadata into the jar -tasks.withType().matching { task: Jar -> - task.name == "jar" || task.name == "shadowJar" +tasks.withType().named { + it == "jar" || it == "shadowJar" }.all { // configure tasks eagerly as workaround for https://github.com/bndtools/bnd/issues/5695 extra["importAPIGuardian"] = importAPIGuardian @@ -89,8 +89,10 @@ val osgiProperties by tasks.registering(WriteProperties::class) { property("-runblacklist", "org.apiguardian.api") } -val osgiVerification by configurations.creatingResolvable { +val osgiVerification = configurations.dependencyScope("osgiVerification") +val osgiVerificationClasspath = configurations.resolvable("osgiVerificationClasspath") { extendsFrom(configurations.runtimeClasspath.get()) + extendsFrom(osgiVerification.get()) } // Bnd's Resolve task is what verifies that a jar can be used in OSGi and @@ -107,9 +109,8 @@ val verifyOSGi by tasks.registering(Resolve::class) { // This adds jars defined in `osgiVerification` also so that bnd // can use them to validate the metadata without causing those to // end up in the dependencies of those projects. - bundles(osgiVerification) + bundles(osgiVerificationClasspath) properties.empty() - outputs.doNotCacheIf("https://github.com/bndtools/bnd/issues/5666") { true } } tasks.check { diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.publishing-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.publishing-conventions.gradle.kts index bab950933c2a..ad877035e1f3 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.publishing-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.publishing-conventions.gradle.kts @@ -40,17 +40,16 @@ tasks.withType().configureEach { dependsOn(tasks.build) } +val signArtifacts = buildParameters.publishing.signArtifacts.getOrElse(!(isSnapshot || buildParameters.ci)) + signing { useGpgCmd() sign(publishing.publications) - isRequired = !(isSnapshot || buildParameters.ci) + isRequired = signArtifacts } tasks.withType().configureEach { - val isSnapshot = project.version.toString().contains("SNAPSHOT") - onlyIf { - !isSnapshot // Gradle Module Metadata currently does not support signing snapshots - } + enabled = signArtifacts } publishing { diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.settings-conventions.settings.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.settings-conventions.settings.gradle.kts index 654e5b73502a..629385a63684 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.settings-conventions.settings.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.settings-conventions.settings.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradle.enterprise") + id("com.gradle.develocity") id("com.gradle.common-custom-user-data-gradle-plugin") id("org.gradle.toolchains.foojay-resolver-convention") } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.shadow-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.shadow-conventions.gradle.kts index 341eb6f826a3..ed1732e00e78 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.shadow-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.shadow-conventions.gradle.kts @@ -2,10 +2,13 @@ import junitbuild.java.ModuleCompileOptions plugins { id("junitbuild.java-library-conventions") - id("com.github.johnrengelman.shadow") + id("com.gradleup.shadow") } -val shadowed by configurations.creatingResolvable +val shadowed = configurations.dependencyScope("shadowed") +val shadowedClasspath = configurations.resolvable("shadowedClasspath") { + extendsFrom(shadowed.get()) +} configurations { listOf(apiElements, runtimeElements).forEach { @@ -22,34 +25,34 @@ configurations { sourceSets { main { - compileClasspath += shadowed + compileClasspath += shadowedClasspath.get() } test { - runtimeClasspath += shadowed + runtimeClasspath += shadowedClasspath.get() } } eclipse { classpath { - plusConfigurations.add(shadowed) + plusConfigurations.add(shadowedClasspath.get()) } } idea { module { - scopes["PROVIDED"]!!["plus"]!!.add(shadowed) + scopes["PROVIDED"]!!["plus"]!!.add(shadowedClasspath.get()) } } tasks { javadoc { - classpath += shadowed + classpath += shadowedClasspath.get() } checkstyleMain { - classpath += shadowed + classpath += shadowedClasspath.get() } shadowJar { - configurations = listOf(shadowed) + configurations = listOf(shadowedClasspath.get()) exclude("META-INF/maven/**") excludes.remove("module-info.class") archiveClassifier = "" @@ -68,7 +71,7 @@ tasks { classpath -= sourceSets.main.get().output classpath += files(shadowJar.map { it.archiveFile }) } - named("compileModule") { - the().modulePath.from(shadowed) - } + named("compileModule") { + the().modulePath.from(shadowedClasspath.get()) + } } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.spotless-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.spotless-conventions.gradle.kts index e078a3a50d1c..0e7090705073 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.spotless-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.spotless-conventions.gradle.kts @@ -1,7 +1,3 @@ -import com.diffplug.gradle.spotless.SpotlessApply -import com.diffplug.gradle.spotless.SpotlessCheck -import com.diffplug.spotless.LineEnding - plugins { id("com.diffplug.spotless") } @@ -11,8 +7,8 @@ val license: License by rootProject.extra spotless { format("misc") { - target("*.gradle.kts", "buildSrc/**/*.gradle.kts", "*.gitignore") - targetExclude("buildSrc/build/**") + target("*.gradle.kts", "gradle/plugins/**/*.gradle.kts", "*.gitignore") + targetExclude("gradle/plugins/**/build/**") indentWithTabs() trimTrailingWhitespace() endWithNewline() @@ -20,21 +16,22 @@ spotless { format("documentation") { target("*.adoc", "*.md", "src/**/*.adoc", "src/**/*.md") - targetExclude("**/build", "**/target") trimTrailingWhitespace() endWithNewline() } pluginManager.withPlugin("java") { - val configDir = rootProject.layout.projectDirectory.dir("gradle/config/eclipse") - val importOrderConfigFile = configDir.file("junit-eclipse.importorder") + val configDir = rootProject.layout.projectDirectory.dir("gradle/config/eclipse") + val importOrderConfigFile = configDir.file("junit-eclipse.importorder") val javaFormatterConfigFile = configDir.file("junit-eclipse-formatter-settings.xml") java { licenseHeaderFile(license.headerFile, "(package|import|open|module) ") importOrderFile(importOrderConfigFile) - eclipse().configFile(javaFormatterConfigFile) + val fullVersion = requiredVersionFromLibs("eclipse") + val majorMinorVersion = "([0-9]+\\.[0-9]+).*".toRegex().matchEntire(fullVersion)!!.let { it.groups[1]!!.value } + eclipse(majorMinorVersion).configFile(javaFormatterConfigFile) trimTrailingWhitespace() endWithNewline() } @@ -49,16 +46,13 @@ spotless { endWithNewline() } } - - // https://github.com/diffplug/spotless/issues/1644 - lineEndings = LineEnding.UNIX // or any other except GIT_ATTRIBUTES } tasks { - withType().configureEach { - notCompatibleWithConfigurationCache("https://github.com/diffplug/spotless/issues/644") + named("spotlessDocumentation") { + outputs.doNotCacheIf("negative avoidance savings") { true } } - withType().configureEach { - notCompatibleWithConfigurationCache("https://github.com/diffplug/spotless/issues/644") + named("spotlessMisc") { + outputs.doNotCacheIf("negative avoidance savings") { true } } } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.temp-maven-repo.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.temp-maven-repo.gradle.kts index fb06e7ec012c..f60def6c4df2 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.temp-maven-repo.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.temp-maven-repo.gradle.kts @@ -1,4 +1,4 @@ -import org.gradle.configurationcache.extensions.capitalized +import junitbuild.extensions.capitalized val tempRepoName by extra("temp") val tempRepoDir by extra { diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.testing-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.testing-conventions.gradle.kts index 086e1e6135af..95fcb06f8139 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.testing-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.testing-conventions.gradle.kts @@ -1,5 +1,4 @@ -import com.gradle.enterprise.gradleplugin.testretry.retry -import com.gradle.enterprise.gradleplugin.testselection.internal.PredictiveTestSelectionExtensionInternal +import com.gradle.develocity.agent.gradle.internal.test.PredictiveTestSelectionConfigurationInternal import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED import org.gradle.internal.os.OperatingSystem @@ -18,29 +17,33 @@ tasks.withType().configureEach { events = setOf(FAILED) exceptionFormat = FULL } - retry { - maxRetries = buildParameters.testing.retries.orElse(if (buildParameters.ci) 2 else 0) - } - distribution { - enabled.convention(buildParameters.enterprise.testDistribution.enabled && (!buildParameters.ci || System.getenv("GRADLE_ENTERPRISE_ACCESS_KEY").isNotBlank())) - maxLocalExecutors = buildParameters.enterprise.testDistribution.maxLocalExecutors - maxRemoteExecutors = buildParameters.enterprise.testDistribution.maxRemoteExecutors - if (buildParameters.ci) { - when { - OperatingSystem.current().isLinux -> requirements.add("os=linux") - OperatingSystem.current().isWindows -> requirements.add("os=windows") - OperatingSystem.current().isMacOsX -> requirements.add("os=macos") + develocity { + testRetry { + maxRetries = buildParameters.testing.retries.orElse(if (buildParameters.ci) 2 else 0) + } + testDistribution { + enabled.convention(buildParameters.junit.develocity.testDistribution.enabled && (!buildParameters.ci || !System.getenv("DEVELOCITY_ACCESS_KEY").isNullOrBlank())) + maxLocalExecutors = buildParameters.junit.develocity.testDistribution.maxLocalExecutors + maxRemoteExecutors = buildParameters.junit.develocity.testDistribution.maxRemoteExecutors + if (buildParameters.ci) { + when { + OperatingSystem.current().isLinux -> requirements.add("os=linux") + OperatingSystem.current().isWindows -> requirements.add("os=windows") + OperatingSystem.current().isMacOsX -> requirements.add("os=macos") + } } } - } - predictiveSelection { - enabled = buildParameters.enterprise.predictiveTestSelection.enabled + predictiveTestSelection { + enabled = buildParameters.junit.develocity.predictiveTestSelection.enabled - // Ensure PTS works when publishing Build Scans to scans.gradle.com - this as PredictiveTestSelectionExtensionInternal - server = uri("https://ge.junit.org") + // Ensure PTS works when publishing Build Scans to scans.gradle.com + this as PredictiveTestSelectionConfigurationInternal + server = uri("https://ge.junit.org") + } } systemProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager") + // Avoid overhead (see https://logging.apache.org/log4j/2.x/manual/jmx.html#enabling-jmx) + systemProperty("log4j2.disableJmx", "true") // Required until ASM officially supports the JDK 14 systemProperty("net.bytebuddy.experimental", true) if (buildParameters.testing.enableJFR) { @@ -73,16 +76,16 @@ dependencies { testImplementation(dependencyFromLibs("assertj")) testImplementation(dependencyFromLibs("mockito")) testImplementation(dependencyFromLibs("testingAnnotations")) - - if (!project.name.startsWith("junit-jupiter")) { - testImplementation(project(":junit-jupiter")) - } + testImplementation(project(":junit-jupiter")) testRuntimeOnly(project(":junit-platform-engine")) testRuntimeOnly(project(":junit-platform-jfr")) testRuntimeOnly(project(":junit-platform-reporting")) testRuntimeOnly(bundleFromLibs("log4j")) + testRuntimeOnly(dependencyFromLibs("jfrPolyfill")) { + because("OpenJ9 does not include JFR") + } testRuntimeOnly(dependencyFromLibs("openTestReporting-events")) { because("it's required to run tests via IntelliJ which does not consumed the shadowed jar of junit-platform-reporting") } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild/extensions/Extensions.kt b/gradle/plugins/common/src/main/kotlin/junitbuild/extensions/Extensions.kt new file mode 100644 index 000000000000..73cfbc4defae --- /dev/null +++ b/gradle/plugins/common/src/main/kotlin/junitbuild/extensions/Extensions.kt @@ -0,0 +1,7 @@ +package junitbuild.extensions + +import java.util.Locale + +fun String.capitalized() = replaceFirstChar { + it.uppercase(Locale.US) +} diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild/java/ExecJarAction.kt b/gradle/plugins/common/src/main/kotlin/junitbuild/java/ExecJarAction.kt deleted file mode 100644 index 8a3cf49ad70e..000000000000 --- a/gradle/plugins/common/src/main/kotlin/junitbuild/java/ExecJarAction.kt +++ /dev/null @@ -1,24 +0,0 @@ -package junitbuild.java - -import org.gradle.api.Action -import org.gradle.api.Task -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.jvm.toolchain.JavaLauncher -import org.gradle.process.ExecOperations -import javax.inject.Inject - -abstract class ExecJarAction @Inject constructor(private val operations: ExecOperations): Action { - - abstract val javaLauncher: Property - - abstract val args: ListProperty - - override fun execute(t: Task) { - operations.exec { - executable = javaLauncher.get() - .metadata.installationPath.file("bin/jar").asFile.absolutePath - args = this@ExecJarAction.args.get() - } - } -} diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild/java/PatchModuleArgumentProvider.kt b/gradle/plugins/common/src/main/kotlin/junitbuild/java/PatchModuleArgumentProvider.kt index 0c7f330f8adf..8149094e6128 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild/java/PatchModuleArgumentProvider.kt +++ b/gradle/plugins/common/src/main/kotlin/junitbuild/java/PatchModuleArgumentProvider.kt @@ -5,7 +5,12 @@ import org.gradle.api.Named import org.gradle.api.Project import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.provider.Property -import org.gradle.api.tasks.* +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.SourceSetContainer import org.gradle.kotlin.dsl.get import org.gradle.kotlin.dsl.the import org.gradle.process.CommandLineArgumentProvider @@ -28,7 +33,7 @@ abstract class PatchModuleArgumentProvider @Inject constructor(compiledProject: compiledProject.files(compiledProject.the().matching { it.name.startsWith("main") } .map { it.output }) else - patchModuleProject.files(patchModuleProject.the()["main"].java.srcDirs) + patchModuleProject.files(patchModuleProject.the()["main"].java.sourceDirectories) }) } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild/java/UpdateJarAction.kt b/gradle/plugins/common/src/main/kotlin/junitbuild/java/UpdateJarAction.kt new file mode 100644 index 000000000000..282a6aeaf469 --- /dev/null +++ b/gradle/plugins/common/src/main/kotlin/junitbuild/java/UpdateJarAction.kt @@ -0,0 +1,46 @@ +package junitbuild.java + +import org.gradle.api.Action +import org.gradle.api.Task +import org.gradle.api.internal.file.archive.ZipCopyAction +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.jvm.toolchain.JavaLauncher +import org.gradle.process.ExecOperations +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.ZoneOffset +import javax.inject.Inject + +abstract class UpdateJarAction @Inject constructor(private val operations: ExecOperations): Action { + + companion object { + // Since ZipCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES is in the default time zone (see its Javadoc), + // we're converting it to the same time in UTC here to make the jar reproducible regardless of the + // build's time zone. + private val CONSTANT_TIME_FOR_ZIP_ENTRIES = LocalDateTime.ofInstant(Instant.ofEpochMilli(ZipCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES), ZoneId.systemDefault()) + .toInstant(ZoneOffset.UTC) + .toString() + } + + abstract val javaLauncher: Property + + abstract val args: ListProperty + + init { + args.addAll( + "--update", + // Use a constant time to make the JAR reproducible. + "--date=$CONSTANT_TIME_FOR_ZIP_ENTRIES", + ) + } + + override fun execute(t: Task) { + operations.exec { + executable = javaLauncher.get() + .metadata.installationPath.file("bin/jar").asFile.absolutePath + args = this@UpdateJarAction.args.get() + } + } +} diff --git a/gradle/plugins/settings.gradle.kts b/gradle/plugins/settings.gradle.kts index 0b948c46d94f..728290a2cad6 100644 --- a/gradle/plugins/settings.gradle.kts +++ b/gradle/plugins/settings.gradle.kts @@ -1,9 +1,3 @@ -val expectedJavaVersion = JavaVersion.VERSION_17 -val actualJavaVersion = JavaVersion.current() -require(actualJavaVersion == expectedJavaVersion) { - "The JUnit 5 build must be executed with Java ${expectedJavaVersion.majorVersion}. Currently executing with Java ${actualJavaVersion.majorVersion}." -} - dependencyResolutionManagement { versionCatalogs { create("libs") { @@ -14,7 +8,10 @@ dependencyResolutionManagement { rootProject.name = "plugins" +includeBuild("../base") + include("build-parameters") include("common") +include("code-generator") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") diff --git a/gradle/scripts/checkBuildReproducibility.sh b/gradle/scripts/checkBuildReproducibility.sh index b07275624da1..c434fc778a44 100755 --- a/gradle/scripts/checkBuildReproducibility.sh +++ b/gradle/scripts/checkBuildReproducibility.sh @@ -7,13 +7,19 @@ export SOURCE_DATE_EPOCH=$(date +%s) function calculate_checksums() { OUTPUT=$1 - ./gradlew --no-build-cache clean assemble --parallel -Porg.gradle.java.installations.auto-download=false -Dscan.tag.Reproducibility + ./gradlew \ + --configuration-cache \ + --no-build-cache \ + -Porg.gradle.java.installations.auto-download=false \ + -Dscan.tag.Reproducibility \ + clean \ + assemble find . -name '*.jar' \ | grep '/build/libs/' \ | grep --invert-match 'javadoc' \ | sort \ - | xargs sha256sum > ${OUTPUT} + | xargs sha256sum > "${OUTPUT}" } diff --git a/gradle/scripts/publishDocumentationSnapshotOnlyIfNecessary.sh b/gradle/scripts/publishDocumentationSnapshotOnlyIfNecessary.sh deleted file mode 100755 index ecd0482a2368..000000000000 --- a/gradle/scripts/publishDocumentationSnapshotOnlyIfNecessary.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash - -readonly checksum_directory='documentation/build/checksum' -readonly current="${checksum_directory}/current-checksum.txt" -readonly published="${checksum_directory}/published-checksum.txt" -readonly github_pages_url='https://raw.githubusercontent.com/junit-team/junit5/gh-pages/docs/snapshot/published-checksum.txt' - -# -# always generate current sums -# -echo "Generating checksum file ${current}..." -mkdir --parents "${checksum_directory}" -md5sum documentation/documentation.gradle.kts > "${current}" -md5sum $(find documentation/src -type f) >> "${current}" -# skip module junit-bom because it doesn't contain relevant documentation -md5sum $(find junit-jupiter -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-jupiter-api -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-jupiter-engine -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-jupiter-migrationsupport -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-jupiter-params -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-platform-commons -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-platform-console -wholename '**/src/main/*.java') >> "${current}" -# skip module junit-platform-console-standalone because it doesn't contain relevant documentation -md5sum $(find junit-platform-engine -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-platform-jfr -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-platform-launcher -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-platform-reporting -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-platform-runner -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-platform-suite -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-platform-suite-api -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-platform-suite-commons -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-platform-suite-engine -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-platform-testkit -wholename '**/src/main/*.java') >> "${current}" -md5sum $(find junit-vintage-engine -wholename '**/src/main/*.java') >> "${current}" -# skip module platform-tests because it doesn't contain relevant documentation -# skip module platform-tooling-support-tests because it doesn't contain relevant documentation -sort --output "${current}" "${current}" -echo -md5sum "${current}" - -# -# compare current with published sums -# -curl --silent --output "${published}" "${github_pages_url}" -md5sum "${published}" -if cmp --silent "${current}" "${published}" ; then - # - # no changes detected: we're done - # - echo - echo "Already published documentation with same source checksum." - echo -else - # - # update checksum file and trigger new documentation build and upload - # - echo - echo "Creating and publishing documentation..." - echo - cp --force "${current}" "${published}" - ./gradlew gitPublishPush -Porg.gradle.java.installations.auto-download=false -Dscan.tag.Documentation -fi diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf4..2c3521197d7c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c2447881d454..68e8816d71c9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=03ec176d388f2aa99defcadc3ac6adf8dd2bce5145a129659537c0874dea5ad1 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index fcb6fca147c0..f5feea6d6b11 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -83,7 +85,9 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -201,11 +205,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 93e3f59f135d..9d21a21834d5 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/junit-bom/README.md b/junit-bom/README.md index 907a361c665a..e3c52f7f76d1 100644 --- a/junit-bom/README.md +++ b/junit-bom/README.md @@ -4,5 +4,5 @@ This module provides a Bill of Materials POM to ease dependency management using or [Gradle]. Please refer to the [User Guide] for details. [Maven]: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies -[Gradle]: https://docs.gradle.org/current/userguide/managing_transitive_dependencies.html#sec:bom_import +[Gradle]: https://docs.gradle.org/current/userguide/platforms.html#sub:bom_import [User Guide]: https://junit.org/junit5/docs/current/user-guide/#dependency-metadata-junit-bom diff --git a/junit-jupiter-api/junit-jupiter-api.gradle.kts b/junit-jupiter-api/junit-jupiter-api.gradle.kts index 7dd03a78f4e9..bdf4c359550f 100644 --- a/junit-jupiter-api/junit-jupiter-api.gradle.kts +++ b/junit-jupiter-api/junit-jupiter-api.gradle.kts @@ -1,5 +1,6 @@ plugins { id("junitbuild.kotlin-library-conventions") + id("junitbuild.code-generator") `java-test-fixtures` } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterAll.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterAll.java index 52743ebef416..7cbbfd7b0f15 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterAll.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterAll.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -29,33 +29,31 @@ * *

Method Signatures

* - *

{@code @AfterAll} methods must have a {@code void} return type and must be - * {@code static} by default. Consequently, {@code @AfterAll} methods are not - * supported in {@link Nested @Nested} test classes or as interface default - * methods unless the test class is annotated with - * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}. - * However, beginning with Java 16 {@code @AfterAll} methods may be declared as - * {@code static} in {@link Nested @Nested} test classes, and the - * {@code Lifecycle.PER_CLASS} restriction no longer applies. {@code @AfterAll} - * methods may optionally declare parameters to be resolved by + *

{@code @AfterAll} methods must have a {@code void} return type and must + * be {@code static} by default. Consequently, {@code @AfterAll} methods are + * not supported in {@link Nested @Nested} test classes or as interface + * default methods unless the test class is annotated with + * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}. However, beginning + * with Java 16 {@code @AfterAll} methods may be declared as {@code static} in + * {@link Nested @Nested} test classes, in which case the {@code Lifecycle.PER_CLASS} + * restriction no longer applies. In addition, {@code @AfterAll} methods may + * optionally declare parameters to be resolved by * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}. * - *

Using {@code private} visibility for {@code @BeforeAll} methods is - * strongly discouraged and will be disallowed in a future release. + *

Using {@code private} visibility for {@code @AfterAll} methods is strongly + * discouraged and will be disallowed in a future release. * *

Inheritance and Execution Order

* - *

{@code @AfterAll} methods are inherited from superclasses as long as - * they are not hidden (default mode with {@code static} modifier), - * overridden, or superseded (i.e., replaced based on - * signature only, irrespective of Java's visibility rules). Furthermore, - * {@code @AfterAll} methods from superclasses will be executed before - * {@code @AfterAll} methods in subclasses. + *

{@code @AfterAll} methods are inherited from superclasses as long as they + * are not overridden according to the visibility rules of the Java + * language. Furthermore, {@code @AfterAll} methods from superclasses will be + * executed after {@code @AfterAll} methods in subclasses. * - *

Similarly, {@code @AfterAll} methods declared in an interface are - * inherited as long as they are not hidden or overridden, - * and {@code @AfterAll} methods from an interface will be executed after - * {@code @AfterAll} methods in the class that implements the interface. + *

Similarly, {@code @AfterAll} methods declared in an interface are inherited + * as long as they are not overridden, and {@code @AfterAll} methods from an + * interface will be executed after {@code @AfterAll} methods in the class that + * implements the interface. * *

JUnit Jupiter does not guarantee the execution order of multiple * {@code @AfterAll} methods that are declared within a single test class or diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterEach.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterEach.java index 8dfd018fa4f9..bf1d90853f33 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterEach.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterEach.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -29,18 +29,19 @@ *

Method Signatures

* *

{@code @AfterEach} methods must have a {@code void} return type and must - * not be {@code static}. Using {@code private} visibility is strongly - * discouraged and will be disallowed in a future release. - * They may optionally declare parameters to be resolved by + * not be {@code static}. In addition, {@code @AfterEach} methods may optionally + * declare parameters to be resolved by * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}. * + *

Using {@code private} visibility for {@code @AfterEach} methods is strongly + * discouraged and will be disallowed in a future release. + * *

Inheritance and Execution Order

* *

{@code @AfterEach} methods are inherited from superclasses as long as they - * are not overridden or superseded (i.e., replaced based on - * signature only, irrespective of Java's visibility rules). Furthermore, - * {@code @AfterEach} methods from superclasses will be executed after - * {@code @AfterEach} methods in subclasses. + * are not overridden according to the visibility rules of the Java + * language. Furthermore, {@code @AfterEach} methods from superclasses will be + * executed after {@code @AfterEach} methods in subclasses. * *

Similarly, {@code @AfterEach} methods declared as interface default * methods are inherited as long as they are not overridden, and diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertAll.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertAll.java index 601e9ecf8d9c..29b1218ff060 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertAll.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertAll.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java index 45d37399c0e1..7da0accd15b4 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java index 2d424aa7ed39..e83f4508daaf 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java index 2a46cea309f3..558d9afc8147 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java index 5291a6ac6ead..83dad97da5ce 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java index 2f265f3fc237..60d9b278e5ee 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java index b837c449bca2..820b5365e2b3 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java index 03e79081bd9e..ef25f47e1dd3 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java index 5a37b059a0fc..4bdd3376248c 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java index 43787ab8c27b..8d63cd76bdd6 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java index 66f795c7a75a..606934f317ca 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java index 53ceb4ce099a..9d674d8c59c0 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java index feafa36231c9..8e9278d19c84 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java index b61570647cb5..02947d1637af 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java index 8a669388b709..830dca520260 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java index bfd1f66806ba..64b625b6c7fa 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -69,7 +69,7 @@ private static T assertTimeout(Duration timeout, ThrowingSupplier supplie result = supplier.get(); } catch (Throwable ex) { - throwAsUncheckedException(ex); + throw throwAsUncheckedException(ex); } long timeElapsed = System.currentTimeMillis() - start; diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeoutPreemptively.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeoutPreemptively.java index 4ff96b71556d..4585bb0e8927 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeoutPreemptively.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeoutPreemptively.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java index cf4f94277a93..0cb0bbc245bd 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionFailureBuilder.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionFailureBuilder.java index 8d0341d3cb46..5f826a88cb8d 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionFailureBuilder.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionFailureBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java index 48a74da2ce96..3ac27f22b1dd 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assertions.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assertions.java index 4be1561689dc..3f5df08971a0 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assertions.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assertions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -59,22 +59,26 @@ *

Preemptive Timeouts

* *

The various {@code assertTimeoutPreemptively()} methods in this class - * execute the provided {@code executable} or {@code supplier} in a different - * thread than that of the calling code. This behavior can lead to undesirable - * side effects if the code that is executed within the {@code executable} or - * {@code supplier} relies on {@link ThreadLocal} storage. + * execute the provided callback ({@code executable} or {@code supplier}) in a + * different thread than that of the calling code. If the timeout is exceeded, + * an attempt will be made to preemptively abort execution of the callback by + * {@linkplain Thread#interrupt() interrupting} the callback's thread. If the + * callback's thread does not return when interrupted, the thread will continue + * to run in the background after the {@code assertTimeoutPreemptively()} method + * has returned. * - *

One common example of this is the transactional testing support in the Spring - * Framework. Specifically, Spring's testing support binds transaction state to - * the current thread (via a {@code ThreadLocal}) before a test method is invoked. - * Consequently, if an {@code executable} or {@code supplier} provided to - * {@code assertTimeoutPreemptively()} invokes Spring-managed components that - * participate in transactions, any actions taken by those components will not be - * rolled back with the test-managed transaction. On the contrary, such actions - * will be committed to the persistent store (e.g., relational database) even - * though the test-managed transaction is rolled back. - * - *

Similar side effects may be encountered with other frameworks that rely on + *

Furthermore, the behavior of {@code assertTimeoutPreemptively()} methods + * can lead to undesirable side effects if the code that is executed within the + * callback relies on {@link ThreadLocal} storage. One common example of this is + * the transactional testing support in the Spring Framework. Specifically, Spring's + * testing support binds transaction state to the current thread (via a + * {@code ThreadLocal}) before a test method is invoked. Consequently, if a + * callback provided to {@code assertTimeoutPreemptively()} invokes Spring-managed + * components that participate in transactions, any actions taken by those + * components will not be rolled back with the test-managed transaction. On the + * contrary, such actions will be committed to the persistent store (e.g., + * relational database) even though the test-managed transaction is rolled back. + * Similar side effects may be encountered with other frameworks that rely on * {@code ThreadLocal} storage. * *

Extensibility

@@ -3066,7 +3070,11 @@ public static T assertThrowsExactly(Class expectedType, *

If you do not want to perform additional checks on the exception instance, * ignore the return value. * - *

Fails with the supplied failure {@code message}. + *

Fails with the supplied failure {@code message}. Note that the supplied + * {@code message} is not the expected message of the thrown + * exception. To assert the expected message of the thrown exception, you must + * use a separate, subsequent assertion against the exception returned from + * this method. * * @since 5.8 */ @@ -3084,7 +3092,11 @@ public static T assertThrowsExactly(Class expectedType, * thrown, this method will fail. * *

If necessary, the failure message will be retrieved lazily from the - * supplied {@code messageSupplier}. + * supplied {@code messageSupplier}. Note that the failure message is + * not the expected message of the thrown exception. To + * assert the expected message of the thrown exception, you must use a + * separate, subsequent assertion against the exception returned from this + * method. * *

If you do not want to perform additional checks on the exception instance, * ignore the return value. @@ -3101,11 +3113,16 @@ public static T assertThrowsExactly(Class expectedType, * Assert that execution of the supplied {@code executable} throws * an exception of the {@code expectedType} and return the exception. * - *

If no exception is thrown, or if an exception of a different type is - * thrown, this method will fail. + *

The assertion passes if the thrown exception type is the same as + * {@code expectedType} or a subtype thereof. To check for the exact thrown + * type use {@link #assertThrowsExactly(Class, Executable) assertThrowsExactly}. + * If no exception is thrown, or if an exception of a different type is thrown, + * this method will fail. * *

If you do not want to perform additional checks on the exception instance, * ignore the return value. + * + * @see #assertThrowsExactly(Class, Executable) */ public static T assertThrows(Class expectedType, Executable executable) { return AssertThrows.assertThrows(expectedType, executable); @@ -3115,13 +3132,22 @@ public static T assertThrows(Class expectedType, Execut * Assert that execution of the supplied {@code executable} throws * an exception of the {@code expectedType} and return the exception. * - *

If no exception is thrown, or if an exception of a different type is - * thrown, this method will fail. + *

The assertion passes if the thrown exception type is the same as + * {@code expectedType} or a subtype thereof. To check for the exact thrown + * type use {@link #assertThrowsExactly(Class, Executable, String) assertThrowsExactly}. + * If no exception is thrown, or if an exception of a different type is thrown, + * this method will fail. * *

If you do not want to perform additional checks on the exception instance, * ignore the return value. * - *

Fails with the supplied failure {@code message}. + *

Fails with the supplied failure {@code message}. Note that the supplied + * {@code message} is not the expected message of the thrown + * exception. To assert the expected message of the thrown exception, you must + * use a separate, subsequent assertion against the exception returned from + * this method. + * + * @see #assertThrowsExactly(Class, Executable, String) */ public static T assertThrows(Class expectedType, Executable executable, String message) { return AssertThrows.assertThrows(expectedType, executable, message); @@ -3131,14 +3157,23 @@ public static T assertThrows(Class expectedType, Execut * Assert that execution of the supplied {@code executable} throws * an exception of the {@code expectedType} and return the exception. * - *

If no exception is thrown, or if an exception of a different type is - * thrown, this method will fail. + *

The assertion passes if the thrown exception type is the same as + * {@code expectedType} or a subtype thereof. To check for the exact thrown + * type use {@link #assertThrowsExactly(Class, Executable, Supplier) assertThrowsExactly}. + * If no exception is thrown, or if an exception of a different type is thrown, + * this method will fail. * *

If necessary, the failure message will be retrieved lazily from the - * supplied {@code messageSupplier}. + * supplied {@code messageSupplier}. Note that the failure message is + * not the expected message of the thrown exception. To + * assert the expected message of the thrown exception, you must use a + * separate, subsequent assertion against the exception returned from this + * method. * *

If you do not want to perform additional checks on the exception instance, * ignore the return value. + * + * @see #assertThrowsExactly(Class, Executable, Supplier) */ public static T assertThrows(Class expectedType, Executable executable, Supplier messageSupplier) { @@ -3410,11 +3445,8 @@ public static T assertTimeout(Duration timeout, ThrowingSupplier supplier * Assert that execution of the supplied {@code executable} * completes before the given {@code timeout} is exceeded. * - *

Note: the {@code executable} will be executed in a different thread than - * that of the calling code. Furthermore, execution of the {@code executable} will - * be preemptively aborted if the timeout is exceeded. See the - * {@linkplain Assertions Preemptive Timeouts} section of the class-level - * Javadoc for a discussion of possible undesirable side effects. + *

See the {@linkplain Assertions Preemptive Timeouts} section of the + * class-level Javadoc for further details. * * @see #assertTimeoutPreemptively(Duration, Executable, String) * @see #assertTimeoutPreemptively(Duration, Executable, Supplier) @@ -3431,11 +3463,8 @@ public static void assertTimeoutPreemptively(Duration timeout, Executable execut * Assert that execution of the supplied {@code executable} * completes before the given {@code timeout} is exceeded. * - *

Note: the {@code executable} will be executed in a different thread than - * that of the calling code. Furthermore, execution of the {@code executable} will - * be preemptively aborted if the timeout is exceeded. See the - * {@linkplain Assertions Preemptive Timeouts} section of the class-level - * Javadoc for a discussion of possible undesirable side effects. + *

See the {@linkplain Assertions Preemptive Timeouts} section of the + * class-level Javadoc for further details. * *

Fails with the supplied failure {@code message}. * @@ -3454,11 +3483,8 @@ public static void assertTimeoutPreemptively(Duration timeout, Executable execut * Assert that execution of the supplied {@code executable} * completes before the given {@code timeout} is exceeded. * - *

Note: the {@code executable} will be executed in a different thread than - * that of the calling code. Furthermore, execution of the {@code executable} will - * be preemptively aborted if the timeout is exceeded. See the - * {@linkplain Assertions Preemptive Timeouts} section of the class-level - * Javadoc for a discussion of possible undesirable side effects. + *

See the {@linkplain Assertions Preemptive Timeouts} section of the + * class-level Javadoc for further details. * *

If necessary, the failure message will be retrieved lazily from the * supplied {@code messageSupplier}. @@ -3481,13 +3507,10 @@ public static void assertTimeoutPreemptively(Duration timeout, Executable execut * Assert that execution of the supplied {@code supplier} * completes before the given {@code timeout} is exceeded. * - *

If the assertion passes then the {@code supplier}'s result is returned. + *

See the {@linkplain Assertions Preemptive Timeouts} section of the + * class-level Javadoc for further details. * - *

Note: the {@code supplier} will be executed in a different thread than - * that of the calling code. Furthermore, execution of the {@code supplier} will - * be preemptively aborted if the timeout is exceeded. See the - * {@linkplain Assertions Preemptive Timeouts} section of the class-level - * Javadoc for a discussion of possible undesirable side effects. + *

If the assertion passes then the {@code supplier}'s result is returned. * * @see #assertTimeoutPreemptively(Duration, Executable) * @see #assertTimeoutPreemptively(Duration, Executable, String) @@ -3504,13 +3527,10 @@ public static T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier * Assert that execution of the supplied {@code supplier} * completes before the given {@code timeout} is exceeded. * - *

If the assertion passes then the {@code supplier}'s result is returned. + *

See the {@linkplain Assertions Preemptive Timeouts} section of the + * class-level Javadoc for further details. * - *

Note: the {@code supplier} will be executed in a different thread than - * that of the calling code. Furthermore, execution of the {@code supplier} will - * be preemptively aborted if the timeout is exceeded. See the - * {@linkplain Assertions Preemptive Timeouts} section of the class-level - * Javadoc for a discussion of possible undesirable side effects. + *

If the assertion passes then the {@code supplier}'s result is returned. * *

Fails with the supplied failure {@code message}. * @@ -3529,13 +3549,10 @@ public static T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier * Assert that execution of the supplied {@code supplier} * completes before the given {@code timeout} is exceeded. * - *

If the assertion passes then the {@code supplier}'s result is returned. + *

See the {@linkplain Assertions Preemptive Timeouts} section of the + * class-level Javadoc for further details. * - *

Note: the {@code supplier} will be executed in a different thread than - * that of the calling code. Furthermore, execution of the {@code supplier} will - * be preemptively aborted if the timeout is exceeded. See the - * {@linkplain Assertions Preemptive Timeouts} section of the class-level - * Javadoc for a discussion of possible undesirable side effects. + *

If the assertion passes then the {@code supplier}'s result is returned. * *

If necessary, the failure message will be retrieved lazily from the * supplied {@code messageSupplier}. @@ -3556,18 +3573,15 @@ public static T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier * Assert that execution of the supplied {@code supplier} * completes before the given {@code timeout} is exceeded. * + *

See the {@linkplain Assertions Preemptive Timeouts} section of the + * class-level Javadoc for further details. + * *

If the assertion passes then the {@code supplier}'s result is returned. * *

In the case the assertion does not pass, the supplied * {@link TimeoutFailureFactory} is invoked to create an exception which is * then thrown. * - *

Note: the {@code supplier} will be executed in a different thread than - * that of the calling code. Furthermore, execution of the {@code supplier} will - * be preemptively aborted if the timeout is exceeded. See the - * {@linkplain Assertions Preemptive Timeouts} section of the class-level - * Javadoc for a discussion of possible undesirable side effects. - * *

If necessary, the failure message will be retrieved lazily from the * supplied {@code messageSupplier}. * diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assumptions.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assumptions.java index d0448a89778e..f1ade0746475 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assumptions.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assumptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -248,7 +248,7 @@ public static void assumingThat(boolean assumption, Executable executable) { executable.execute(); } catch (Throwable t) { - ExceptionUtils.throwAsUncheckedException(t); + throw ExceptionUtils.throwAsUncheckedException(t); } } } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AutoClose.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AutoClose.java new file mode 100644 index 000000000000..0a29faba7a95 --- /dev/null +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AutoClose.java @@ -0,0 +1,94 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.api; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @AutoClose} is used to indicate that an annotated field will be + * automatically closed after test execution. + * + *

{@code @AutoClose} fields may be either {@code static} or non-static. If + * the value of an {@code @AutoClose} field is {@code null} when it is evaluated + * the field will be ignored, but a warning message will be logged to inform you. + * + *

By default, {@code @AutoClose} expects the value of the annotated field to + * implement a {@code close()} method that will be invoked to close the resource. + * However, developers can customize the name of the {@code close} method via the + * {@link #value} attribute. For example, {@code @AutoClose("shutdown")} instructs + * JUnit to look for a {@code shutdown()} method to close the resource. + * + *

{@code @AutoClose} may be used as a meta-annotation in order to create a + * custom composed annotation that inherits the semantics of + * {@code @AutoClose}. + * + *

Inheritance

+ * + *

{@code @AutoClose} fields are inherited from superclasses. Furthermore, + * {@code @AutoClose} fields from subclasses will be closed before + * {@code @AutoClose} fields in superclasses. + * + *

Evaluation Order

+ * + *

When multiple {@code @AutoClose} fields exist within a given test class, + * the order in which the resources are closed depends on an algorithm that is + * deterministic but intentionally nonobvious. This ensures that subsequent runs + * of a test suite close resources in the same order, thereby allowing for + * repeatable builds. + * + *

Scope and Lifecycle

+ * + *

The extension that closes {@code @AutoClose} fields implements the + * {@link org.junit.jupiter.api.extension.AfterAllCallback AfterAllCallback} and + * {@link org.junit.jupiter.api.extension.TestInstancePreDestroyCallback + * TestInstancePreDestroyCallback} extension APIs. Consequently, a {@code static} + * {@code @AutoClose} field will be closed after all tests in the current test + * class have completed, effectively after {@code @AfterAll} methods have executed + * for the test class. A non-static {@code @AutoClose} field will be closed before + * the current test class instance is destroyed. Specifically, if the test class + * is configured with + * {@link TestInstance.Lifecycle#PER_METHOD @TestInstance(Lifecycle.PER_METHOD)} + * semantics, a non-static {@code @AutoClose} field will be closed after the + * execution of each test method, test factory method, or test template method. + * However, if the test class is configured with + * {@link TestInstance.Lifecycle#PER_CLASS @TestInstance(Lifecycle.PER_CLASS)} + * semantics, a non-static {@code @AutoClose} field will not be closed until the + * current test class instance is no longer needed, which means after + * {@code @AfterAll} methods and after all {@code static} {@code @AutoClose} fields + * have been closed. + * + * @since 5.11 + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@API(status = EXPERIMENTAL, since = "5.11") +public @interface AutoClose { + + /** + * Specify the name of the method to invoke to close the resource. + * + *

The default value is {@code "close"} which works with any type that + * implements {@link AutoCloseable} or has a {@code close()} method. + * + * @return the name of the method to invoke to close the resource + */ + String value() default "close"; + +} diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeAll.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeAll.java index 30eba9b73746..197556e7ad3b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeAll.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeAll.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -33,29 +33,27 @@ * be {@code static} by default. Consequently, {@code @BeforeAll} methods are * not supported in {@link Nested @Nested} test classes or as interface * default methods unless the test class is annotated with - * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}. - * However, beginning with Java 16 {@code @BeforeAll} methods may be declared as - * {@code static} in {@link Nested @Nested} test classes, and the - * {@code Lifecycle.PER_CLASS} restriction no longer applies. {@code @BeforeAll} - * methods may optionally declare parameters to be resolved by + * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}. However, beginning + * with Java 16 {@code @BeforeAll} methods may be declared as {@code static} in + * {@link Nested @Nested} test classes, in which case the {@code Lifecycle.PER_CLASS} + * restriction no longer applies. In addition, {@code @BeforeAll} methods may + * optionally declare parameters to be resolved by * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}. * - *

Using {@code private} visibility for {@code @BeforeAll} methods is - * strongly discouraged and will be disallowed in a future release. + *

Using {@code private} visibility for {@code @BeforeAll} methods is strongly + * discouraged and will be disallowed in a future release. * *

Inheritance and Execution Order

* - *

{@code @BeforeAll} methods are inherited from superclasses as long as - * they are not hidden (default mode with {@code static} modifier), - * overridden, or superseded (i.e., replaced based on - * signature only, irrespective of Java's visibility rules). Furthermore, - * {@code @BeforeAll} methods from superclasses will be executed before - * {@code @BeforeAll} methods in subclasses. + *

{@code @BeforeAll} methods are inherited from superclasses as long as they + * are not overridden according to the visibility rules of the Java + * language. Furthermore, {@code @BeforeAll} methods from superclasses will be + * executed before {@code @BeforeAll} methods in subclasses. * - *

Similarly, {@code @BeforeAll} methods declared in an interface are - * inherited as long as they are not hidden or overridden, - * and {@code @BeforeAll} methods from an interface will be executed before - * {@code @BeforeAll} methods in the class that implements the interface. + *

Similarly, {@code @BeforeAll} methods declared in an interface are inherited + * as long as they are not overridden, and {@code @BeforeAll} methods from an + * interface will be executed before {@code @BeforeAll} methods in the class that + * implements the interface. * *

JUnit Jupiter does not guarantee the execution order of multiple * {@code @BeforeAll} methods that are declared within a single test class or diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeEach.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeEach.java index 37ca2498c889..7aee562c0ac8 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeEach.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeEach.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -29,18 +29,19 @@ *

Method Signatures

* *

{@code @BeforeEach} methods must have a {@code void} return type and must - * not be {@code static}. Using {@code private} visibility is strongly - * discouraged and will be disallowed in a future release. - * They may optionally declare parameters to be resolved by + * not be {@code static}. In addition, {@code @BeforeEach} methods may optionally + * declare parameters to be resolved by * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}. * + *

Using {@code private} visibility for {@code @BeforeEach} methods is strongly + * discouraged and will be disallowed in a future release. + * *

Inheritance and Execution Order

* *

{@code @BeforeEach} methods are inherited from superclasses as long as they - * are not overridden or superseded (i.e., replaced based on - * signature only, irrespective of Java's visibility rules). Furthermore, - * {@code @BeforeEach} methods from superclasses will be executed before - * {@code @BeforeEach} methods in subclasses. + * are not overridden according to the visibility rules of the Java + * language. Furthermore, {@code @BeforeEach} methods from superclasses will be + * executed before {@code @BeforeEach} methods in subclasses. * *

Similarly, {@code @BeforeEach} methods declared as interface default * methods are inherited as long as they are not overridden, and diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassDescriptor.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassDescriptor.java index 5773e0b007c5..d6c53bee156c 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassDescriptor.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrderer.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrderer.java index 2bc852b412a5..e27c05abb926 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrderer.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -15,7 +15,6 @@ import java.util.Collections; import java.util.Comparator; -import java.util.Optional; import org.apiguardian.api.API; import org.junit.platform.commons.logging.Logger; @@ -185,8 +184,8 @@ private static int getOrder(ClassDescriptor descriptor) { *

Custom Seed

* *

By default, the random seed used for ordering classes is the - * value returned by {@link System#nanoTime()} during static initialization - * of this class. In order to support repeatable builds, the value of the + * value returned by {@link System#nanoTime()} during static class + * initialization. In order to support repeatable builds, the value of the * default random seed is logged at {@code CONFIG} level. In addition, a * custom seed (potentially the default seed from the previous test plan * execution) may be specified via the {@value Random#RANDOM_SEED_PROPERTY_NAME} @@ -202,15 +201,8 @@ class Random implements ClassOrderer { private static final Logger logger = LoggerFactory.getLogger(Random.class); - /** - * Default seed, which is generated during initialization of this class - * via {@link System#nanoTime()} for reproducibility of tests. - */ - private static final long DEFAULT_SEED; - static { - DEFAULT_SEED = System.nanoTime(); - logger.config(() -> "ClassOrderer.Random default seed: " + DEFAULT_SEED); + logger.config(() -> "ClassOrderer.Random default seed: " + RandomOrdererUtils.DEFAULT_SEED); } /** @@ -231,7 +223,7 @@ class Random implements ClassOrderer { * * @see MethodOrderer.Random */ - public static final String RANDOM_SEED_PROPERTY_NAME = MethodOrderer.Random.RANDOM_SEED_PROPERTY_NAME; + public static final String RANDOM_SEED_PROPERTY_NAME = RandomOrdererUtils.RANDOM_SEED_PROPERTY_NAME; public Random() { } @@ -243,27 +235,7 @@ public Random() { @Override public void orderClasses(ClassOrdererContext context) { Collections.shuffle(context.getClassDescriptors(), - new java.util.Random(getCustomSeed(context).orElse(DEFAULT_SEED))); - } - - private Optional getCustomSeed(ClassOrdererContext context) { - return context.getConfigurationParameter(RANDOM_SEED_PROPERTY_NAME).map(configurationParameter -> { - Long seed = null; - try { - seed = Long.valueOf(configurationParameter); - logger.config( - () -> String.format("Using custom seed for configuration parameter [%s] with value [%s].", - RANDOM_SEED_PROPERTY_NAME, configurationParameter)); - } - catch (NumberFormatException ex) { - logger.warn(ex, - () -> String.format( - "Failed to convert configuration parameter [%s] with value [%s] to a long. " - + "Using default seed [%s] as fallback.", - RANDOM_SEED_PROPERTY_NAME, configurationParameter, DEFAULT_SEED)); - } - return seed; - }); + new java.util.Random(RandomOrdererUtils.getSeed(context::getConfigurationParameter, logger))); } } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrdererContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrdererContext.java index 45f60c12679c..9d9d0325cdb9 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrdererContext.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrdererContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Disabled.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Disabled.java index 9a7717053491..44ec76fca882 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Disabled.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Disabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -30,11 +30,17 @@ *

When applied at the class level, all test methods within that class * are automatically disabled as well. * - *

When applied at the method level, the presence of this annotation does not - * prevent the test class from being instantiated. Rather, it prevents the - * execution of the test method and method-level lifecycle callbacks such as + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding - * extension APIs. + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * * @since 5.0 * @see #value diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayName.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayName.java index e99ee34c75fc..abc398327f15 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayName.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayName.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGeneration.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGeneration.java index bb0e1bf9f70a..5345e4d7305e 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGeneration.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGeneration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGenerator.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGenerator.java index 22c6ec62551d..f396ce850c7d 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGenerator.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicContainer.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicContainer.java index dcc48186c5ec..5a37b8e156fd 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicContainer.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicNode.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicNode.java index c1a151779f7f..be569469d852 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicNode.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java index 935951663d48..b61ccb71a57a 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,6 +12,7 @@ import static java.util.Spliterator.ORDERED; import static java.util.Spliterators.spliteratorUnknownSize; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.MAINTAINED; import java.net.URI; @@ -226,6 +227,67 @@ public static Stream stream(Stream> inputStr .map(input -> dynamicTest(input.getName(), () -> testExecutor.accept(input.getPayload()))); } + /** + * Generate a stream of dynamic tests based on the given iterator. + * + *

Use this method when the set of dynamic tests is nondeterministic in + * nature or when the input comes from an existing {@link Iterator}. See + * {@link #stream(Stream)} as an alternative. + * + *

The given {@code iterator} is responsible for supplying + * {@link Named} input values that provide an {@link Executable} code block. + * A {@link DynamicTest} comprised of both parts will be added to the + * resulting stream for each dynamically supplied input value. + * + * @param iterator an {@code Iterator} that supplies named executables; + * never {@code null} + * @param the type of input supplied by the {@code inputStream} + * @return a stream of dynamic tests based on the given iterator; never + * {@code null} + * @since 5.11 + * @see #dynamicTest(String, Executable) + * @see #stream(Stream) + * @see NamedExecutable + */ + @API(status = EXPERIMENTAL, since = "5.11") + public static , E extends Executable> Stream stream( + Iterator iterator) { + Preconditions.notNull(iterator, "iterator must not be null"); + + return stream(StreamSupport.stream(spliteratorUnknownSize(iterator, ORDERED), false)); + } + + /** + * Generate a stream of dynamic tests based on the given input stream. + * + *

Use this method when the set of dynamic tests is nondeterministic in + * nature or when the input comes from an existing {@link Stream}. See + * {@link #stream(Iterator)} as an alternative. + * + *

The given {@code inputStream} is responsible for supplying + * {@link Named} input values that provide an {@link Executable} code block. + * A {@link DynamicTest} comprised of both parts will be added to the + * resulting stream for each dynamically supplied input value. + * + * @param inputStream a {@code Stream} that supplies named executables; + * never {@code null} + * @param the type of input supplied by the {@code inputStream} + * @return a stream of dynamic tests based on the given stream; never + * {@code null} + * @since 5.11 + * @see #dynamicTest(String, Executable) + * @see #stream(Iterator) + * @see NamedExecutable + */ + @API(status = EXPERIMENTAL, since = "5.11") + public static , E extends Executable> Stream stream( + Stream inputStream) { + Preconditions.notNull(inputStream, "inputStream must not be null"); + + return inputStream. // + map(input -> dynamicTest(input.getName(), input.getPayload())); + } + private final Executable executable; private DynamicTest(String displayName, URI testSourceUri, Executable executable) { diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/IndicativeSentencesGeneration.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/IndicativeSentencesGeneration.java index 97ab817eb4b3..9a24a7e4e1d6 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/IndicativeSentencesGeneration.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/IndicativeSentencesGeneration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodDescriptor.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodDescriptor.java index 49f62ab2760b..7fbf3f569b32 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodDescriptor.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrderer.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrderer.java index c8269f4a705f..4a71d5944551 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrderer.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -248,11 +248,11 @@ private static int getOrder(MethodDescriptor descriptor) { *

Custom Seed

* *

By default, the random seed used for ordering methods is the - * value returned by {@link System#nanoTime()} during static initialization - * of this class. In order to support repeatable builds, the value of the + * value returned by {@link System#nanoTime()} during static class + * initialization. In order to support repeatable builds, the value of the * default random seed is logged at {@code CONFIG} level. In addition, a * custom seed (potentially the default seed from the previous test plan - * execution) may be specified via the {@value ClassOrderer.Random#RANDOM_SEED_PROPERTY_NAME} + * execution) may be specified via the {@value Random#RANDOM_SEED_PROPERTY_NAME} * configuration parameter which can be supplied via the {@code Launcher} * API, build tools (e.g., Gradle and Maven), a JVM system property, or the JUnit * Platform configuration file (i.e., a file named {@code junit-platform.properties} @@ -265,15 +265,8 @@ class Random implements MethodOrderer { private static final Logger logger = LoggerFactory.getLogger(Random.class); - /** - * Default seed, which is generated during initialization of this class - * via {@link System#nanoTime()} for reproducibility of tests. - */ - private static final long DEFAULT_SEED; - static { - DEFAULT_SEED = System.nanoTime(); - logger.config(() -> "MethodOrderer.Random default seed: " + DEFAULT_SEED); + logger.config(() -> "MethodOrderer.Random default seed: " + RandomOrdererUtils.DEFAULT_SEED); } /** @@ -294,7 +287,7 @@ class Random implements MethodOrderer { * * @see ClassOrderer.Random */ - public static final String RANDOM_SEED_PROPERTY_NAME = "junit.jupiter.execution.order.random.seed"; + public static final String RANDOM_SEED_PROPERTY_NAME = RandomOrdererUtils.RANDOM_SEED_PROPERTY_NAME; public Random() { } @@ -306,28 +299,9 @@ public Random() { @Override public void orderMethods(MethodOrdererContext context) { Collections.shuffle(context.getMethodDescriptors(), - new java.util.Random(getCustomSeed(context).orElse(DEFAULT_SEED))); + new java.util.Random(RandomOrdererUtils.getSeed(context::getConfigurationParameter, logger))); } - private Optional getCustomSeed(MethodOrdererContext context) { - return context.getConfigurationParameter(RANDOM_SEED_PROPERTY_NAME).map(configurationParameter -> { - Long seed = null; - try { - seed = Long.valueOf(configurationParameter); - logger.config( - () -> String.format("Using custom seed for configuration parameter [%s] with value [%s].", - RANDOM_SEED_PROPERTY_NAME, configurationParameter)); - } - catch (NumberFormatException ex) { - logger.warn(ex, - () -> String.format( - "Failed to convert configuration parameter [%s] with value [%s] to a long. " - + "Using default seed [%s] as fallback.", - RANDOM_SEED_PROPERTY_NAME, configurationParameter, DEFAULT_SEED)); - } - return seed; - }); - } } } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrdererContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrdererContext.java index cb585b709416..bab5d3e10ec5 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrdererContext.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrdererContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Named.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Named.java index 6dee03e4a1ea..e41b4be29ab3 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Named.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Named.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/NamedExecutable.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/NamedExecutable.java new file mode 100644 index 000000000000..16d5a72d2d18 --- /dev/null +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/NamedExecutable.java @@ -0,0 +1,48 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.api; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.util.Iterator; +import java.util.stream.Stream; + +import org.apiguardian.api.API; +import org.junit.jupiter.api.function.Executable; + +/** + * {@code NamedExecutable} joins {@code Executable} and {@code Named} in a + * one self-typed functional interface. + * + *

The default implementation of {@link #getName()} returns the result of + * calling {@link Object#toString()} on the implementing instance but may be + * overridden by concrete implementations to provide a more meaningful name. + * + *

On Java 16 or later, it is recommended to implement this interface using + * a record type. + * + * @since 5.11 + * @see DynamicTest#stream(Stream) + * @see DynamicTest#stream(Iterator) + */ +@FunctionalInterface +@API(status = EXPERIMENTAL, since = "5.11") +public interface NamedExecutable extends Named, Executable { + @Override + default String getName() { + return toString(); + } + + @Override + default Executable getPayload() { + return this; + } +} diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Nested.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Nested.java index 1627010af1a0..2e9c559e9390 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Nested.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Nested.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Order.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Order.java index 5b48253fe0c0..3055cdc2cd0d 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Order.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Order.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RandomOrdererUtils.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RandomOrdererUtils.java new file mode 100644 index 000000000000..35b5dc6208ae --- /dev/null +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RandomOrdererUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.api; + +import java.util.Optional; +import java.util.function.Function; + +import org.junit.platform.commons.logging.Logger; + +/** + * Shared utility methods for ordering test classes and test methods randomly. + * + * @since 5.11 + * @see ClassOrderer.Random + * @see MethodOrderer.Random + */ +class RandomOrdererUtils { + + static final String RANDOM_SEED_PROPERTY_NAME = "junit.jupiter.execution.order.random.seed"; + + static final long DEFAULT_SEED = System.nanoTime(); + + static Long getSeed(Function> configurationParameterLookup, Logger logger) { + return getCustomSeed(configurationParameterLookup, logger).orElse(DEFAULT_SEED); + } + + private static Optional getCustomSeed(Function> configurationParameterLookup, + Logger logger) { + return configurationParameterLookup.apply(RANDOM_SEED_PROPERTY_NAME).map(configurationParameter -> { + try { + logger.config(() -> String.format("Using custom seed for configuration parameter [%s] with value [%s].", + RANDOM_SEED_PROPERTY_NAME, configurationParameter)); + return Long.valueOf(configurationParameter); + } + catch (NumberFormatException ex) { + logger.warn(ex, + () -> String.format( + "Failed to convert configuration parameter [%s] with value [%s] to a long. " + + "Using default seed [%s] as fallback.", + RANDOM_SEED_PROPERTY_NAME, configurationParameter, DEFAULT_SEED)); + return null; + } + }); + } +} diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RepeatedTest.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RepeatedTest.java index 05b51d6f1983..063228c97486 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RepeatedTest.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RepeatedTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -44,6 +44,13 @@ * create a custom composed annotation that inherits the semantics * of {@code @RepeatedTest}. * + *

Inheritance

+ * + *

{@code @RepeatedTest} methods are inherited from superclasses as long as + * they are not overridden according to the visibility rules of the Java + * language. Similarly, {@code @RepeatedTest} methods declared as interface + * default methods are inherited as long as they are not overridden. + * *

Test Execution Order

* *

By default, test methods will be ordered using an algorithm that is diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RepetitionInfo.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RepetitionInfo.java index 77c214f92c19..e7b84a024ac1 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RepetitionInfo.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RepetitionInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Tag.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Tag.java index 0def1ed38dcb..87777564aaaf 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Tag.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Tag.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Tags.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Tags.java index 0d6526ecd9b8..5b681705a018 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Tags.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Tags.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Test.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Test.java index 53c520bd2f2a..f17d735904a4 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Test.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Test.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -22,19 +22,24 @@ import org.junit.platform.commons.annotation.Testable; /** - * {@code @Test} is used to signal that the annotated method is a - * test method. + * {@code @Test} is used to signal that the annotated method is a test + * method. * - *

{@code @Test} methods must not be {@code private} or {@code static} - * and must not return a value. + *

{@code @Test} methods must not be {@code private} or {@code static} and + * must not return a value. * - *

{@code @Test} methods may optionally declare parameters to be - * resolved by {@link org.junit.jupiter.api.extension.ParameterResolver - * ParameterResolvers}. + *

{@code @Test} methods may optionally declare parameters to be resolved by + * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}. * - *

{@code @Test} may also be used as a meta-annotation in order to create - * a custom composed annotation that inherits the semantics of - * {@code @Test}. + *

{@code @Test} may also be used as a meta-annotation in order to create a + * custom composed annotation that inherits the semantics of {@code @Test}. + * + *

Inheritance

+ * + *

{@code @Test} methods are inherited from superclasses as long as they are + * not overridden according to the visibility rules of the Java language. + * Similarly, {@code @Test} methods declared as interface default methods + * are inherited as long as they are not overridden. * *

Test Execution Order

* diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestClassOrder.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestClassOrder.java index 7658214e7559..86bc8c66a9da 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestClassOrder.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestClassOrder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestFactory.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestFactory.java index 96b1f5248a03..dd17d2647186 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestFactory.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -43,6 +43,13 @@ * resolved by {@link org.junit.jupiter.api.extension.ParameterResolver * ParameterResolvers}. * + *

Inheritance

+ * + *

{@code @TestFactory} methods are inherited from superclasses as long as + * they are not overridden according to the visibility rules of the Java + * language. Similarly, {@code @TestFactory} methods declared as interface + * default methods are inherited as long as they are not overridden. + * *

Test Execution Order

* *

By default, test methods will be ordered using an algorithm that is diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInfo.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInfo.java index c7949d1cbb78..518a4ad69c15 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInfo.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInstance.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInstance.java index 5c57e95e4a14..8ff063d7927a 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInstance.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInstance.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -20,6 +20,7 @@ import java.lang.annotation.Target; import org.apiguardian.api.API; +import org.junit.jupiter.api.parallel.Execution; /** * {@code @TestInstance} is a type-level annotation that is used to configure @@ -59,8 +60,14 @@ * create a custom composed annotation that inherits the semantics * of {@code @TestInstance}. * + *

Parallel Execution

+ *

Using the {@link Lifecycle#PER_CLASS PER_CLASS} lifecycle mode disables + * parallel execution unless the test class or test method is annotated with + * {@link Execution @Execution(CONCURRENT)}. + * * @since 5.0 * @see Nested @Nested + * @see Execution @Execution */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestMethodOrder.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestMethodOrder.java index 2e6a571eed1c..93790ca79305 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestMethodOrder.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestMethodOrder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -20,6 +20,7 @@ import java.lang.annotation.Target; import org.apiguardian.api.API; +import org.junit.jupiter.api.parallel.Execution; /** * {@code @TestMethodOrder} is a type-level annotation that is used to configure @@ -64,6 +65,11 @@ * } * * + *

Parallel Execution

+ *

Using a {@link MethodOrderer} disables parallel execution unless the test + * class or test method is annotated with + * {@link Execution @Execution(CONCURRENT)}. + * * @since 5.4 * @see MethodOrderer * @see TestClassOrder diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestReporter.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestReporter.java index 6b5b349b62de..ffcf580603ff 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestReporter.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestReporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestTemplate.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestTemplate.java index 24f8208a7229..c9d337108405 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestTemplate.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -49,6 +49,13 @@ * create a custom composed annotation that inherits the semantics * of {@code @TestTemplate}. * + *

Inheritance

+ * + *

{@code @TestTemplate} methods are inherited from superclasses as long as + * they are not overridden according to the visibility rules of the Java + * language. Similarly, {@code @TestTemplate} methods declared as interface + * default methods are inherited as long as they are not overridden. + * *

Test Execution Order

* *

By default, test methods will be ordered using an algorithm that is diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Timeout.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Timeout.java index f80d8bf6f2ee..831fb2963686 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Timeout.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Timeout.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -354,7 +354,7 @@ * @since 5.9 * @see ThreadMode */ - @API(status = EXPERIMENTAL, since = "5.9") + @API(status = STABLE, since = "5.11") ThreadMode threadMode() default ThreadMode.INFERRED; /** @@ -363,7 +363,7 @@ * * @since 5.9 */ - @API(status = EXPERIMENTAL, since = "5.9") + @API(status = STABLE, since = "5.11") enum ThreadMode { /** * The thread mode is determined using the parameter configured in property diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractOsBasedExecutionCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractOsBasedExecutionCondition.java index 0a82b367c684..b27d52c7332d 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractOsBasedExecutionCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractOsBasedExecutionCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractRepeatableAnnotationCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractRepeatableAnnotationCondition.java index 5463f23c16e4..2e29da7e653b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractRepeatableAnnotationCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractRepeatableAnnotationCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/BooleanExecutionCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/BooleanExecutionCondition.java index a4f7ac18f6ab..3d84af41b40b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/BooleanExecutionCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/BooleanExecutionCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledForJreRange.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledForJreRange.java index a3b0762e0de5..2ca116acfb9f 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledForJreRange.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledForJreRange.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -22,17 +22,24 @@ import org.junit.jupiter.api.extension.ExtendWith; /** - * {@code @DisabledForJreRange} is used to signal that the annotated test class or - * test method is only disabled for a specific range of Java Runtime + * {@code @DisabledForJreRange} is used to signal that the annotated test class + * or test method is disabled for a specific range of Java Runtime * Environment (JRE) versions from {@link #min} to {@link #max}. * *

When applied at the class level, all test methods within that class will * be disabled on the same specified JRE versions. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

This annotation may be used as a meta-annotation in order to create a * custom composed annotation that inherits the semantics of this diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledForJreRangeCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledForJreRangeCondition.java index 90e157d13d0a..fb8d45b0963e 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledForJreRangeCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledForJreRangeCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIf.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIf.java index 06bdd569048a..6e823856cc38 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIf.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIf.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -23,16 +23,23 @@ /** * {@code @DisabledIf} is used to signal that the annotated test class or test - * method is disabled only if the provided - * {@linkplain #value() condition} evaluates to {@code true}. + * method is disabled if the provided {@linkplain #value() condition} + * evaluates to {@code true}. * *

When applied at the class level, all test methods within that class will * be disabled on the same condition. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

This annotation may be used as a meta-annotation in order to create a * custom composed annotation that inherits the semantics of this diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfCondition.java index 1998f8840d3e..2d27cbeb8f8a 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariable.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariable.java index 0cc90eec72a4..69c600576056 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariable.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariable.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -31,10 +31,17 @@ *

When declared at the class level, the result will apply to all test methods * within that class as well. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

If the specified environment variable is undefined, the presence of this * annotation will have no effect on whether or not the class or method diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableCondition.java index de9a85154e4c..43dd839d544c 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariables.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariables.java index deef2a4a8fa0..b29e874949cf 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariables.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariables.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -28,6 +28,10 @@ * is completely optional since {@code @DisabledIfEnvironmentVariable} is a {@linkplain * java.lang.annotation.Repeatable repeatable} annotation. * + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * * @since 5.6 * @see DisabledIfEnvironmentVariable * @see java.lang.annotation.Repeatable diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemProperties.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemProperties.java index ff6877bf1a06..f103e3066b29 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemProperties.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -28,6 +28,10 @@ * is completely optional since {@code @DisabledIfSystemProperty} is a {@linkplain * java.lang.annotation.Repeatable repeatable} annotation. * + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * * @since 5.6 * @see DisabledIfSystemProperty * @see java.lang.annotation.Repeatable diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemProperty.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemProperty.java index ac7d0937d848..43e856b6a9c6 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemProperty.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -31,10 +31,17 @@ *

When declared at the class level, the result will apply to all test methods * within that class as well. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

If the specified system property is undefined, the presence of this * annotation will have no effect on whether or not the class or method diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyCondition.java index c2e17250374e..d28a906c0150 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledInNativeImage.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledInNativeImage.java index a96399e799be..e7505d328a00 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledInNativeImage.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledInNativeImage.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -22,16 +22,23 @@ /** * {@code @DisabledInNativeImage} is used to signal that the annotated test class - * or test method is only disabled when executing within a GraalVM native + * or test method is disabled when executing within a GraalVM native * image. * *

When applied at the class level, all test methods within that class will * be disabled within a native image. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

This annotation may be used as a meta-annotation in order to create a * custom composed annotation that inherits the semantics of this diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnJre.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnJre.java index 2aa7f012c947..ed1c6e272511 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnJre.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnJre.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -23,16 +23,23 @@ /** * {@code @DisabledOnJre} is used to signal that the annotated test class or - * test method is disabled on one or more specified Java - * Runtime Environment (JRE) {@linkplain #value versions}. + * test method is disabled on one or more specified Java Runtime + * Environment (JRE) {@linkplain #value versions}. * *

When applied at the class level, all test methods within that class * will be disabled on the same specified JRE versions. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

This annotation may be used as a meta-annotation in order to create a * custom composed annotation that inherits the semantics of this diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnJreCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnJreCondition.java index a2bacd66911d..fd2b0996c5e2 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnJreCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnJreCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnOs.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnOs.java index f067c2290083..a8dd1dd0fe3e 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnOs.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnOs.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -34,10 +34,17 @@ * will be disabled on the same specified operating systems, architectures, or * the specified combinations of both. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

This annotation may be used as a meta-annotation in order to create a * custom composed annotation that inherits the semantics of this diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnOsCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnOsCondition.java index 90ec208976af..714c74cb37bd 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnOsCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnOsCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledForJreRange.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledForJreRange.java index b1efb7c88e0d..1ccd390c08e8 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledForJreRange.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledForJreRange.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -29,10 +29,17 @@ *

When applied at the class level, all test methods within that class will * be enabled on the same specified JRE versions. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

This annotation may be used as a meta-annotation in order to create a * custom composed annotation that inherits the semantics of this diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledForJreRangeCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledForJreRangeCondition.java index 5a7c9e5417d3..0c7c0187778e 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledForJreRangeCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledForJreRangeCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIf.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIf.java index 5bd4b3df30e7..56d72c8fce7d 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIf.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIf.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -23,16 +23,23 @@ /** * {@code @EnabledIf} is used to signal that the annotated test class or test - * method is enabled only if the provided - * {@linkplain #value() condition} evaluates to {@code true}. + * method is only enabled if the provided {@linkplain #value() condition} + * evaluates to {@code true}. * *

When applied at the class level, all test methods within that class will * be enabled on the same condition. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

This annotation may be used as a meta-annotation in order to create a * custom composed annotation that inherits the semantics of this diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfCondition.java index 3ebaa594dc9a..2edcec77b7b9 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariable.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariable.java index 44cf3bf5940f..85d455d98582 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariable.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariable.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -31,10 +31,17 @@ *

When declared at the class level, the result will apply to all test methods * within that class as well. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

If the specified environment variable is undefined, the annotated class or * method will be disabled. diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableCondition.java index 13848432aa0d..b336e6d78a64 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariables.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariables.java index 3589ed58b217..60d8b5de9cfb 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariables.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariables.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -28,6 +28,10 @@ * is completely optional since {@code @EnabledIfEnvironmentVariable} is a {@linkplain * java.lang.annotation.Repeatable repeatable} annotation. * + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * * @since 5.6 * @see EnabledIfEnvironmentVariable * @see java.lang.annotation.Repeatable diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemProperties.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemProperties.java index f33bdfae4790..1f26ceef55a5 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemProperties.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -28,6 +28,10 @@ * is completely optional since {@code @EnabledIfSystemProperty} is a {@linkplain * java.lang.annotation.Repeatable repeatable} annotation. * + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * * @since 5.6 * @see EnabledIfSystemProperty * @see java.lang.annotation.Repeatable diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemProperty.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemProperty.java index 26cbcc10743a..a2f8fb004d60 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemProperty.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -31,10 +31,17 @@ *

When declared at the class level, the result will apply to all test methods * within that class as well. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

If the specified system property is undefined, the annotated class or * method will be disabled. diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyCondition.java index af8c7cb903cb..f6d5ffb79ebf 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledInNativeImage.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledInNativeImage.java index 33641509e7a0..c2c7993adafe 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledInNativeImage.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledInNativeImage.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -28,10 +28,17 @@ *

When applied at the class level, all test methods within that class will * be enabled within a native image. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

This annotation may be used as a meta-annotation in order to create a * custom composed annotation that inherits the semantics of this diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnJre.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnJre.java index ab6e18a193e4..b37776e31ee6 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnJre.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnJre.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -29,10 +29,17 @@ *

When applied at the class level, all test methods within that class * will be enabled on the same specified JRE versions. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

This annotation may be used as a meta-annotation in order to create a * custom composed annotation that inherits the semantics of this diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnJreCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnJreCondition.java index 2bd865351d20..70c0b3867a66 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnJreCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnJreCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnOs.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnOs.java index de284cd68654..eff74e6ccb57 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnOs.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnOs.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -34,10 +34,17 @@ * will be enabled on the same specified operating systems, architectures, or * the specified combinations of both. * - *

If a test method is disabled via this annotation, that does not prevent - * the test class from being instantiated. Rather, it prevents the execution of - * the test method and method-level lifecycle callbacks such as {@code @BeforeEach} - * methods, {@code @AfterEach} methods, and corresponding extension APIs. + *

This annotation is not {@link java.lang.annotation.Inherited @Inherited}. + * Consequently, if you wish to apply the same semantics to a subclass, this + * annotation must be redeclared on the subclass. + * + *

If a test method is disabled via this annotation, that prevents execution + * of the test method and method-level lifecycle callbacks such as + * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding + * extension APIs. However, that does not prevent the test class from being + * instantiated, and it does not prevent the execution of class-level lifecycle + * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and + * corresponding extension APIs. * *

This annotation may be used as a meta-annotation in order to create a * custom composed annotation that inherits the semantics of this diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnOsCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnOsCondition.java index 092342f7a2ac..1853b9f09f8a 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnOsCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnOsCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/MethodBasedCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/MethodBasedCondition.java index 64fc1008ba2b..f06738d1eea2 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/MethodBasedCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/MethodBasedCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/OS.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/OS.java index adedf30de2a5..9e4094599fa2 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/OS.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/OS.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterAllCallback.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterAllCallback.java index cdf4c267c468..b98600d85849 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterAllCallback.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterAllCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterEachCallback.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterEachCallback.java index 6c26cc11743c..2d8724e1974c 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterEachCallback.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterEachCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterTestExecutionCallback.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterTestExecutionCallback.java index 817744a167fe..72ca8e31d8a1 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterTestExecutionCallback.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterTestExecutionCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AnnotatedElementContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AnnotatedElementContext.java index 49cf3500a033..924e4a918d93 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AnnotatedElementContext.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AnnotatedElementContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeAllCallback.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeAllCallback.java index ae78aa2ddc10..5533e1169904 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeAllCallback.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeAllCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeEachCallback.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeEachCallback.java index 2ba5b378abe5..273a7de1f900 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeEachCallback.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeEachCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.java index 9a25088158ad..35b634c47473 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ConditionEvaluationResult.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ConditionEvaluationResult.java index e01c5ac66b43..9b67863e51bb 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ConditionEvaluationResult.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ConditionEvaluationResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/DynamicTestInvocationContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/DynamicTestInvocationContext.java index a040ae804347..f92391570724 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/DynamicTestInvocationContext.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/DynamicTestInvocationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,7 @@ package org.junit.jupiter.api.extension; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.STABLE; import org.apiguardian.api.API; import org.junit.jupiter.api.function.Executable; @@ -23,7 +23,7 @@ * @since 5.8 * @see org.junit.jupiter.api.DynamicTest */ -@API(status = EXPERIMENTAL, since = "5.8") +@API(status = STABLE, since = "5.11") public interface DynamicTestInvocationContext { /** diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExecutableInvoker.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExecutableInvoker.java index 7cfad263ad5b..4e1b8e753d31 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExecutableInvoker.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExecutableInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,7 @@ package org.junit.jupiter.api.extension; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.STABLE; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -24,7 +24,7 @@ * * @since 5.9 */ -@API(status = EXPERIMENTAL, since = "5.9") +@API(status = STABLE, since = "5.11") public interface ExecutableInvoker { /** diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExecutionCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExecutionCondition.java index 9747a1e4d82c..d82687f4fd22 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExecutionCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExecutionCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -24,10 +24,12 @@ * supplied {@link ExtensionContext}. * *

If an {@code ExecutionCondition} {@linkplain ConditionEvaluationResult#disabled - * disables} a test method, that does not prevent the test class from being - * instantiated. Rather, it prevents the execution of the test method and + * disables} a test method, that prevents execution of the test method and * method-level lifecycle callbacks such as {@code @BeforeEach} methods, - * {@code @AfterEach} methods, and corresponding extension APIs. + * {@code @AfterEach} methods, and corresponding extension APIs. However, that + * does not prevent the test class from being instantiated, and it does not prevent + * the execution of class-level lifecycle callbacks such as {@code @BeforeAll} + * methods, {@code @AfterAll} methods, and corresponding extension APIs. * *

Constructor Requirements

* diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtendWith.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtendWith.java index 1030887ac384..036e7eb90396 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtendWith.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtendWith.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -23,9 +23,9 @@ import org.apiguardian.api.API; /** - * {@code @ExtendWith} is a {@linkplain Repeatable repeatable} annotation - * that is used to register {@linkplain Extension extensions} for the annotated - * test class, test interface, test method, parameter, or field. + * {@code @ExtendWith} is a {@linkplain Repeatable repeatable} annotation that + * is used to register {@linkplain Extension extensions} for the annotated test + * class, test interface, test method, parameter, or field. * *

Annotated parameters are supported in test class constructors, in test * methods, and in {@code @BeforeAll}, {@code @AfterAll}, {@code @BeforeEach}, @@ -35,10 +35,10 @@ * *

Inheritance

* - *

{@code @ExtendWith} fields are inherited from superclasses as long as they - * are not hidden or overridden. Furthermore, {@code @ExtendWith} - * fields from superclasses will be registered before {@code @ExtendWith} fields - * in subclasses. + *

{@code @ExtendWith} fields are inherited from superclasses. Furthermore, + * {@code @ExtendWith} fields from superclasses will be registered before + * {@code @ExtendWith} fields in subclasses unless {@code @Order} is used to + * alter that behavior (see below). * *

Registration Order

* diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/Extension.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/Extension.java index fa1d255e65c1..78c68ab96867 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/Extension.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/Extension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionConfigurationException.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionConfigurationException.java index f4204b49427c..49e6defb985d 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionConfigurationException.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionConfigurationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContext.java index 3182b91035db..44a3447a4e7b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContext.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,6 @@ package org.junit.jupiter.api.extension; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; import java.lang.reflect.AnnotatedElement; @@ -399,7 +398,7 @@ default void publishReportEntry(String value) { * * @since 5.9 */ - @API(status = EXPERIMENTAL, since = "5.9") + @API(status = STABLE, since = "5.11") ExecutableInvoker getExecutableInvoker(); /** diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContextException.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContextException.java index c47bfc48e6fa..56192607892c 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContextException.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContextException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/Extensions.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/Extensions.java index a683dea9ff2c..3607def6d0aa 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/Extensions.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/Extensions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -33,7 +33,7 @@ * @see ExtendWith * @see java.lang.annotation.Repeatable */ -@Target({ ElementType.TYPE, ElementType.METHOD }) +@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/InvocationInterceptor.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/InvocationInterceptor.java index 91cf7e35c136..81bf9dc1fd32 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/InvocationInterceptor.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/InvocationInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,7 +11,6 @@ package org.junit.jupiter.api.extension; import static org.apiguardian.api.API.Status.DEPRECATED; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; import java.lang.reflect.Constructor; @@ -178,7 +177,7 @@ default void interceptDynamicTest(Invocation invocation, ExtensionContext * @param extensionContext the current extension context; never {@code null} * @throws Throwable in case of failures */ - @API(status = EXPERIMENTAL, since = "5.8") + @API(status = STABLE, since = "5.11") default void interceptDynamicTest(Invocation invocation, DynamicTestInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { // by default call the old interceptDynamicTest(Invocation, ExtensionContext) method so that existing extensions still work diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.java index 1852e1b9e8ec..de752e28eb1b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterContext.java index 88165ba8539f..d8ae7d8f18e0 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterContext.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterResolutionException.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterResolutionException.java index 5e34e82330ce..53865320a86d 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterResolutionException.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterResolutionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterResolver.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterResolver.java index 3ce4fdb4ba82..6678d72b898a 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterResolver.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ReflectiveInvocationContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ReflectiveInvocationContext.java index 1108fce671ea..ad074b3e745f 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ReflectiveInvocationContext.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ReflectiveInvocationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/RegisterExtension.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/RegisterExtension.java index 16c51bfd397d..eb84ab641a8f 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/RegisterExtension.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/RegisterExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -65,10 +65,10 @@ * *

Inheritance

* - *

{@code @RegisterExtension} fields are inherited from superclasses as long - * as they are not hidden or overridden. Furthermore, - * {@code @RegisterExtension} fields from superclasses will be registered before - * {@code @RegisterExtension} fields in subclasses. + *

{@code @RegisterExtension} fields are inherited from superclasses. + * Furthermore, {@code @RegisterExtension} fields from superclasses will be + * registered before {@code @RegisterExtension} fields in subclasses unless + * {@code @Order} is used to alter that behavior (see below). * *

Registration Order

* diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.java index cfd03f746b32..325ae97ad297 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstanceFactory.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstanceFactory.java index beb98895641b..a5e7e514c540 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstanceFactory.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstanceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstanceFactoryContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstanceFactoryContext.java index 99af882dfc06..25b8b76755f9 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstanceFactoryContext.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstanceFactoryContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePostProcessor.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePostProcessor.java index a2ec718e48e8..6b0cd8e59b17 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePostProcessor.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -23,7 +23,9 @@ * etc. * *

Extensions that implement {@code TestInstancePostProcessor} must be - * registered at the class level. + * registered at the class level, {@linkplain ExtendWith declaratively} via a + * field of the test class, or {@linkplain RegisterExtension programmatically} + * via a static field of the test class. * *

Constructor Requirements

* diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePreConstructCallback.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePreConstructCallback.java index 0bad10b8c447..933d7bc9d27b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePreConstructCallback.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePreConstructCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,7 @@ package org.junit.jupiter.api.extension; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.STABLE; import org.apiguardian.api.API; import org.junit.jupiter.api.TestInstance.Lifecycle; @@ -43,7 +43,7 @@ * @see ParameterResolver */ @FunctionalInterface -@API(status = EXPERIMENTAL, since = "5.9") +@API(status = STABLE, since = "5.11") public interface TestInstancePreConstructCallback extends Extension { /** diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePreDestroyCallback.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePreDestroyCallback.java index dbc9cb547b30..a7d27fee7681 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePreDestroyCallback.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePreDestroyCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstances.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstances.java index d2bab8067aa9..e38d33b678d3 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstances.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstances.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstantiationException.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstantiationException.java index 46ae5de4dba3..1cccc49a20e1 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstantiationException.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstantiationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestTemplateInvocationContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestTemplateInvocationContext.java index 268b5c474e52..c75c34ae4f97 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestTemplateInvocationContext.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestTemplateInvocationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestTemplateInvocationContextProvider.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestTemplateInvocationContextProvider.java index f7e0d61b31b6..851da77e7cd7 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestTemplateInvocationContextProvider.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestTemplateInvocationContextProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestWatcher.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestWatcher.java index a38ff8d088a9..176d5fa2fa68 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestWatcher.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestWatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolver.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolver.java index 2df41aed6a76..1eb5d6c89f53 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolver.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/Executable.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/Executable.java index c6309581f83e..c60bd15db337 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/Executable.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/Executable.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/ThrowingConsumer.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/ThrowingConsumer.java index a5f74507466f..5041f2515777 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/ThrowingConsumer.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/ThrowingConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/ThrowingSupplier.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/ThrowingSupplier.java index 573dffe70f12..892fa15f91d1 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/ThrowingSupplier.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/ThrowingSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/CleanupMode.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/CleanupMode.java index 59472e28dcf4..5a7a187278c0 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/CleanupMode.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/CleanupMode.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,7 @@ package org.junit.jupiter.api.io; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.STABLE; import org.apiguardian.api.API; @@ -25,7 +25,7 @@ * @since 5.9 * @see TempDir */ -@API(status = EXPERIMENTAL, since = "5.9") +@API(status = STABLE, since = "5.11") public enum CleanupMode { /** @@ -48,6 +48,6 @@ public enum CleanupMode { /** * Never clean up a temporary directory after the test has completed. */ - NEVER; + NEVER } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java index 519b14f1e2c8..55d9bbfcf56b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -40,11 +40,24 @@ * *

The temporary directory is only created if a field in a test class or a * parameter in a lifecycle method or test method is annotated with - * {@code @TempDir}. If the field type or parameter type is neither {@link Path} - * nor {@link File}, if a field is declared as {@code final}, or if the temporary - * directory cannot be created, an {@link ExtensionConfigurationException} or a - * {@link ParameterResolutionException} will be thrown as appropriate. In - * addition, a {@code ParameterResolutionException} will be thrown for a + * {@code @TempDir}. + * An {@link ExtensionConfigurationException} or a + * {@link ParameterResolutionException} will be thrown in one of the following + * cases: + * + *

    + *
  • If the field type or parameter type is neither {@link Path} nor + {@link File}.
  • + *
  • If a field is declared as {@code final}.
  • + *
  • If the temporary directory cannot be created.
  • + *
  • If the field type or parameter type is {@code File} and a custom + * {@linkplain TempDir#factory() factory} is used, which creates a temporary + * directory that does not belong to the + * {@linkplain java.nio.file.FileSystems#getDefault() default file system}. + *
  • + *
+ * + * In addition, a {@code ParameterResolutionException} will be thrown for a * constructor parameter annotated with {@code @TempDir}. * *

Scope

@@ -172,7 +185,7 @@ * * @since 5.9 */ - @API(status = EXPERIMENTAL, since = "5.9") + @API(status = STABLE, since = "5.11") CleanupMode cleanup() default CleanupMode.DEFAULT; } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDirFactory.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDirFactory.java index b7ffbc9411d8..d375482a759c 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDirFactory.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDirFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -73,7 +73,10 @@ default void close() throws IOException { /** * Standard {@link TempDirFactory} implementation which delegates to - * {@link Files#createTempDirectory} using {@code "junit"} as the prefix. + * {@link Files#createTempDirectory} using {@code "junit-,"} as prefix. + * + *

The created temporary directory is always created in the default + * file system with the system's default temporary directory as its parent. * * @see Files#createTempDirectory(java.lang.String, java.nio.file.attribute.FileAttribute[]) */ @@ -81,7 +84,7 @@ class Standard implements TempDirFactory { public static final TempDirFactory INSTANCE = new Standard(); - private static final String TEMP_DIR_PREFIX = "junit"; + private static final String TEMP_DIR_PREFIX = "junit-"; public Standard() { } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Execution.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Execution.java index cb1c8c01ffee..c2500e84ac4c 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Execution.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Execution.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -20,6 +20,8 @@ import java.lang.annotation.Target; import org.apiguardian.api.API; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.TestInstance; /** * {@code @Execution} is used to configure the parallel execution @@ -44,6 +46,12 @@ *

{@value #DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME} overrides * {@value #DEFAULT_EXECUTION_MODE_PROPERTY_NAME} for top-level classes. * + *

The default execution mode is not applied to classes that use the + * {@link TestInstance.Lifecycle#PER_CLASS PER_CLASS} lifecycle or a + * {@link MethodOrderer}. In both cases, test methods in such test classes are + * only executed concurrently if the {@code @Execution(CONCURRENT)} annotation + * is present on the test class or method. + * * @see Isolated * @see ResourceLock * @since 5.3 diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExecutionMode.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExecutionMode.java index b27ade10cec1..136a1a0054ec 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExecutionMode.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExecutionMode.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Isolated.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Isolated.java index 874fd984f844..8683cd470969 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Isolated.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Isolated.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceAccessMode.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceAccessMode.java index 9ada99fc741c..24cdc2240e21 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceAccessMode.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceAccessMode.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLock.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLock.java index 73064926404a..f9839322b37a 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLock.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -20,6 +20,8 @@ import java.lang.annotation.Target; import org.apiguardian.api.API; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; /** * {@code @ResourceLock} is used to declare that the annotated test class or test @@ -35,6 +37,12 @@ * methods that also require {@code READ} access but not at the same time as any * other test that requires {@code READ_WRITE} access. * + *

This guarantee extends to lifecycle methods of a test class or method. For + * example, if a test method is annotated with a {@code @ResourceLock} + * annotation the "lock" will be acquired before any + * {@link BeforeEach @BeforeEach} methods are executed and released after all + * {@link AfterEach @AfterEach} methods have been executed. + * *

This annotation can be repeated to declare the use of multiple shared resources. * *

Since JUnit Jupiter 5.4, this annotation is {@linkplain Inherited inherited} diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLocks.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLocks.java index 1e2c2548a729..80588aa9461b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLocks.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLocks.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Resources.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Resources.java index df5fcd7eb98a..c9fa33ff23f0 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Resources.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Resources.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/main/kotlin/org/junit/jupiter/api/Assertions.kt b/junit-jupiter-api/src/main/kotlin/org/junit/jupiter/api/Assertions.kt index d0f5af1e218b..709aaba4fb1d 100644 --- a/junit-jupiter-api/src/main/kotlin/org/junit/jupiter/api/Assertions.kt +++ b/junit-jupiter-api/src/main/kotlin/org/junit/jupiter/api/Assertions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -19,72 +19,79 @@ import org.junit.jupiter.api.function.ThrowingSupplier import java.time.Duration import java.util.function.Supplier import java.util.stream.Stream +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract /** * @see Assertions.fail */ -fun fail(message: String?, throwable: Throwable? = null): Nothing = - Assertions.fail(message, throwable) +fun fail( + message: String?, + throwable: Throwable? = null +): Nothing = Assertions.fail(message, throwable) /** * @see Assertions.fail */ -fun fail(message: (() -> String)?): Nothing = - Assertions.fail(message) +fun fail(message: (() -> String)?): Nothing = Assertions.fail(message) /** * @see Assertions.fail */ -fun fail(throwable: Throwable?): Nothing = - Assertions.fail(throwable) +fun fail(throwable: Throwable?): Nothing = Assertions.fail(throwable) /** * [Stream] of functions to be executed. */ private typealias ExecutableStream = Stream<() -> Unit> + private fun ExecutableStream.convert() = map { Executable(it) } /** * @see Assertions.assertAll */ -fun assertAll(executables: ExecutableStream) = - Assertions.assertAll(executables.convert()) +fun assertAll(executables: ExecutableStream) = Assertions.assertAll(executables.convert()) /** * @see Assertions.assertAll */ -fun assertAll(heading: String?, executables: ExecutableStream) = - Assertions.assertAll(heading, executables.convert()) +fun assertAll( + heading: String?, + executables: ExecutableStream +) = Assertions.assertAll(heading, executables.convert()) /** * [Collection] of functions to be executed. */ private typealias ExecutableCollection = Collection<() -> Unit> + private fun ExecutableCollection.convert() = map { Executable(it) } /** * @see Assertions.assertAll */ -fun assertAll(executables: ExecutableCollection) = - Assertions.assertAll(executables.convert()) +fun assertAll(executables: ExecutableCollection) = Assertions.assertAll(executables.convert()) /** * @see Assertions.assertAll */ -fun assertAll(heading: String?, executables: ExecutableCollection) = - Assertions.assertAll(heading, executables.convert()) +fun assertAll( + heading: String?, + executables: ExecutableCollection +) = Assertions.assertAll(heading, executables.convert()) /** * @see Assertions.assertAll */ -fun assertAll(vararg executables: () -> Unit) = - assertAll(executables.toList().stream()) +fun assertAll(vararg executables: () -> Unit) = assertAll(executables.toList().stream()) /** * @see Assertions.assertAll */ -fun assertAll(heading: String?, vararg executables: () -> Unit) = - assertAll(heading, executables.toList().stream()) +fun assertAll( + heading: String?, + vararg executables: () -> Unit +) = assertAll(heading, executables.toList().stream()) /** * Example usage: @@ -97,11 +104,12 @@ fun assertAll(heading: String?, vararg executables: () -> Unit) = * @see Assertions.assertThrows */ inline fun assertThrows(executable: () -> Unit): T { - val throwable: Throwable? = try { - executable() - } catch (caught: Throwable) { - caught - } as? Throwable + val throwable: Throwable? = + try { + executable() + } catch (caught: Throwable) { + caught + } as? Throwable return Assertions.assertThrows(T::class.java) { if (throwable != null) { @@ -120,8 +128,10 @@ inline fun assertThrows(executable: () -> Unit): T { * ``` * @see Assertions.assertThrows */ -inline fun assertThrows(message: String, executable: () -> Unit): T = - assertThrows({ message }, executable) +inline fun assertThrows( + message: String, + executable: () -> Unit +): T = assertThrows({ message }, executable) /** * Example usage: @@ -133,16 +143,20 @@ inline fun assertThrows(message: String, executable: () * ``` * @see Assertions.assertThrows */ -inline fun assertThrows(noinline message: () -> String, executable: () -> Unit): T { - val throwable: Throwable? = try { - executable() - } catch (caught: Throwable) { - caught - } as? Throwable +inline fun assertThrows( + noinline message: () -> String, + executable: () -> Unit +): T { + val throwable: Throwable? = + try { + executable() + } catch (caught: Throwable) { + caught + } as? Throwable return Assertions.assertThrows( T::class.java, - Executable { + { if (throwable != null) { throw throwable } @@ -161,9 +175,8 @@ inline fun assertThrows(noinline message: () -> String, * @see Assertions.assertDoesNotThrow * @param R the result type of the [executable] */ -@API(status = EXPERIMENTAL, since = "5.5") -inline fun assertDoesNotThrow(executable: () -> R): R = - Assertions.assertDoesNotThrow(evaluateAndWrap(executable)) +@API(status = STABLE, since = "5.11") +inline fun assertDoesNotThrow(executable: () -> R): R = Assertions.assertDoesNotThrow(evaluateAndWrap(executable)) /** * Example usage: @@ -175,9 +188,11 @@ inline fun assertDoesNotThrow(executable: () -> R): R = * @see Assertions.assertDoesNotThrow * @param R the result type of the [executable] */ -@API(status = EXPERIMENTAL, since = "5.5") -inline fun assertDoesNotThrow(message: String, executable: () -> R): R = - assertDoesNotThrow({ message }, executable) +@API(status = STABLE, since = "5.11") +inline fun assertDoesNotThrow( + message: String, + executable: () -> R +): R = assertDoesNotThrow({ message }, executable) /** * Example usage: @@ -189,20 +204,24 @@ inline fun assertDoesNotThrow(message: String, executable: () -> R): R = * @see Assertions.assertDoesNotThrow * @param R the result type of the [executable] */ -@API(status = EXPERIMENTAL, since = "5.5") -inline fun assertDoesNotThrow(noinline message: () -> String, executable: () -> R): R = +@API(status = STABLE, since = "5.11") +inline fun assertDoesNotThrow( + noinline message: () -> String, + executable: () -> R +): R = Assertions.assertDoesNotThrow( evaluateAndWrap(executable), Supplier(message) ) @PublishedApi -internal inline fun evaluateAndWrap(executable: () -> R): ThrowingSupplier = try { - val result = executable() - ThrowingSupplier { result } -} catch (throwable: Throwable) { - ThrowingSupplier { throw throwable } -} +internal inline fun evaluateAndWrap(executable: () -> R): ThrowingSupplier = + try { + val result = executable() + ThrowingSupplier { result } + } catch (throwable: Throwable) { + ThrowingSupplier { throw throwable } + } /** * Example usage: @@ -214,9 +233,11 @@ internal inline fun evaluateAndWrap(executable: () -> R): ThrowingSupplier assertTimeout(timeout: Duration, executable: () -> R): R = - Assertions.assertTimeout(timeout, executable) +@API(status = STABLE, since = "5.11") +fun assertTimeout( + timeout: Duration, + executable: () -> R +): R = Assertions.assertTimeout(timeout, executable) /** * Example usage: @@ -228,9 +249,12 @@ fun assertTimeout(timeout: Duration, executable: () -> R): R = * @see Assertions.assertTimeout * @paramR the result of the [executable]. */ -@API(status = EXPERIMENTAL, since = "5.5") -fun assertTimeout(timeout: Duration, message: String, executable: () -> R): R = - Assertions.assertTimeout(timeout, executable, message) +@API(status = STABLE, since = "5.11") +fun assertTimeout( + timeout: Duration, + message: String, + executable: () -> R +): R = Assertions.assertTimeout(timeout, executable, message) /** * Example usage: @@ -242,9 +266,12 @@ fun assertTimeout(timeout: Duration, message: String, executable: () -> R): * @see Assertions.assertTimeout * @paramR the result of the [executable]. */ -@API(status = EXPERIMENTAL, since = "5.5") -fun assertTimeout(timeout: Duration, message: () -> String, executable: () -> R): R = - Assertions.assertTimeout(timeout, executable, message) +@API(status = STABLE, since = "5.11") +fun assertTimeout( + timeout: Duration, + message: () -> String, + executable: () -> R +): R = Assertions.assertTimeout(timeout, executable, message) /** * Example usage: @@ -256,9 +283,11 @@ fun assertTimeout(timeout: Duration, message: () -> String, executable: () - * @see Assertions.assertTimeoutPreemptively * @paramR the result of the [executable]. */ -@API(status = EXPERIMENTAL, since = "5.5") -fun assertTimeoutPreemptively(timeout: Duration, executable: () -> R): R = - Assertions.assertTimeoutPreemptively(timeout, executable) +@API(status = STABLE, since = "5.11") +fun assertTimeoutPreemptively( + timeout: Duration, + executable: () -> R +): R = Assertions.assertTimeoutPreemptively(timeout, executable) /** * Example usage: @@ -270,9 +299,12 @@ fun assertTimeoutPreemptively(timeout: Duration, executable: () -> R): R = * @see Assertions.assertTimeoutPreemptively * @paramR the result of the [executable]. */ -@API(status = EXPERIMENTAL, since = "5.5") -fun assertTimeoutPreemptively(timeout: Duration, message: String, executable: () -> R): R = - Assertions.assertTimeoutPreemptively(timeout, executable, message) +@API(status = STABLE, since = "5.11") +fun assertTimeoutPreemptively( + timeout: Duration, + message: String, + executable: () -> R +): R = Assertions.assertTimeoutPreemptively(timeout, executable, message) /** * Example usage: @@ -284,6 +316,45 @@ fun assertTimeoutPreemptively(timeout: Duration, message: String, executable * @see Assertions.assertTimeoutPreemptively * @paramR the result of the [executable]. */ -@API(status = EXPERIMENTAL, since = "5.5") -fun assertTimeoutPreemptively(timeout: Duration, message: () -> String, executable: () -> R): R = - Assertions.assertTimeoutPreemptively(timeout, executable, message) +@API(status = STABLE, since = "5.11") +fun assertTimeoutPreemptively( + timeout: Duration, + message: () -> String, + executable: () -> R +): R = Assertions.assertTimeoutPreemptively(timeout, executable, message) + +/** + * Example usage: + * ```kotlin + * assertInstanceOf(list, "List should support fast random access") + * ``` + * @see Assertions.assertInstanceOf + * @since 5.11 + */ +@OptIn(ExperimentalContracts::class) +@API(status = EXPERIMENTAL, since = "5.11") +inline fun assertInstanceOf( + actualValue: Any?, + message: String? = null +): T { + contract { + returns() implies (actualValue is T) + } + return Assertions.assertInstanceOf(T::class.java, actualValue, message) +} + +/* + * @see Assertions.assertInstanceOf + * @since 5.11 + */ +@OptIn(ExperimentalContracts::class) +@API(status = EXPERIMENTAL, since = "5.11") +inline fun assertInstanceOf( + actualValue: Any?, + noinline message: () -> String +): T { + contract { + returns() implies (actualValue is T) + } + return Assertions.assertInstanceOf(T::class.java, actualValue, message) +} diff --git a/junit-jupiter-api/src/module/org.junit.jupiter.api/module-info.java b/junit-jupiter-api/src/module/org.junit.jupiter.api/module-info.java index b6856c78a11e..631d5331ae0a 100644 --- a/junit-jupiter-api/src/module/org.junit.jupiter.api/module-info.java +++ b/junit-jupiter-api/src/module/org.junit.jupiter.api/module-info.java @@ -19,6 +19,7 @@ exports org.junit.jupiter.api; exports org.junit.jupiter.api.condition; exports org.junit.jupiter.api.extension; + exports org.junit.jupiter.api.extension.support; exports org.junit.jupiter.api.function; exports org.junit.jupiter.api.io; exports org.junit.jupiter.api.parallel; diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/JRE.java b/junit-jupiter-api/src/templates/resources/main/org/junit/jupiter/api/condition/JRE.java.jte similarity index 52% rename from junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/JRE.java rename to junit-jupiter-api/src/templates/resources/main/org/junit/jupiter/api/condition/JRE.java.jte index ef868dfdc4b1..1a8598d9e980 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/JRE.java +++ b/junit-jupiter-api/src/templates/resources/main/org/junit/jupiter/api/condition/JRE.java.jte @@ -1,13 +1,10 @@ -/* - * Copyright 2015-2023 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ +@import java.util.List +@import gg.jte.support.ForSupport +@import junitbuild.generator.model.JRE +@param List jres +@param String licenseHeader +${licenseHeader} package org.junit.jupiter.api.condition; import static org.apiguardian.api.API.Status.STABLE; @@ -30,22 +27,10 @@ * {@linkplain #isCurrentVersion current JRE version}. * * @since 5.1 - * @see #JAVA_8 - * @see #JAVA_9 - * @see #JAVA_10 - * @see #JAVA_11 - * @see #JAVA_12 - * @see #JAVA_13 - * @see #JAVA_14 - * @see #JAVA_15 - * @see #JAVA_16 - * @see #JAVA_17 - * @see #JAVA_18 - * @see #JAVA_19 - * @see #JAVA_20 - * @see #JAVA_21 - * @see #JAVA_22 - * @see #OTHER +@for(JRE jre : jres)<%-- +--%> * @see #JAVA_${jre.getVersion()} +@endfor<%-- +--%> * @see #OTHER * @see EnabledOnJre * @see DisabledOnJre * @see EnabledForJreRange @@ -53,122 +38,28 @@ */ @API(status = STABLE, since = "5.1") public enum JRE { - - /** - * Java 8. - */ - JAVA_8, - - /** - * Java 9. - */ - JAVA_9, - - /** - * Java 10. - */ - JAVA_10, - - /** - * Java 11. - */ - JAVA_11, - - /** - * Java 12. - * - * @since 5.4 - */ - @API(status = STABLE, since = "5.4") - JAVA_12, - - /** - * Java 13. - * - * @since 5.4 - */ - @API(status = STABLE, since = "5.4") - JAVA_13, - - /** - * Java 14. - * - * @since 5.5 - */ - @API(status = STABLE, since = "5.5") - JAVA_14, - - /** - * Java 15. - * - * @since 5.6 - */ - @API(status = STABLE, since = "5.6") - JAVA_15, - - /** - * Java 16. - * - * @since 5.7 - */ - @API(status = STABLE, since = "5.7") - JAVA_16, - - /** - * Java 17. - * - * @since 5.7.1 - */ - @API(status = STABLE, since = "5.7.1") - JAVA_17, - - /** - * Java 18. - * - * @since 5.8.1 - */ - @API(status = STABLE, since = "5.8.1") - JAVA_18, - - /** - * Java 19. - * - * @since 5.9 - */ - @API(status = STABLE, since = "5.9") - JAVA_19, - - /** - * Java 20. - * - * @since 5.9 - */ - @API(status = STABLE, since = "5.9") - JAVA_20, - - /** - * Java 21. - * - * @since 5.9.2 - */ - @API(status = STABLE, since = "5.9.2") - JAVA_21, - - /** - * Java 22. - * - * @since 5.10 - */ - @API(status = STABLE, since = "5.10") - JAVA_22, - - /** - * A JRE version other than {@link #JAVA_8}, {@link #JAVA_9}, - * {@link #JAVA_10}, {@link #JAVA_11}, {@link #JAVA_12}, - * {@link #JAVA_13}, {@link #JAVA_14}, {@link #JAVA_15}, - * {@link #JAVA_16}, {@link #JAVA_17}, {@link #JAVA_18}, - * {@link #JAVA_19}, {@link #JAVA_20}, {@link #JAVA_21}, or - * {@link #JAVA_22}. +@for(var jre : jres) + /** + * Java ${jre.getVersion()}. + @if(jre.getSince() != null)<%-- +--%> * + * @since ${jre.getSince()} + @endif<%-- +--%> */ + @if(jre.getSince() != null)<%-- +--%>@API(status = STABLE, since = "${jre.getSince()}") + @endif<%-- +--%>JAVA_${jre.getVersion()}, +@endfor + /** + * A JRE version other than <%-- +--%>@for(var jre : ForSupport.of(jres))<%-- + --%>@if(jre.isLast())or @endif<%-- + --%>{@link #JAVA_${jre.get().getVersion()}}<%-- + --%>@if(jre.isLast()).@else,@endif<%-- + --%>@if(jre.getIndex() % 3 == 1 && !jre.isLast()) + * @elseif(!jre.isLast()) @endif<%-- +--%>@endfor */ OTHER; @@ -197,35 +88,13 @@ private static JRE determineCurrentVersion() { Object version = ReflectionUtils.invokeMethod(versionMethod, null); Method majorMethod = version.getClass().getMethod("major"); int major = (int) ReflectionUtils.invokeMethod(majorMethod, version); - switch (major) { - case 9: - return JAVA_9; - case 10: - return JAVA_10; - case 11: - return JAVA_11; - case 12: - return JAVA_12; - case 13: - return JAVA_13; - case 14: - return JAVA_14; - case 15: - return JAVA_15; - case 16: - return JAVA_16; - case 17: - return JAVA_17; - case 18: - return JAVA_18; - case 19: - return JAVA_19; - case 20: - return JAVA_20; - case 21: - return JAVA_21; - case 22: - return JAVA_22; + switch (major) {<%-- + --%>@for(var jre : jres)<%-- + --%>@if(jre.getVersion() != 8) + case ${jre.getVersion()}: + return JAVA_${jre.getVersion()};<%-- + --%>@endif<%-- + --%>@endfor default: return OTHER; } diff --git a/junit-jupiter-api/src/templates/resources/testFixtures/org/junit/jupiter/api/condition/JavaVersionPredicates.java.jte b/junit-jupiter-api/src/templates/resources/testFixtures/org/junit/jupiter/api/condition/JavaVersionPredicates.java.jte new file mode 100644 index 000000000000..de731bb4cd8f --- /dev/null +++ b/junit-jupiter-api/src/templates/resources/testFixtures/org/junit/jupiter/api/condition/JavaVersionPredicates.java.jte @@ -0,0 +1,22 @@ +@import java.util.List +@import gg.jte.support.ForSupport +@import junitbuild.generator.model.JRE + +@param List jres +@param String licenseHeader +${licenseHeader} +package org.junit.jupiter.api.condition; + +public class JavaVersionPredicates { + + private static final String JAVA_VERSION = System.getProperty("java.version"); +@for(JRE jre : jres) + static boolean onJava${jre.getVersion()}() { + return JAVA_VERSION.startsWith("@if(jre.getVersion() == 8)1.@endif${jre.getVersion()}"); + } +@endfor + static boolean onKnownVersion() { + return @for(var jre : ForSupport.of(jres))onJava${jre.get().getVersion()}()@if(!jre.isLast()) // + || @endif@endfor; + } +} diff --git a/junit-jupiter-api/src/test/README.md b/junit-jupiter-api/src/test/README.md new file mode 100644 index 000000000000..86ed9e49af1d --- /dev/null +++ b/junit-jupiter-api/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `jupiter-tests` project. diff --git a/junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/extension/DisabledOnOpenJ9.java b/junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/extension/DisabledOnOpenJ9.java new file mode 100644 index 000000000000..965de2b7d255 --- /dev/null +++ b/junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/extension/DisabledOnOpenJ9.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.api.extension; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; + +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@DisabledIfSystemProperty(named = "java.vm.vendor", matches = ".*OpenJ9.*") +public @interface DisabledOnOpenJ9 { +} diff --git a/junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/extension/ExtensionContextParameterResolver.java b/junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/extension/ExtensionContextParameterResolver.java index 836aabab88f8..a68e59f37d1d 100644 --- a/junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/extension/ExtensionContextParameterResolver.java +++ b/junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/extension/ExtensionContextParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/fixtures/TrackLogRecords.java b/junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/fixtures/TrackLogRecords.java index ef33de69dda6..1abdd36632a8 100644 --- a/junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/fixtures/TrackLogRecords.java +++ b/junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/fixtures/TrackLogRecords.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -41,7 +41,7 @@ * @see LoggerFactory * @see LogRecordListener */ -@Target({ ElementType.TYPE, ElementType.METHOD }) +@Target({ ElementType.TYPE, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) @ExtendWith(TrackLogRecords.Extension.class) public @interface TrackLogRecords { diff --git a/junit-jupiter-engine/junit-jupiter-engine.gradle.kts b/junit-jupiter-engine/junit-jupiter-engine.gradle.kts index 26b4b2664e20..04d86e5f0da7 100644 --- a/junit-jupiter-engine/junit-jupiter-engine.gradle.kts +++ b/junit-jupiter-engine/junit-jupiter-engine.gradle.kts @@ -1,9 +1,5 @@ -import org.gradle.api.tasks.PathSensitivity.RELATIVE - plugins { id("junitbuild.kotlin-library-conventions") - id("junitbuild.testing-conventions") - groovy `java-test-fixtures` } @@ -16,26 +12,10 @@ dependencies { compileOnlyApi(libs.apiguardian) - testImplementation(projects.junitPlatformLauncher) - testImplementation(projects.junitPlatformSuiteEngine) - testImplementation(projects.junitPlatformTestkit) - testImplementation(testFixtures(projects.junitPlatformCommons)) - testImplementation(kotlin("stdlib")) - testImplementation(libs.jimfs) - testImplementation(libs.junit4) - testImplementation(libs.kotlinx.coroutines) - testImplementation(libs.groovy4) - testImplementation(libs.memoryfilesystem) - testImplementation(testFixtures(projects.junitJupiterApi)) - osgiVerification(projects.junitPlatformLauncher) } tasks { - test { - inputs.dir("src/test/resources").withPathSensitivity(RELATIVE) - systemProperty("developmentVersion", version) - } jar { bundle { val platformVersion: String by rootProject.extra diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java index 2c7b509c9964..1b0636f18d10 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/JupiterTestEngine.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/JupiterTestEngine.java index dbd799b7f5b2..94b13afd4548 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/JupiterTestEngine.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/JupiterTestEngine.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/CachingJupiterConfiguration.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/CachingJupiterConfiguration.java index 2d61b58c1c32..1280c4b12a11 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/CachingJupiterConfiguration.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/CachingJupiterConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/DefaultJupiterConfiguration.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/DefaultJupiterConfiguration.java index d64c4ceee318..83ef7592534f 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/DefaultJupiterConfiguration.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/DefaultJupiterConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/EnumConfigurationParameterConverter.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/EnumConfigurationParameterConverter.java index 75fd3f99f708..16e13ed18a4a 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/EnumConfigurationParameterConverter.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/EnumConfigurationParameterConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverter.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverter.java index ddc5acd121f7..36727edfd0ad 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverter.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/JupiterConfiguration.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/JupiterConfiguration.java index 559b4d7d5715..7a90072363d2 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/JupiterConfiguration.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/JupiterConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/AbstractExtensionContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/AbstractExtensionContext.java index 58ff7c73936b..a1772e796146 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/AbstractExtensionContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/AbstractExtensionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -55,8 +55,9 @@ abstract class AbstractExtensionContext implements Ext private final ExecutableInvoker executableInvoker; AbstractExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener, T testDescriptor, - JupiterConfiguration configuration, ExecutableInvoker executableInvoker) { - this.executableInvoker = executableInvoker; + JupiterConfiguration configuration, + Function executableInvokerFactory) { + this.executableInvoker = executableInvokerFactory.apply(this); Preconditions.notNull(testDescriptor, "TestDescriptor must not be null"); Preconditions.notNull(configuration, "JupiterConfiguration must not be null"); @@ -74,7 +75,7 @@ abstract class AbstractExtensionContext implements Ext // @formatter:on } - private NamespacedHierarchicalStore createStore(ExtensionContext parent) { + private static NamespacedHierarchicalStore createStore(ExtensionContext parent) { NamespacedHierarchicalStore parentStore = null; if (parent != null) { parentStore = ((AbstractExtensionContext) parent).valuesStore; diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassBasedTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassBasedTestDescriptor.java index 6005c99deb34..baa884c18e7e 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassBasedTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassBasedTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -15,7 +15,8 @@ import static org.junit.jupiter.engine.descriptor.ExtensionUtils.populateNewExtensionRegistryFromExtendWithAnnotation; import static org.junit.jupiter.engine.descriptor.ExtensionUtils.registerExtensionsFromConstructorParameters; import static org.junit.jupiter.engine.descriptor.ExtensionUtils.registerExtensionsFromExecutableParameters; -import static org.junit.jupiter.engine.descriptor.ExtensionUtils.registerExtensionsFromFields; +import static org.junit.jupiter.engine.descriptor.ExtensionUtils.registerExtensionsFromInstanceFields; +import static org.junit.jupiter.engine.descriptor.ExtensionUtils.registerExtensionsFromStaticFields; import static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findAfterAllMethods; import static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findAfterEachMethods; import static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findBeforeAllMethods; @@ -38,7 +39,6 @@ import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.ExecutableInvoker; import org.junit.jupiter.api.extension.Extension; import org.junit.jupiter.api.extension.ExtensionConfigurationException; import org.junit.jupiter.api.extension.ExtensionContext; @@ -152,7 +152,7 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte // Register extensions from static fields here, at the class level but // after extensions registered via @ExtendWith. - registerExtensionsFromFields(registry, this.testClass, null); + registerExtensionsFromStaticFields(registry, this.testClass); // Resolve the TestInstanceFactory at the class level in order to fail // the entire class in case of configuration errors (e.g., more than @@ -175,12 +175,12 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte registerBeforeEachMethodAdapters(registry); registerAfterEachMethodAdapters(registry); this.afterAllMethods.forEach(method -> registerExtensionsFromExecutableParameters(registry, method)); + registerExtensionsFromInstanceFields(registry, this.testClass); ThrowableCollector throwableCollector = createThrowableCollector(); - ExecutableInvoker executableInvoker = new DefaultExecutableInvoker(context); ClassExtensionContext extensionContext = new ClassExtensionContext(context.getExtensionContext(), context.getExecutionListener(), this, this.lifecycle, context.getConfiguration(), throwableCollector, - executableInvoker); + it -> new DefaultExecutableInvoker(it, registry)); // @formatter:off return context.extend() @@ -288,10 +288,10 @@ private TestInstances instantiateAndPostProcessTestInstance(JupiterEngineExecuti throwableCollector); throwableCollector.execute(() -> { invokeTestInstancePostProcessors(instances.getInnermostInstance(), registry, extensionContext); - // In addition, we register extensions from instance fields here since the - // best time to do that is immediately following test class instantiation - // and post processing. - registerExtensionsFromFields(registrar, this.testClass, instances.getInnermostInstance()); + // In addition, we initialize extension registered programmatically from instance fields here + // since the best time to do that is immediately following test class instantiation + // and post-processing. + registrar.initializeExtensions(this.testClass, instances.getInnermostInstance()); }); return instances; } @@ -383,7 +383,7 @@ private void executeAndMaskThrowable(Executable executable) { executable.execute(); } catch (Throwable throwable) { - ExceptionUtils.throwAsUncheckedException(throwable); + throw ExceptionUtils.throwAsUncheckedException(throwable); } } diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassExtensionContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassExtensionContext.java index 57d40a86edbb..ddeb750dbb29 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassExtensionContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassExtensionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,6 +13,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.Optional; +import java.util.function.Function; import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.ExecutableInvoker; @@ -38,21 +39,23 @@ final class ClassExtensionContext extends AbstractExtensionContext executableInvokerFactory) { this(parent, engineExecutionListener, testDescriptor, Lifecycle.PER_METHOD, configuration, throwableCollector, - executableInvoker); + executableInvokerFactory); } ClassExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener, ClassBasedTestDescriptor testDescriptor, Lifecycle lifecycle, JupiterConfiguration configuration, - ThrowableCollector throwableCollector, ExecutableInvoker executableInvoker) { + ThrowableCollector throwableCollector, + Function executableInvokerFactory) { - super(parent, engineExecutionListener, testDescriptor, configuration, executableInvoker); + super(parent, engineExecutionListener, testDescriptor, configuration, executableInvokerFactory); this.lifecycle = lifecycle; this.throwableCollector = throwableCollector; diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTestDescriptor.java index 2296129809d8..4b840a0d6057 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DefaultDynamicTestInvocationContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DefaultDynamicTestInvocationContext.java index f6e272bc3e99..1c3337224a6b 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DefaultDynamicTestInvocationContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DefaultDynamicTestInvocationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DefaultTestInstanceFactoryContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DefaultTestInstanceFactoryContext.java index affdf5b57545..ad8bf3b33c55 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DefaultTestInstanceFactoryContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DefaultTestInstanceFactoryContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DisplayNameUtils.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DisplayNameUtils.java index 168f09353224..ec36ab6fb881 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DisplayNameUtils.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DisplayNameUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicContainerTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicContainerTestDescriptor.java index a72981df70d3..2ca4525dbf10 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicContainerTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicContainerTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicDescendantFilter.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicDescendantFilter.java index 48e45e0bb11d..b94a1eda75e5 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicDescendantFilter.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicDescendantFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicExtensionContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicExtensionContext.java index 92ec7b24abeb..0bc5c0542167 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicExtensionContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicExtensionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,6 +13,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.Optional; +import java.util.function.Function; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.ExecutableInvoker; @@ -26,8 +27,8 @@ class DynamicExtensionContext extends AbstractExtensionContext executableInvokerFactory) { + super(parent, engineExecutionListener, testDescriptor, configuration, executableInvokerFactory); } @Override diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicNodeTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicNodeTestDescriptor.java index bf5bde6c63dd..ba07a11b6ec4 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicNodeTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicNodeTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -46,7 +46,8 @@ public String getLegacyReportingName() { @Override public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) { DynamicExtensionContext extensionContext = new DynamicExtensionContext(context.getExtensionContext(), - context.getExecutionListener(), this, context.getConfiguration(), new DefaultExecutableInvoker(context)); + context.getExecutionListener(), this, context.getConfiguration(), + it -> new DefaultExecutableInvoker(it, context.getExtensionRegistry())); // @formatter:off return context.extend() .withExtensionContext(extensionContext) diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicTestTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicTestTestDescriptor.java index 3fc75d00c0ed..9a1729a3aed2 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicTestTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicTestTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ExtensionUtils.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ExtensionUtils.java index a5d576cf508d..ad503e0e81dd 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ExtensionUtils.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ExtensionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -35,6 +35,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.engine.extension.ExtensionRegistrar; import org.junit.jupiter.engine.extension.MutableExtensionRegistry; +import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.util.Preconditions; import org.junit.platform.commons.util.ReflectionUtils; @@ -71,60 +72,94 @@ static MutableExtensionRegistry populateNewExtensionRegistryFromExtendWithAnnota Preconditions.notNull(parentRegistry, "Parent ExtensionRegistry must not be null"); Preconditions.notNull(annotatedElement, "AnnotatedElement must not be null"); - return MutableExtensionRegistry.createRegistryFrom(parentRegistry, streamExtensionTypes(annotatedElement)); + return MutableExtensionRegistry.createRegistryFrom(parentRegistry, + streamDeclarativeExtensionTypes(annotatedElement)); } /** - * Register extensions using the supplied registrar from fields in the supplied - * class that are annotated with {@link ExtendWith @ExtendWith} or - * {@link RegisterExtension @RegisterExtension}. + * Register extensions using the supplied registrar from static fields in + * the supplied class that are annotated with {@link ExtendWith @ExtendWith} + * or {@link RegisterExtension @RegisterExtension}. * *

The extensions will be sorted according to {@link Order @Order} semantics * prior to registration. * * @param registrar the registrar with which to register the extensions; never {@code null} * @param clazz the class or interface in which to find the fields; never {@code null} - * @param instance the instance of the supplied class; may be {@code null} - * when searching for {@code static} fields in the class + * @since 5.11 */ - static void registerExtensionsFromFields(ExtensionRegistrar registrar, Class clazz, Object instance) { - Preconditions.notNull(registrar, "ExtensionRegistrar must not be null"); - Preconditions.notNull(clazz, "Class must not be null"); + static void registerExtensionsFromStaticFields(ExtensionRegistrar registrar, Class clazz) { + streamExtensionRegisteringFields(clazz, ReflectionUtils::isStatic) // + .forEach(field -> { + List> extensionTypes = streamDeclarativeExtensionTypes(field).collect( + toList()); + boolean isExtendWithPresent = !extensionTypes.isEmpty(); - Predicate predicate = (instance == null ? ReflectionUtils::isStatic : ReflectionUtils::isNotStatic); + if (isExtendWithPresent) { + extensionTypes.forEach(registrar::registerExtension); + } + if (isAnnotated(field, RegisterExtension.class)) { + Extension extension = readAndValidateExtensionFromField(field, null, extensionTypes); + registrar.registerExtension(extension, field); + } + }); + } - streamFields(clazz, predicate, TOP_DOWN)// - .sorted(orderComparator)// + /** + * Register extensions using the supplied registrar from instance fields in + * the supplied class that are annotated with {@link ExtendWith @ExtendWith} + * or {@link RegisterExtension @RegisterExtension}. + * + *

The extensions will be sorted according to {@link Order @Order} semantics + * prior to registration. + * + * @param registrar the registrar with which to register the extensions; never {@code null} + * @param clazz the class or interface in which to find the fields; never {@code null} + * @since 5.11 + */ + static void registerExtensionsFromInstanceFields(ExtensionRegistrar registrar, Class clazz) { + streamExtensionRegisteringFields(clazz, ReflectionUtils::isNotStatic) // .forEach(field -> { - List> extensionTypes = streamExtensionTypes(field).collect(toList()); + List> extensionTypes = streamDeclarativeExtensionTypes(field).collect( + toList()); boolean isExtendWithPresent = !extensionTypes.isEmpty(); - boolean isRegisterExtensionPresent = isAnnotated(field, RegisterExtension.class); + if (isExtendWithPresent) { extensionTypes.forEach(registrar::registerExtension); } - if (isRegisterExtensionPresent) { - tryToReadFieldValue(field, instance).ifSuccess(value -> { - Preconditions.condition(value instanceof Extension, () -> String.format( - "Failed to register extension via @RegisterExtension field [%s]: field value's type [%s] must implement an [%s] API.", - field, (value != null ? value.getClass().getName() : null), Extension.class.getName())); - - if (isExtendWithPresent) { - Class valueType = value.getClass(); - extensionTypes.forEach(extensionType -> { - Preconditions.condition(!extensionType.equals(valueType), - () -> String.format("Failed to register extension via field [%s]. " - + "The field registers an extension of type [%s] via @RegisterExtension and @ExtendWith, " - + "but only one registration of a given extension type is permitted.", - field, valueType.getName())); - }); - } - - registrar.registerExtension((Extension) value, field); - }); + if (isAnnotated(field, RegisterExtension.class)) { + registrar.registerUninitializedExtension(clazz, field, + instance -> readAndValidateExtensionFromField(field, instance, extensionTypes)); } }); } + /** + * @since 5.11 + */ + private static Extension readAndValidateExtensionFromField(Field field, Object instance, + List> declarativeExtensionTypes) { + Object value = tryToReadFieldValue(field, instance) // + .getOrThrow(e -> new PreconditionViolationException( + String.format("Failed to read @RegisterExtension field [%s]", field), e)); + + Preconditions.condition(value instanceof Extension, () -> String.format( + "Failed to register extension via @RegisterExtension field [%s]: field value's type [%s] must implement an [%s] API.", + field, (value != null ? value.getClass().getName() : null), Extension.class.getName())); + + declarativeExtensionTypes.forEach(extensionType -> { + Class valueType = value.getClass(); + Preconditions.condition(!extensionType.equals(valueType), + () -> String.format( + "Failed to register extension via field [%s]. " + + "The field registers an extension of type [%s] via @RegisterExtension and @ExtendWith, " + + "but only one registration of a given extension type is permitted.", + field, valueType.getName())); + }); + + return (Extension) value; + } + /** * Register extensions using the supplied registrar from parameters in the * declared constructor of the supplied class that are annotated with @@ -157,22 +192,32 @@ static void registerExtensionsFromExecutableParameters(ExtensionRegistrar regist // @formatter:off Arrays.stream(executable.getParameters()) .map(parameter -> findRepeatableAnnotations(parameter, index.getAndIncrement(), ExtendWith.class)) - .flatMap(ExtensionUtils::streamExtensionTypes) + .flatMap(ExtensionUtils::streamDeclarativeExtensionTypes) .forEach(registrar::registerExtension); // @formatter:on } /** - * @since 5.8 + * @since 5.11 */ - private static Stream> streamExtensionTypes(AnnotatedElement annotatedElement) { - return streamExtensionTypes(findRepeatableAnnotations(annotatedElement, ExtendWith.class)); + private static Stream streamExtensionRegisteringFields(Class clazz, Predicate predicate) { + return streamFields(clazz, predicate.and(registersExtension), TOP_DOWN)// + .sorted(orderComparator); } /** - * @since 5.8 + * @since 5.11 */ - private static Stream> streamExtensionTypes(List extendWithAnnotations) { + private static Stream> streamDeclarativeExtensionTypes( + AnnotatedElement annotatedElement) { + return streamDeclarativeExtensionTypes(findRepeatableAnnotations(annotatedElement, ExtendWith.class)); + } + + /** + * @since 5.11 + */ + private static Stream> streamDeclarativeExtensionTypes( + List extendWithAnnotations) { return extendWithAnnotations.stream().map(ExtendWith::value).flatMap(Arrays::stream); } @@ -189,4 +234,14 @@ private static int getOrder(Field field) { return findAnnotation(field, Order.class).map(Order::value).orElse(Order.DEFAULT); } + /** + * {@link Predicate} which determines if a {@link Field} registers an extension via + * {@link RegisterExtension @RegisterExtension} or {@link ExtendWith @ExtendWith}. + * + * @since 5.11.3 + */ + private static final Predicate registersExtension = // + field -> isAnnotated(field, RegisterExtension.class) + || !findRepeatableAnnotations(field, ExtendWith.class).isEmpty(); + } diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/Filterable.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/Filterable.java index a9ca7ad14dc4..61f24b9b070b 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/Filterable.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/Filterable.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterEngineDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterEngineDescriptor.java index 3a87753ca530..0f87b7a182b2 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterEngineDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterEngineDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -14,7 +14,6 @@ import static org.junit.jupiter.engine.descriptor.JupiterTestDescriptor.toExecutionMode; import org.apiguardian.api.API; -import org.junit.jupiter.api.extension.ExecutableInvoker; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.engine.config.JupiterConfiguration; import org.junit.jupiter.engine.execution.DefaultExecutableInvoker; @@ -53,9 +52,8 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte MutableExtensionRegistry extensionRegistry = MutableExtensionRegistry.createRegistryWithDefaultExtensions( context.getConfiguration()); EngineExecutionListener executionListener = context.getExecutionListener(); - ExecutableInvoker executableInvoker = new DefaultExecutableInvoker(context); ExtensionContext extensionContext = new JupiterEngineExtensionContext(executionListener, this, - context.getConfiguration(), executableInvoker); + context.getConfiguration(), it -> new DefaultExecutableInvoker(it, extensionRegistry)); // @formatter:off return context.extend() diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterEngineExtensionContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterEngineExtensionContext.java index 26eb83f7fa50..988dc8ea0254 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterEngineExtensionContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterEngineExtensionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,9 +13,11 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.Optional; +import java.util.function.Function; import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.ExecutableInvoker; +import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.TestInstances; import org.junit.jupiter.engine.config.JupiterConfiguration; import org.junit.platform.engine.EngineExecutionListener; @@ -28,9 +30,9 @@ final class JupiterEngineExtensionContext extends AbstractExtensionContext executableInvokerFactory) { - super(null, engineExecutionListener, testDescriptor, configuration, executableInvoker); + super(null, engineExecutionListener, testDescriptor, configuration, executableInvokerFactory); } @Override diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptor.java index 541844b0da8f..4d975ce4b684 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -117,7 +117,7 @@ private void invokeExecutionExceptionHandlers(List exce // No handlers left? if (exceptionHandlers.isEmpty()) { - ExceptionUtils.throwAsUncheckedException(throwable); + throw ExceptionUtils.throwAsUncheckedException(throwable); } try { diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtils.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtils.java index 9adb38ff3945..2d4cea214f3d 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtils.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,7 +11,7 @@ package org.junit.jupiter.engine.descriptor; import static org.junit.platform.commons.util.AnnotationUtils.findAnnotatedMethods; -import static org.junit.platform.commons.util.ReflectionUtils.returnsVoid; +import static org.junit.platform.commons.util.ReflectionUtils.returnsPrimitiveVoid; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -96,7 +96,7 @@ private static void assertNonStatic(Class annotationType, } private static void assertVoid(Class annotationType, Method method) { - if (!returnsVoid(method)) { + if (!returnsPrimitiveVoid(method)) { throw new JUnitException(String.format("@%s method '%s' must not return a value.", annotationType.getSimpleName(), method.toGenericString())); } diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodBasedTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodBasedTestDescriptor.java index d3c523a39f0e..8c851955c5b2 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodBasedTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodBasedTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodExtensionContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodExtensionContext.java index 22db33b33de3..6c5e2efc6fbf 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodExtensionContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodExtensionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,6 +13,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.Optional; +import java.util.function.Function; import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.ExecutableInvoker; @@ -34,9 +35,10 @@ final class MethodExtensionContext extends AbstractExtensionContext executableInvokerFactory) { - super(parent, engineExecutionListener, testDescriptor, configuration, executableInvoker); + super(parent, engineExecutionListener, testDescriptor, configuration, executableInvokerFactory); this.throwableCollector = throwableCollector; } diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodSourceSupport.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodSourceSupport.java index fd461ba479cf..f5ebe602576f 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodSourceSupport.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodSourceSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/NestedClassTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/NestedClassTestDescriptor.java index f750e2f5c432..84ce0645b7d3 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/NestedClassTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/NestedClassTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptor.java index 671cc6b63fc6..1a86ba2e8d9a 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtils.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtils.java index 8aae97f9b754..56378f766684 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtils.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestMethodTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestMethodTestDescriptor.java index 252714e266c4..b4f874966522 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestMethodTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestMethodTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -24,7 +24,6 @@ import org.junit.jupiter.api.extension.AfterTestExecutionCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; -import org.junit.jupiter.api.extension.ExecutableInvoker; import org.junit.jupiter.api.extension.Extension; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.InvocationInterceptor; @@ -99,9 +98,9 @@ public Type getType() { public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) { MutableExtensionRegistry registry = populateNewExtensionRegistry(context); ThrowableCollector throwableCollector = createThrowableCollector(); - ExecutableInvoker executableInvoker = new DefaultExecutableInvoker(context); MethodExtensionContext extensionContext = new MethodExtensionContext(context.getExtensionContext(), - context.getExecutionListener(), this, context.getConfiguration(), throwableCollector, executableInvoker); + context.getExecutionListener(), this, context.getConfiguration(), throwableCollector, + it -> new DefaultExecutableInvoker(it, registry)); throwableCollector.execute(() -> { TestInstances testInstances = context.getTestInstancesProvider().getTestInstances(registry, throwableCollector); diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateExtensionContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateExtensionContext.java index 5a3506f8ea6a..ae4f92b8195f 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateExtensionContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateExtensionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,6 +13,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.Optional; +import java.util.function.Function; import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.ExecutableInvoker; @@ -31,9 +32,9 @@ final class TestTemplateExtensionContext extends AbstractExtensionContext executableInvokerFactory) { - super(parent, engineExecutionListener, testDescriptor, configuration, executableInvoker); + super(parent, engineExecutionListener, testDescriptor, configuration, executableInvokerFactory); this.testInstances = testInstances; } diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptor.java index ab45e058dfca..32d627dff049 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptor.java index 885a07d93636..aa22bcbe70aa 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -21,7 +21,6 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apiguardian.api.API; -import org.junit.jupiter.api.extension.ExecutableInvoker; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.TestInstances; import org.junit.jupiter.api.extension.TestTemplateInvocationContext; @@ -74,16 +73,16 @@ public boolean mayRegisterTests() { // --- Node ---------------------------------------------------------------- @Override - public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) throws Exception { + public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) { MutableExtensionRegistry registry = populateNewExtensionRegistryFromExtendWithAnnotation( context.getExtensionRegistry(), getTestMethod()); // The test instance should be properly maintained by the enclosing class's ExtensionContext. TestInstances testInstances = context.getExtensionContext().getTestInstances().orElse(null); - ExecutableInvoker executableInvoker = new DefaultExecutableInvoker(context); ExtensionContext extensionContext = new TestTemplateExtensionContext(context.getExtensionContext(), - context.getExecutionListener(), this, context.getConfiguration(), testInstances, executableInvoker); + context.getExecutionListener(), this, context.getConfiguration(), testInstances, + it -> new DefaultExecutableInvoker(it, registry)); // @formatter:off return context.extend() diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractAnnotatedDescriptorWrapper.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractAnnotatedDescriptorWrapper.java index 31666b8c2ecb..73ad78fe1d93 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractAnnotatedDescriptorWrapper.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractAnnotatedDescriptorWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractOrderingVisitor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractOrderingVisitor.java index e19e5c967d82..4e014b166bb8 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractOrderingVisitor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractOrderingVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassOrderingVisitor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassOrderingVisitor.java index 0b8ea6d09c13..142f7f2c08e0 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassOrderingVisitor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassOrderingVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassSelectorResolver.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassSelectorResolver.java index 1a597810d628..e63656017ad0 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassSelectorResolver.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassSelectorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultClassDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultClassDescriptor.java index b11c1b0270e0..fd51f71b14a8 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultClassDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultClassDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultClassOrdererContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultClassOrdererContext.java index 2c8c8743bca2..f9646db799c5 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultClassOrdererContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultClassOrdererContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultMethodDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultMethodDescriptor.java index a8cdd12c575a..66931bebb06f 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultMethodDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultMethodDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultMethodOrdererContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultMethodOrdererContext.java index fd51d4b7cd64..de10c94a6ba7 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultMethodOrdererContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultMethodOrdererContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolver.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolver.java index 42771f96d1a7..2bf17487949b 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolver.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -21,10 +21,12 @@ /** * {@code DiscoverySelectorResolver} resolves {@link TestDescriptor TestDescriptors} - * for containers and tests selected by DiscoverySelectors with the help of the - * {@code JavaElementsResolver}. + * for containers and tests selected by {@link org.junit.platform.engine.DiscoverySelector + * DiscoverySelectors}, with the help of an {@link EngineDiscoveryRequestResolver}. * - *

This class is the only public entry point into the discovery package. + *

This is an internal utility which is only {@code public} in order to provide + * the {@link org.junit.jupiter.engine.JupiterTestEngine JupiterTestEngine} access + * to the functionality of the {@code discovery} package. * * @since 5.0 */ diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodFinder.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodFinder.java index 648ad453b3dd..2b2adf3e12b5 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodFinder.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodFinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodOrderingVisitor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodOrderingVisitor.java index e1e7c8afab03..f7d997a5a727 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodOrderingVisitor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodOrderingVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodSelectorResolver.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodSelectorResolver.java index 62698001ee5b..57df96514da7 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodSelectorResolver.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodSelectorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsInnerClass.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsInnerClass.java index c46822be0edc..30adff839a04 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsInnerClass.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsInnerClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsNestedTestClass.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsNestedTestClass.java index ceaa9f3de12b..0039082f1609 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsNestedTestClass.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsNestedTestClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsPotentialTestContainer.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsPotentialTestContainer.java index 525e9aaf6aa3..8e2074445949 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsPotentialTestContainer.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsPotentialTestContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestClassWithTests.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestClassWithTests.java index edec7724a83e..49528f285d6d 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestClassWithTests.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestClassWithTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethod.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethod.java index 186d5ab6333f..7a7f5549e7aa 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethod.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethod.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethod.java index 9d30765d1ce5..3f4add6be2f1 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethod.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethod.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethod.java index 70a4391a6912..576188582630 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethod.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestableMethod.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestableMethod.java index e8906f0d55ac..7796a047a5f5 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestableMethod.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestableMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -14,7 +14,7 @@ import static org.junit.platform.commons.util.ReflectionUtils.isAbstract; import static org.junit.platform.commons.util.ReflectionUtils.isPrivate; import static org.junit.platform.commons.util.ReflectionUtils.isStatic; -import static org.junit.platform.commons.util.ReflectionUtils.returnsVoid; +import static org.junit.platform.commons.util.ReflectionUtils.returnsPrimitiveVoid; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -26,11 +26,11 @@ abstract class IsTestableMethod implements Predicate { private final Class annotationType; - private final boolean mustReturnVoid; + private final boolean mustReturnPrimitiveVoid; - IsTestableMethod(Class annotationType, boolean mustReturnVoid) { + IsTestableMethod(Class annotationType, boolean mustReturnPrimitiveVoid) { this.annotationType = annotationType; - this.mustReturnVoid = mustReturnVoid; + this.mustReturnPrimitiveVoid = mustReturnPrimitiveVoid; } @Override @@ -45,7 +45,7 @@ public boolean test(Method candidate) { if (isAbstract(candidate)) { return false; } - if (returnsVoid(candidate) != this.mustReturnVoid) { + if (returnsPrimitiveVoid(candidate) != this.mustReturnPrimitiveVoid) { return false; } diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/AfterEachMethodAdapter.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/AfterEachMethodAdapter.java index 2b9dff6876d3..32fa951c6330 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/AfterEachMethodAdapter.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/AfterEachMethodAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/BeforeEachMethodAdapter.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/BeforeEachMethodAdapter.java index 709e35e80759..2ab5db2695c2 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/BeforeEachMethodAdapter.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/BeforeEachMethodAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConditionEvaluationException.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConditionEvaluationException.java index ddafc0a4ad21..e44bf3f2322c 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConditionEvaluationException.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConditionEvaluationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConditionEvaluator.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConditionEvaluator.java index fb5e0ff2ace8..9c200e0d4423 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConditionEvaluator.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConditionEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConstructorInvocation.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConstructorInvocation.java index 4100d667a780..e2230d139430 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConstructorInvocation.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConstructorInvocation.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultExecutableInvoker.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultExecutableInvoker.java index 2276cc94273b..18b4c6fdf9fd 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultExecutableInvoker.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultExecutableInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -37,10 +37,6 @@ public DefaultExecutableInvoker(ExtensionContext extensionContext, ExtensionRegi this.extensionRegistry = extensionRegistry; } - public DefaultExecutableInvoker(JupiterEngineExecutionContext context) { - this(context.getExtensionContext(), context.getExtensionRegistry()); - } - @Override public T invoke(Constructor constructor, Object outerInstance) { Object[] arguments = resolveParameters(constructor, Optional.empty(), Optional.ofNullable(outerInstance), diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultParameterContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultParameterContext.java index 900289b3b39f..30b6af17126e 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultParameterContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultParameterContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultTestInstances.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultTestInstances.java index dc611f69975d..3fec299191f9 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultTestInstances.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultTestInstances.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvoker.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvoker.java index b0a1fdb28283..e81c46c0715b 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvoker.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InvocationInterceptorChain.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InvocationInterceptorChain.java index f622a386360c..1c49b1f60ca3 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InvocationInterceptorChain.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InvocationInterceptorChain.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContext.java index a31c9c02ad27..ab4a41b69182 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -17,8 +17,6 @@ import org.junit.jupiter.engine.config.JupiterConfiguration; import org.junit.jupiter.engine.extension.MutableExtensionRegistry; import org.junit.platform.commons.JUnitException; -import org.junit.platform.commons.logging.Logger; -import org.junit.platform.commons.logging.LoggerFactory; import org.junit.platform.engine.EngineExecutionListener; import org.junit.platform.engine.support.hierarchical.EngineExecutionContext; import org.junit.platform.engine.support.hierarchical.ThrowableCollector; @@ -29,8 +27,6 @@ @API(status = INTERNAL, since = "5.0") public class JupiterEngineExecutionContext implements EngineExecutionContext { - private static final Logger logger = LoggerFactory.getLogger(JupiterEngineExecutionContext.class); - private final State state; // The following is not "cloneable" State. @@ -53,8 +49,7 @@ public void close() throws Exception { ((AutoCloseable) extensionContext).close(); } catch (Exception e) { - logger.error(e, () -> "Caught exception while closing extension context: " + extensionContext); - throw e; + throw new JUnitException("Failed to close extension context", e); } } } diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/MethodInvocation.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/MethodInvocation.java index a8bb6ae7dc14..22e1345d88e6 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/MethodInvocation.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/MethodInvocation.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/NamespaceAwareStore.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/NamespaceAwareStore.java index 7d30d496a708..10a6e2b96b64 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/NamespaceAwareStore.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/NamespaceAwareStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ParameterResolutionUtils.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ParameterResolutionUtils.java index 87e121a3a496..5d6e1b1802cc 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ParameterResolutionUtils.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ParameterResolutionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/TestInstancesProvider.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/TestInstancesProvider.java index 1f5819a68282..bceef6248a67 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/TestInstancesProvider.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/TestInstancesProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/AutoCloseExtension.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/AutoCloseExtension.java new file mode 100644 index 000000000000..331f591b6d88 --- /dev/null +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/AutoCloseExtension.java @@ -0,0 +1,112 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.engine.extension; + +import static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.function.Predicate; + +import org.junit.jupiter.api.AutoClose; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.ExtensionConfigurationException; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestInstancePreDestroyCallback; +import org.junit.platform.commons.logging.Logger; +import org.junit.platform.commons.logging.LoggerFactory; +import org.junit.platform.commons.util.AnnotationUtils; +import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.commons.util.ReflectionUtils; +import org.junit.platform.commons.util.StringUtils; +import org.junit.platform.engine.support.hierarchical.ThrowableCollector; + +/** + * {@code AutoCloseExtension} is a JUnit Jupiter extension that closes resources + * if a field in a test class is annotated with {@link AutoClose @AutoClose}. + * + *

Consult the Javadoc for {@code @AutoClose} for details on the contract. + * + * @since 5.11 + * @see AutoClose + */ +class AutoCloseExtension implements TestInstancePreDestroyCallback, AfterAllCallback { + + private static final Logger logger = LoggerFactory.getLogger(AutoCloseExtension.class); + + @Override + public void preDestroyTestInstance(ExtensionContext context) { + ThrowableCollector throwableCollector = new ThrowableCollector(__ -> false); + TestInstancePreDestroyCallback.preDestroyTestInstances(context, + testInstance -> closeFields(testInstance.getClass(), testInstance, throwableCollector)); + throwableCollector.assertEmpty(); + } + + @Override + public void afterAll(ExtensionContext context) { + ThrowableCollector throwableCollector = new ThrowableCollector(__ -> false); + closeFields(context.getRequiredTestClass(), null, throwableCollector); + throwableCollector.assertEmpty(); + } + + private static void closeFields(Class testClass, Object testInstance, ThrowableCollector throwableCollector) { + Predicate predicate = (testInstance == null ? ReflectionUtils::isStatic : ReflectionUtils::isNotStatic); + AnnotationUtils.findAnnotatedFields(testClass, AutoClose.class, predicate, BOTTOM_UP).forEach( + field -> throwableCollector.execute(() -> closeField(field, testInstance))); + } + + private static void closeField(Field field, Object testInstance) throws Exception { + String methodName = AnnotationUtils.findAnnotation(field, AutoClose.class).get().value(); + Class fieldType = field.getType(); + + checkCondition(StringUtils.isNotBlank(methodName), "@AutoClose on field %s must specify a method name.", field); + checkCondition(!fieldType.isPrimitive(), "@AutoClose is not supported on primitive field %s.", field); + checkCondition(!fieldType.isArray(), "@AutoClose is not supported on array field %s.", field); + + Object fieldValue = ReflectionUtils.tryToReadFieldValue(field, testInstance).get(); + if (fieldValue == null) { + logger.warn(() -> String.format("Cannot @AutoClose field %s because it is null.", getQualifiedName(field))); + } + else { + invokeCloseMethod(field, fieldValue, methodName.trim()); + } + } + + private static void invokeCloseMethod(Field field, Object target, String methodName) throws Exception { + // Avoid reflection if we can directly invoke close() via AutoCloseable. + if (target instanceof AutoCloseable && "close".equals(methodName)) { + ((AutoCloseable) target).close(); + return; + } + + Class targetType = target.getClass(); + Method closeMethod = ReflectionUtils.findMethod(targetType, methodName).orElseThrow( + () -> new ExtensionConfigurationException( + String.format("Cannot @AutoClose field %s because %s does not define method %s().", + getQualifiedName(field), targetType.getName(), methodName))); + + closeMethod = ReflectionUtils.getInterfaceMethodIfPossible(closeMethod, targetType); + ReflectionUtils.invokeMethod(closeMethod, target); + } + + private static void checkCondition(boolean condition, String messageFormat, Field field) { + Preconditions.condition(condition, () -> String.format(messageFormat, getQualifiedName(field))); + } + + private static String getQualifiedName(Field field) { + String typeName = field.getDeclaringClass().getCanonicalName(); + if (typeName == null) { + typeName = field.getDeclaringClass().getTypeName(); + } + return typeName + "." + field.getName(); + } + +} diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DefaultRepetitionInfo.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DefaultRepetitionInfo.java index feca90ee7137..0141ecf77431 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DefaultRepetitionInfo.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DefaultRepetitionInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DisabledCondition.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DisabledCondition.java index 6279cac68e13..0d06023adfde 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DisabledCondition.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DisabledCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/ExtensionRegistrar.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/ExtensionRegistrar.java index e555aecde0bd..3c4e90ed4d13 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/ExtensionRegistrar.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/ExtensionRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,8 +12,12 @@ import static org.apiguardian.api.API.Status.INTERNAL; +import java.lang.reflect.Field; +import java.util.function.Function; + import org.apiguardian.api.API; import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.RegisterExtension; /** * An {@code ExtensionRegistrar} is used to register extensions. @@ -45,11 +49,11 @@ public interface ExtensionRegistrar { * {@link org.junit.jupiter.api.extension.ExtendWith @ExtendWith}, the * {@code source} and the {@code extension} should be the same object. * However, if an extension is registered programmatically via - * {@link org.junit.jupiter.api.extension.RegisterExtension @RegisterExtension}, - * the {@code source} object should be the {@link java.lang.reflect.Field} - * that is annotated with {@code @RegisterExtension}. Similarly, if an - * extension is registered programmatically as a lambda expression - * or method reference, the {@code source} object should be the underlying + * {@link RegisterExtension @RegisterExtension}, the {@code source} object + * should be the {@link java.lang.reflect.Field} that is annotated with + * {@code @RegisterExtension}. Similarly, if an extension is registered + * programmatically as a lambda expression or method reference, the + * {@code source} object should be the underlying * {@link java.lang.reflect.Method} that implements the extension API. * * @param extension the extension to register; never {@code null} @@ -68,4 +72,34 @@ public interface ExtensionRegistrar { */ void registerSyntheticExtension(Extension extension, Object source); + /** + * Register an uninitialized extension for the supplied {@code testClass} to + * be initialized using the supplied {@code initializer} when an instance of + * the test class is created. + * + *

Uninitialized extensions are typically registered for fields annotated + * with {@link RegisterExtension @RegisterExtension} that cannot be + * initialized until an instance of the test class is created. Until they + * are initialized, such extensions are not available for use. + * + * @param testClass the test class for which the extension is registered; + * never {@code null} + * @param source the source of the extension; never {@code null} + * @param initializer the initializer function to be used to create the + * extension; never {@code null} + */ + void registerUninitializedExtension(Class testClass, Field source, + Function initializer); + + /** + * Initialize all registered extensions for the supplied {@code testClass} + * using the supplied {@code testInstance}. + * + * @param testClass the test class for which the extensions are initialized; + * never {@code null} + * @param testInstance the test instance to be used to initialize the + * extensions; never {@code null} + */ + void initializeExtensions(Class testClass, Object testInstance); + } diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/ExtensionRegistry.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/ExtensionRegistry.java index 1c20bf0be12a..ccd58b879006 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/ExtensionRegistry.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/ExtensionRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/MutableExtensionRegistry.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/MutableExtensionRegistry.java index 0a1433505c7b..b7b3f43034dd 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/MutableExtensionRegistry.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/MutableExtensionRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,18 +10,24 @@ package org.junit.jupiter.engine.extension; -import static java.util.stream.Stream.concat; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; import static org.apiguardian.api.API.Status.INTERNAL; +import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; +import java.util.function.Function; import java.util.stream.Stream; import org.apiguardian.api.API; @@ -36,10 +42,6 @@ /** * Default, mutable implementation of {@link ExtensionRegistry}. * - *

A registry has a reference to its parent registry, and all lookups are - * performed first in the current registry itself and then recursively in its - * ancestors. - * * @since 5.5 */ @API(status = INTERNAL, since = "5.5") @@ -49,6 +51,7 @@ public class MutableExtensionRegistry implements ExtensionRegistry, ExtensionReg private static final List DEFAULT_STATELESS_EXTENSIONS = Collections.unmodifiableList(Arrays.asList(// new DisabledCondition(), // + new AutoCloseExtension(), // new TimeoutExtension(), // new RepeatedTestExtension(), // new TestInfoParameterResolver(), // @@ -68,7 +71,7 @@ public class MutableExtensionRegistry implements ExtensionRegistry, ExtensionReg * @return a new {@code ExtensionRegistry}; never {@code null} */ public static MutableExtensionRegistry createRegistryWithDefaultExtensions(JupiterConfiguration configuration) { - MutableExtensionRegistry extensionRegistry = new MutableExtensionRegistry(null); + MutableExtensionRegistry extensionRegistry = new MutableExtensionRegistry(); DEFAULT_STATELESS_EXTENSIONS.forEach(extensionRegistry::registerDefaultExtension); @@ -105,38 +108,41 @@ public static MutableExtensionRegistry createRegistryFrom(MutableExtensionRegist return registry; } - private final MutableExtensionRegistry parent; - - private final Set> registeredExtensionTypes = new LinkedHashSet<>(); + private final Set> registeredExtensionTypes; + private final List registeredExtensions; + private final Map, LateInitExtensions> lateInitExtensions; - private final List registeredExtensions = new ArrayList<>(); + private MutableExtensionRegistry() { + this(emptySet(), emptyList()); + } private MutableExtensionRegistry(MutableExtensionRegistry parent) { - this.parent = parent; + this(parent.registeredExtensionTypes, parent.registeredExtensions); } - @Override - public Stream stream(Class extensionType) { - if (this.parent == null) { - return streamLocal(extensionType); - } - return concat(this.parent.stream(extensionType), streamLocal(extensionType)); + private MutableExtensionRegistry(Set> registeredExtensionTypes, + List registeredExtensions) { + this.registeredExtensionTypes = new LinkedHashSet<>(registeredExtensionTypes); + this.registeredExtensions = new ArrayList<>(registeredExtensions.size()); + this.lateInitExtensions = new LinkedHashMap<>(); + registeredExtensions.forEach(entry -> { + Entry newEntry = entry; + if (entry instanceof LateInitEntry) { + LateInitEntry lateInitEntry = (LateInitEntry) entry; + newEntry = lateInitEntry.getExtension() // + .map(Entry::of) // + .orElseGet(() -> getLateInitExtensions(lateInitEntry.getTestClass()).add(lateInitEntry.copy())); + } + this.registeredExtensions.add(newEntry); + }); } - /** - * Stream all {@code Extensions} of the specified type that are present - * in this registry. - * - *

Extensions in ancestors are ignored. - * - * @param extensionType the type of {@link Extension} to stream - */ - private Stream streamLocal(Class extensionType) { - // @formatter:off - return this.registeredExtensions.stream() - .filter(extensionType::isInstance) + @Override + public Stream stream(Class extensionType) { + return this.registeredExtensions.stream() // + .map(p -> p.getExtension().orElse(null)) // + .filter(extensionType::isInstance) // .map(extensionType::cast); - // @formatter:on } @Override @@ -151,8 +157,7 @@ public void registerExtension(Class extensionType) { * parent registry. */ private boolean isAlreadyRegistered(Class extensionType) { - return (this.registeredExtensionTypes.contains(extensionType) - || (this.parent != null && this.parent.isAlreadyRegistered(extensionType))); + return this.registeredExtensionTypes.contains(extensionType); } @Override @@ -166,6 +171,36 @@ public void registerSyntheticExtension(Extension extension, Object source) { registerExtension("synthetic", extension, source); } + @Override + public void registerUninitializedExtension(Class testClass, Field source, + Function initializer) { + Preconditions.notNull(testClass, "testClass must not be null"); + Preconditions.notNull(source, "source must not be null"); + Preconditions.notNull(initializer, "initializer must not be null"); + + logger.trace(() -> String.format("Registering local extension (late-init) for [%s]%s", + source.getType().getName(), buildSourceInfo(source))); + + LateInitEntry entry = getLateInitExtensions(testClass) // + .add(new LateInitEntry(testClass, initializer)); + this.registeredExtensions.add(entry); + } + + @Override + public void initializeExtensions(Class testClass, Object testInstance) { + Preconditions.notNull(testClass, "testClass must not be null"); + Preconditions.notNull(testInstance, "testInstance must not be null"); + + LateInitExtensions extensions = lateInitExtensions.remove(testClass); + if (extensions != null) { + extensions.initialize(testInstance); + } + } + + private LateInitExtensions getLateInitExtensions(Class testClass) { + return this.lateInitExtensions.computeIfAbsent(testClass, __ -> new LateInitExtensions()); + } + private void registerDefaultExtension(Extension extension) { registerExtension("default", extension); } @@ -184,12 +219,12 @@ private void registerExtension(String category, Extension extension) { private void registerExtension(String category, Extension extension, Object source) { Preconditions.notBlank(category, "category must not be null or blank"); - Preconditions.notNull(extension, "Extension must not be null"); + Preconditions.notNull(extension, "extension must not be null"); logger.trace( () -> String.format("Registering %s extension [%s]%s", category, extension, buildSourceInfo(source))); - this.registeredExtensions.add(extension); + this.registeredExtensions.add(Entry.of(extension)); this.registeredExtensionTypes.add(extension.getClass()); } @@ -205,4 +240,62 @@ private String buildSourceInfo(Object source) { return " from source [" + source + "]"; } + private interface Entry { + + static Entry of(Extension extension) { + Optional value = Optional.of(extension); + return () -> value; + } + + Optional getExtension(); + } + + private static class LateInitEntry implements Entry { + + private final Class testClass; + private final Function initializer; + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + private Optional extension = Optional.empty(); + + public LateInitEntry(Class testClass, Function initializer) { + this.testClass = testClass; + this.initializer = initializer; + } + + @Override + public Optional getExtension() { + return extension; + } + + public Class getTestClass() { + return testClass; + } + + void initialize(Object testInstance) { + Preconditions.condition(!extension.isPresent(), "Extension already initialized"); + extension = Optional.of(initializer.apply(testInstance)); + } + + LateInitEntry copy() { + Preconditions.condition(!extension.isPresent(), "Extension already initialized"); + return new LateInitEntry(testClass, initializer); + } + } + + private static class LateInitExtensions { + + private final List entries = new ArrayList<>(); + + LateInitEntry add(LateInitEntry entry) { + entries.add(entry); + return entry; + } + + void initialize(Object testInstance) { + entries.forEach(entry -> entry.initialize(testInstance)); + } + + } + } diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestDisplayNameFormatter.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestDisplayNameFormatter.java index 1b2657fdac28..b351721e4971 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestDisplayNameFormatter.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestDisplayNameFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestExtension.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestExtension.java index c50ba4bbf9a5..3ecc984c5106 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestExtension.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestInvocationContext.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestInvocationContext.java index 46180ca046c3..b40c3699e53c 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestInvocationContext.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestInvocationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java index 1d667b299a29..e4975f866d52 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocation.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocation.java index e620151dffd7..fc4834eff4e9 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocation.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocation.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocation.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocation.java index 9453577a472a..f102bae0cf9a 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocation.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocation.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java index fe76498bd4ae..ac5831d88329 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -26,6 +26,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Parameter; import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.NoSuchFileException; @@ -57,6 +58,7 @@ import org.junit.jupiter.engine.config.EnumConfigurationParameterConverter; import org.junit.jupiter.engine.config.JupiterConfiguration; import org.junit.platform.commons.JUnitException; +import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.logging.Logger; import org.junit.platform.commons.logging.LoggerFactory; import org.junit.platform.commons.util.ExceptionUtils; @@ -79,6 +81,8 @@ class TempDirectory implements BeforeAllCallback, BeforeEachCallback, ParameterR static final Namespace NAMESPACE = Namespace.create(TempDirectory.class); private static final String KEY = "temp.dir"; + private static final String FAILURE_TRACKER = "failure.tracker"; + private static final String CHILD_FAILED = "child.failed"; // for testing purposes static final String FILE_OPERATIONS_KEY = "file.operations"; @@ -96,6 +100,7 @@ public TempDirectory(JupiterConfiguration configuration) { */ @Override public void beforeAll(ExtensionContext context) { + installFailureTracker(context); injectStaticFields(context, context.getRequiredTestClass()); } @@ -106,10 +111,20 @@ public void beforeAll(ExtensionContext context) { */ @Override public void beforeEach(ExtensionContext context) { + installFailureTracker(context); context.getRequiredTestInstances().getAllInstances() // .forEach(instance -> injectInstanceFields(context, instance)); } + private static void installFailureTracker(ExtensionContext context) { + context.getStore(NAMESPACE).put(FAILURE_TRACKER, (CloseableResource) () -> context.getParent() // + .ifPresent(it -> { + if (selfOrChildFailed(context)) { + it.getStore(NAMESPACE).put(CHILD_FAILED, true); + } + })); + } + private void injectStaticFields(ExtensionContext context, Class testClass) { injectFields(context, null, testClass, ReflectionUtils::isStatic); } @@ -131,10 +146,10 @@ private void injectFields(ExtensionContext context, Object testInstance, Class type) { } } - private Object getPathOrFile(AnnotatedElementContext elementContext, Class type, TempDirFactory factory, + private Object getPathOrFile(Class elementType, AnnotatedElementContext elementContext, TempDirFactory factory, CleanupMode cleanupMode, Scope scope, ExtensionContext extensionContext) { Namespace namespace = scope == Scope.PER_DECLARATION // ? NAMESPACE.append(elementContext) // : NAMESPACE; Path path = extensionContext.getStore(namespace) // - .getOrComputeIfAbsent(KEY, __ -> createTempDir(factory, cleanupMode, elementContext, extensionContext), + .getOrComputeIfAbsent(KEY, + __ -> createTempDir(factory, cleanupMode, elementType, elementContext, extensionContext), CloseablePath.class) // .get(); - return (type == Path.class) ? path : path.toFile(); + return (elementType == Path.class) ? path : path.toFile(); } - static CloseablePath createTempDir(TempDirFactory factory, CleanupMode cleanupMode, + static CloseablePath createTempDir(TempDirFactory factory, CleanupMode cleanupMode, Class elementType, AnnotatedElementContext elementContext, ExtensionContext extensionContext) { try { - return new CloseablePath(factory, cleanupMode, elementContext, extensionContext); + return new CloseablePath(factory, cleanupMode, elementType, elementContext, extensionContext); } catch (Exception ex) { throw new ExtensionConfigurationException("Failed to create default temp directory", ex); } } + private static boolean selfOrChildFailed(ExtensionContext context) { + return context.getExecutionException().isPresent() // + || context.getStore(NAMESPACE).getOrDefault(CHILD_FAILED, Boolean.class, false); + } + static class CloseablePath implements CloseableResource { private static final Logger logger = LoggerFactory.getLogger(CloseablePath.class); @@ -266,12 +287,24 @@ static class CloseablePath implements CloseableResource { private final CleanupMode cleanupMode; private final ExtensionContext extensionContext; - CloseablePath(TempDirFactory factory, CleanupMode cleanupMode, AnnotatedElementContext elementContext, - ExtensionContext extensionContext) throws Exception { + private CloseablePath(TempDirFactory factory, CleanupMode cleanupMode, Class elementType, + AnnotatedElementContext elementContext, ExtensionContext extensionContext) throws Exception { this.dir = factory.createTempDirectory(elementContext, extensionContext); this.factory = factory; this.cleanupMode = cleanupMode; this.extensionContext = extensionContext; + + if (dir == null || !Files.isDirectory(dir)) { + close(); + throw new PreconditionViolationException("temp directory must be a directory"); + } + + if (elementType == File.class && !dir.getFileSystem().equals(FileSystems.getDefault())) { + close(); + throw new PreconditionViolationException( + "temp directory with non-default file system cannot be injected into " + File.class.getName() + + " target"); + } } Path get() { @@ -281,8 +314,7 @@ Path get() { @Override public void close() throws IOException { try { - if (cleanupMode == NEVER - || (cleanupMode == ON_SUCCESS && extensionContext.getExecutionException().isPresent())) { + if (cleanupMode == NEVER || (cleanupMode == ON_SUCCESS && selfOrChildFailed(extensionContext))) { logger.info(() -> "Skipping cleanup of temp dir " + dir + " due to cleanup mode configuration."); return; } @@ -302,7 +334,7 @@ public void close() throws IOException { private SortedMap deleteAllFilesAndDirectories(FileOperations fileOperations) throws IOException { - if (Files.notExists(dir)) { + if (dir == null || Files.notExists(dir)) { return Collections.emptySortedMap(); } @@ -321,6 +353,9 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { @Override public FileVisitResult visitFileFailed(Path file, IOException exc) { + if (exc instanceof NoSuchFileException) { + return CONTINUE; + } // IOException includes `AccessDeniedException` thrown by non-readable or non-executable flags resetPermissionsAndTryToDeleteAgain(file, exc); return CONTINUE; diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java index 1fdd3c7b9e5a..6805daece4a1 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java index ffa5cf066e2a..57e402da37e8 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutConfiguration.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutConfiguration.java index 1391d4cd293e..6238c0c4e1d7 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutConfiguration.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutDuration.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutDuration.java index eb058aef673e..977b6ba8f383 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutDuration.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutDuration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutDurationParser.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutDurationParser.java index 9412983b6495..28f28e8ce72c 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutDurationParser.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutDurationParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactory.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactory.java index 82c39e5d9a87..b76269ac6d59 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactory.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExtension.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExtension.java index 9efe2255e9d3..f9b23e0e4cbd 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExtension.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactory.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactory.java index 08f6851f1943..004915069e32 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactory.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/JupiterThrowableCollectorFactory.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/JupiterThrowableCollectorFactory.java index 49a80ddb6c33..bb06d12759a2 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/JupiterThrowableCollectorFactory.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/JupiterThrowableCollectorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollector.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollector.java index 5df8ff6a9074..bcaffa8f6503 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollector.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/README.md b/junit-jupiter-engine/src/test/README.md new file mode 100644 index 000000000000..86ed9e49af1d --- /dev/null +++ b/junit-jupiter-engine/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `jupiter-tests` project. diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnJreConditionTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnJreConditionTests.java deleted file mode 100644 index acba9ef791c7..000000000000 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnJreConditionTests.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2015-2023 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.jupiter.api.condition; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava10; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava11; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava12; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava13; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava14; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava15; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava16; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava17; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava18; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava19; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava20; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava21; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava22; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava8; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava9; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExecutionCondition; -import org.junit.platform.commons.PreconditionViolationException; - -/** - * Unit tests for {@link DisabledOnJreCondition}. - * - *

Note that test method names MUST match the test method names in - * {@link DisabledOnJreIntegrationTests}. - * - * @since 5.1 - */ -class DisabledOnJreConditionTests extends AbstractExecutionConditionTests { - - @Override - protected ExecutionCondition getExecutionCondition() { - return new DisabledOnJreCondition(); - } - - @Override - protected Class getTestClass() { - return DisabledOnJreIntegrationTests.class; - } - - /** - * @see DisabledOnJreIntegrationTests#enabledBecauseAnnotationIsNotPresent() - */ - @Test - void enabledBecauseAnnotationIsNotPresent() { - evaluateCondition(); - assertEnabled(); - assertReasonContains("@DisabledOnJre is not present"); - } - - /** - * @see DisabledOnJreIntegrationTests#missingJreDeclaration() - */ - @Test - void missingJreDeclaration() { - Exception exception = assertThrows(PreconditionViolationException.class, this::evaluateCondition); - assertThat(exception).hasMessageContaining("You must declare at least one JRE"); - } - - /** - * @see DisabledOnJreIntegrationTests#disabledOnAllJavaVersions() - */ - @Test - void disabledOnAllJavaVersions() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(true); - assertCustomDisabledReasonIs("Disabled on every JRE"); - } - - /** - * @see DisabledOnJreIntegrationTests#java8() - */ - @Test - void java8() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava8()); - } - - /** - * @see DisabledOnJreIntegrationTests#java9() - */ - @Test - void java9() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava9()); - } - - /** - * @see DisabledOnJreIntegrationTests#java10() - */ - @Test - void java10() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava10()); - } - - /** - * @see DisabledOnJreIntegrationTests#java11() - */ - @Test - void java11() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava11()); - } - - /** - * @see DisabledOnJreIntegrationTests#java12() - */ - @Test - void java12() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava12()); - } - - /** - * @see DisabledOnJreIntegrationTests#java13() - */ - @Test - void java13() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava13()); - } - - /** - * @see DisabledOnJreIntegrationTests#java14() - */ - @Test - void java14() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava14()); - } - - /** - * @see DisabledOnJreIntegrationTests#java15() - */ - @Test - void java15() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava15()); - } - - /** - * @see DisabledOnJreIntegrationTests#java16() - */ - @Test - void java16() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava16()); - } - - /** - * @see DisabledOnJreIntegrationTests#java17() - */ - @Test - void java17() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava17()); - } - - /** - * @see DisabledOnJreIntegrationTests#java18() - */ - @Test - void java18() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava18()); - } - - /** - * @see DisabledOnJreIntegrationTests#java19() - */ - @Test - void java19() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava19()); - } - - /** - * @see DisabledOnJreIntegrationTests#java20() - */ - @Test - void java20() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava20()); - } - - /** - * @see DisabledOnJreIntegrationTests#java21() - */ - @Test - void java21() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava21()); - } - - /** - * @see DisabledOnJreIntegrationTests#java22() - */ - @Test - void java22() { - evaluateCondition(); - assertDisabledOnCurrentJreIf(onJava22()); - } - - /** - * @see DisabledOnJreIntegrationTests#other() - */ - @Test - void other() { - evaluateCondition(); - assertDisabledOnCurrentJreIf( - !(onJava8() || onJava9() || onJava10() || onJava11() || onJava12() || onJava13() || onJava14() || onJava15() - || onJava16() || onJava17() || onJava18() || onJava19() || onJava20() || onJava21() || onJava22())); - } - - private void assertDisabledOnCurrentJreIf(boolean condition) { - if (condition) { - assertDisabled(); - assertReasonContains("Disabled on JRE version: " + System.getProperty("java.version")); - } - else { - assertEnabled(); - assertReasonContains("Enabled on JRE version: " + System.getProperty("java.version")); - } - } - -} diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnJreIntegrationTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnJreIntegrationTests.java deleted file mode 100644 index 2c7f922cd6b7..000000000000 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnJreIntegrationTests.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2015-2023 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.jupiter.api.condition; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava10; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava11; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava12; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava13; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava14; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava15; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava16; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava17; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava18; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava19; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava20; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava21; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava22; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava8; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava9; -import static org.junit.jupiter.api.condition.JRE.JAVA_10; -import static org.junit.jupiter.api.condition.JRE.JAVA_11; -import static org.junit.jupiter.api.condition.JRE.JAVA_12; -import static org.junit.jupiter.api.condition.JRE.JAVA_13; -import static org.junit.jupiter.api.condition.JRE.JAVA_14; -import static org.junit.jupiter.api.condition.JRE.JAVA_15; -import static org.junit.jupiter.api.condition.JRE.JAVA_16; -import static org.junit.jupiter.api.condition.JRE.JAVA_17; -import static org.junit.jupiter.api.condition.JRE.JAVA_18; -import static org.junit.jupiter.api.condition.JRE.JAVA_19; -import static org.junit.jupiter.api.condition.JRE.JAVA_20; -import static org.junit.jupiter.api.condition.JRE.JAVA_21; -import static org.junit.jupiter.api.condition.JRE.JAVA_22; -import static org.junit.jupiter.api.condition.JRE.JAVA_8; -import static org.junit.jupiter.api.condition.JRE.JAVA_9; -import static org.junit.jupiter.api.condition.JRE.OTHER; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -/** - * Integration tests for {@link DisabledOnJre}. - * - * @since 5.1 - */ -class DisabledOnJreIntegrationTests { - - @Test - @Disabled("Only used in a unit test via reflection") - void enabledBecauseAnnotationIsNotPresent() { - } - - @Test - @Disabled("Only used in a unit test via reflection") - @DisabledOnJre({}) - void missingJreDeclaration() { - } - - @Test - @DisabledOnJre(value = { JAVA_8, JAVA_9, JAVA_10, JAVA_11, JAVA_12, JAVA_13, JAVA_14, JAVA_15, JAVA_16, JAVA_17, - JAVA_18, JAVA_19, JAVA_20, JAVA_21, JAVA_22, OTHER }, disabledReason = "Disabled on every JRE") - void disabledOnAllJavaVersions() { - fail("should be disabled"); - } - - @Test - @DisabledOnJre(JAVA_8) - void java8() { - assertFalse(onJava8()); - } - - @Test - @DisabledOnJre(JAVA_9) - void java9() { - assertFalse(onJava9()); - } - - @Test - @DisabledOnJre(JAVA_10) - void java10() { - assertFalse(onJava10()); - } - - @Test - @DisabledOnJre(JAVA_11) - void java11() { - assertFalse(onJava11()); - } - - @Test - @DisabledOnJre(JAVA_12) - void java12() { - assertFalse(onJava12()); - } - - @Test - @DisabledOnJre(JAVA_13) - void java13() { - assertFalse(onJava13()); - } - - @Test - @DisabledOnJre(JAVA_14) - void java14() { - assertFalse(onJava14()); - } - - @Test - @DisabledOnJre(JAVA_15) - void java15() { - assertFalse(onJava15()); - } - - @Test - @DisabledOnJre(JAVA_16) - void java16() { - assertFalse(onJava16()); - } - - @Test - @DisabledOnJre(JAVA_17) - void java17() { - assertFalse(onJava17()); - } - - @Test - @DisabledOnJre(JAVA_18) - void java18() { - assertFalse(onJava18()); - } - - @Test - @DisabledOnJre(JAVA_19) - void java19() { - assertFalse(onJava19()); - } - - @Test - @DisabledOnJre(JAVA_20) - void java20() { - assertFalse(onJava20()); - } - - @Test - @DisabledOnJre(JAVA_21) - void java21() { - assertFalse(onJava21()); - } - - @Test - @DisabledOnJre(JAVA_22) - void java22() { - assertFalse(onJava22()); - } - - @Test - @DisabledOnJre(OTHER) - void other() { - assertTrue( - onJava8() || onJava9() || onJava10() || onJava11() || onJava12() || onJava13() || onJava14() || onJava15() - || onJava16() || onJava17() || onJava18() || onJava19() || onJava20() || onJava21() || onJava22()); - } - -} diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnJreConditionTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnJreConditionTests.java deleted file mode 100644 index 60f3cb70be1f..000000000000 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnJreConditionTests.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2015-2023 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.jupiter.api.condition; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava10; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava11; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava12; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava13; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava14; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava15; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava16; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava17; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava18; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava19; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava20; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava21; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava22; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava8; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava9; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExecutionCondition; -import org.junit.platform.commons.PreconditionViolationException; - -/** - * Unit tests for {@link EnabledOnJreCondition}. - * - *

Note that test method names MUST match the test method names in - * {@link EnabledOnJreIntegrationTests}. - * - * @since 5.1 - */ -class EnabledOnJreConditionTests extends AbstractExecutionConditionTests { - - @Override - protected ExecutionCondition getExecutionCondition() { - return new EnabledOnJreCondition(); - } - - @Override - protected Class getTestClass() { - return EnabledOnJreIntegrationTests.class; - } - - /** - * @see EnabledOnJreIntegrationTests#enabledBecauseAnnotationIsNotPresent() - */ - @Test - void enabledBecauseAnnotationIsNotPresent() { - evaluateCondition(); - assertEnabled(); - assertReasonContains("@EnabledOnJre is not present"); - } - - /** - * @see EnabledOnJreIntegrationTests#missingJreDeclaration() - */ - @Test - void missingJreDeclaration() { - Exception exception = assertThrows(PreconditionViolationException.class, this::evaluateCondition); - assertThat(exception).hasMessageContaining("You must declare at least one JRE"); - } - - /** - * @see EnabledOnJreIntegrationTests#enabledOnAllJavaVersions() - */ - @Test - void enabledOnAllJavaVersions() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(true); - } - - /** - * @see EnabledOnJreIntegrationTests#java8() - */ - @Test - void java8() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava8()); - } - - /** - * @see EnabledOnJreIntegrationTests#java9() - */ - @Test - void java9() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava9()); - } - - /** - * @see EnabledOnJreIntegrationTests#java10() - */ - @Test - void java10() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava10()); - } - - /** - * @see EnabledOnJreIntegrationTests#java11() - */ - @Test - void java11() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava11()); - } - - /** - * @see EnabledOnJreIntegrationTests#java12() - */ - @Test - void java12() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava12()); - } - - /** - * @see EnabledOnJreIntegrationTests#java13() - */ - @Test - void java13() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava13()); - } - - /** - * @see EnabledOnJreIntegrationTests#java14() - */ - @Test - void java14() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava14()); - } - - /** - * @see EnabledOnJreIntegrationTests#java15() - */ - @Test - void java15() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava15()); - } - - /** - * @see EnabledOnJreIntegrationTests#java16() - */ - @Test - void java16() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava16()); - } - - /** - * @see EnabledOnJreIntegrationTests#java17() - */ - @Test - void java17() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava17()); - } - - /** - * @see EnabledOnJreIntegrationTests#java18() - */ - @Test - void java18() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava18()); - } - - /** - * @see EnabledOnJreIntegrationTests#java19() - */ - @Test - void java19() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava19()); - } - - /** - * @see EnabledOnJreIntegrationTests#java20() - */ - @Test - void java20() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava20()); - } - - /** - * @see EnabledOnJreIntegrationTests#java21() - */ - @Test - void java21() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava21()); - } - - /** - * @see EnabledOnJreIntegrationTests#java22() - */ - @Test - void java22() { - evaluateCondition(); - assertEnabledOnCurrentJreIf(onJava22()); - } - - /** - * @see EnabledOnJreIntegrationTests#other() - */ - @Test - void other() { - evaluateCondition(); - assertEnabledOnCurrentJreIf( - !(onJava8() || onJava9() || onJava10() || onJava11() || onJava12() || onJava13() || onJava14() || onJava15() - || onJava16() || onJava17() || onJava18() || onJava19() || onJava20() || onJava21() || onJava22())); - assertCustomDisabledReasonIs("Disabled on almost every JRE"); - } - - private void assertEnabledOnCurrentJreIf(boolean condition) { - if (condition) { - assertEnabled(); - assertReasonContains("Enabled on JRE version: " + System.getProperty("java.version")); - } - else { - assertDisabled(); - assertReasonContains("Disabled on JRE version: " + System.getProperty("java.version")); - } - } - -} diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnJreIntegrationTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnJreIntegrationTests.java deleted file mode 100644 index 3ad579262415..000000000000 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnJreIntegrationTests.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2015-2023 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.jupiter.api.condition; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.condition.JRE.JAVA_10; -import static org.junit.jupiter.api.condition.JRE.JAVA_11; -import static org.junit.jupiter.api.condition.JRE.JAVA_12; -import static org.junit.jupiter.api.condition.JRE.JAVA_13; -import static org.junit.jupiter.api.condition.JRE.JAVA_14; -import static org.junit.jupiter.api.condition.JRE.JAVA_15; -import static org.junit.jupiter.api.condition.JRE.JAVA_16; -import static org.junit.jupiter.api.condition.JRE.JAVA_17; -import static org.junit.jupiter.api.condition.JRE.JAVA_18; -import static org.junit.jupiter.api.condition.JRE.JAVA_19; -import static org.junit.jupiter.api.condition.JRE.JAVA_20; -import static org.junit.jupiter.api.condition.JRE.JAVA_21; -import static org.junit.jupiter.api.condition.JRE.JAVA_22; -import static org.junit.jupiter.api.condition.JRE.JAVA_8; -import static org.junit.jupiter.api.condition.JRE.JAVA_9; -import static org.junit.jupiter.api.condition.JRE.OTHER; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -/** - * Integration tests for {@link EnabledOnJre}. - * - * @since 5.1 - */ -class EnabledOnJreIntegrationTests { - - private static final String JAVA_VERSION = System.getProperty("java.version"); - - @Test - @Disabled("Only used in a unit test via reflection") - void enabledBecauseAnnotationIsNotPresent() { - } - - @Test - @Disabled("Only used in a unit test via reflection") - @EnabledOnJre({}) - void missingJreDeclaration() { - } - - @Test - @EnabledOnJre({ JAVA_8, JAVA_9, JAVA_10, JAVA_11, JAVA_12, JAVA_13, JAVA_14, JAVA_15, JAVA_16, JAVA_17, JAVA_18, - JAVA_19, JAVA_20, JAVA_21, JAVA_22, OTHER }) - void enabledOnAllJavaVersions() { - } - - @Test - @EnabledOnJre(JAVA_8) - void java8() { - assertTrue(onJava8()); - } - - @Test - @EnabledOnJre(JAVA_9) - void java9() { - assertTrue(onJava9()); - } - - @Test - @EnabledOnJre(JAVA_10) - void java10() { - assertTrue(onJava10()); - } - - @Test - @EnabledOnJre(JAVA_11) - void java11() { - assertTrue(onJava11()); - } - - @Test - @EnabledOnJre(JAVA_12) - void java12() { - assertTrue(onJava12()); - } - - @Test - @EnabledOnJre(JAVA_13) - void java13() { - assertTrue(onJava13()); - } - - @Test - @EnabledOnJre(JAVA_14) - void java14() { - assertTrue(onJava14()); - } - - @Test - @EnabledOnJre(JAVA_15) - void java15() { - assertTrue(onJava15()); - } - - @Test - @EnabledOnJre(JAVA_16) - void java16() { - assertTrue(onJava16()); - } - - @Test - @EnabledOnJre(JAVA_17) - void java17() { - assertTrue(onJava17()); - } - - @Test - @EnabledOnJre(JAVA_18) - void java18() { - assertTrue(onJava18()); - } - - @Test - @EnabledOnJre(JAVA_19) - void java19() { - assertTrue(onJava19()); - } - - @Test - @EnabledOnJre(JAVA_20) - void java20() { - assertTrue(onJava20()); - } - - @Test - @EnabledOnJre(JAVA_21) - void java21() { - assertTrue(onJava21()); - } - - @Test - @EnabledOnJre(JAVA_22) - void java22() { - assertTrue(onJava22()); - } - - @Test - @EnabledOnJre(value = OTHER, disabledReason = "Disabled on almost every JRE") - void other() { - assertFalse( - onJava8() || onJava9() || onJava10() || onJava11() || onJava12() || onJava13() || onJava14() || onJava15() - || onJava16() || onJava17() || onJava18() || onJava19() || onJava20() || onJava21() || onJava22()); - } - - static boolean onJava8() { - return JAVA_VERSION.startsWith("1.8"); - } - - static boolean onJava9() { - return JAVA_VERSION.startsWith("9"); - } - - static boolean onJava10() { - return JAVA_VERSION.startsWith("10"); - } - - static boolean onJava11() { - return JAVA_VERSION.startsWith("11"); - } - - static boolean onJava12() { - return JAVA_VERSION.startsWith("12"); - } - - static boolean onJava13() { - return JAVA_VERSION.startsWith("13"); - } - - static boolean onJava14() { - return JAVA_VERSION.startsWith("14"); - } - - static boolean onJava15() { - return JAVA_VERSION.startsWith("15"); - } - - static boolean onJava16() { - return JAVA_VERSION.startsWith("16"); - } - - static boolean onJava17() { - return JAVA_VERSION.startsWith("17"); - } - - static boolean onJava18() { - return JAVA_VERSION.startsWith("18"); - } - - static boolean onJava19() { - return JAVA_VERSION.startsWith("19"); - } - - static boolean onJava20() { - return JAVA_VERSION.startsWith("20"); - } - - static boolean onJava21() { - return JAVA_VERSION.startsWith("21"); - } - - static boolean onJava22() { - return JAVA_VERSION.startsWith("22"); - } - -} diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/CloseablePathCleanupTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/CloseablePathCleanupTests.java deleted file mode 100644 index 85daa507785a..000000000000 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/CloseablePathCleanupTests.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2015-2023 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.jupiter.engine.extension; - -import static java.nio.file.Files.deleteIfExists; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.io.CleanupMode.ALWAYS; -import static org.junit.jupiter.api.io.CleanupMode.NEVER; -import static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.util.Optional; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.AnnotatedElementContext; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; -import org.junit.jupiter.api.io.CleanupMode; -import org.junit.jupiter.api.io.TempDir; -import org.junit.jupiter.api.io.TempDirFactory; -import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; -import org.junit.jupiter.engine.execution.NamespaceAwareStore; -import org.junit.platform.engine.support.store.NamespacedHierarchicalStore; - -/** - * Integration tests for cleanup of the {@link TempDirectory} when the {@link CleanupMode} is - * set to {@link CleanupMode#ALWAYS}, {@link CleanupMode#NEVER}, or {@link CleanupMode#ON_SUCCESS}. - * - * @since 5.9 - * - * @see TempDir - * @see CleanupMode - */ -class CloseablePathCleanupTests extends AbstractJupiterTestEngineTests { - - private final AnnotatedElementContext elementContext = mock(); - private final ExtensionContext extensionContext = mock(); - private final TempDirFactory factory = spy(TempDirFactory.Standard.INSTANCE); - - private TempDirectory.CloseablePath closeablePath; - - @BeforeEach - void setUpExtensionContext() { - var store = new NamespaceAwareStore(new NamespacedHierarchicalStore<>(null), Namespace.GLOBAL); - when(extensionContext.getStore(any())).thenReturn(store); - } - - @AfterEach - void cleanupTempDirectory() throws IOException { - deleteIfExists(closeablePath.get()); - } - - @Test - @DisplayName("is cleaned up for a cleanup mode of ALWAYS") - void always() throws IOException { - closeablePath = TempDirectory.createTempDir(factory, ALWAYS, elementContext, extensionContext); - assertThat(closeablePath.get()).exists(); - - closeablePath.close(); - assertThat(closeablePath.get()).doesNotExist(); - verify(factory).close(); - } - - @Test - @DisplayName("is not cleaned up for a cleanup mode of NEVER") - void never() throws IOException { - closeablePath = TempDirectory.createTempDir(factory, NEVER, elementContext, extensionContext); - assertThat(closeablePath.get()).exists(); - - closeablePath.close(); - assertThat(closeablePath.get()).exists(); - verify(factory).close(); - } - - @Test - @DisplayName("is not cleaned up for a cleanup mode of ON_SUCCESS, if there is an exception") - void onSuccessWithException() throws IOException { - when(extensionContext.getExecutionException()).thenReturn(Optional.of(new Exception())); - - closeablePath = TempDirectory.createTempDir(factory, ON_SUCCESS, elementContext, extensionContext); - assertThat(closeablePath.get()).exists(); - - closeablePath.close(); - assertThat(closeablePath.get()).exists(); - verify(factory).close(); - } - - @Test - @DisplayName("is cleaned up for a cleanup mode of ON_SUCCESS, if there is no exception") - void onSuccessWithNoException() throws IOException { - when(extensionContext.getExecutionException()).thenReturn(Optional.empty()); - - closeablePath = TempDirectory.createTempDir(factory, ON_SUCCESS, elementContext, extensionContext); - assertThat(closeablePath.get()).exists(); - - closeablePath.close(); - assertThat(closeablePath.get()).doesNotExist(); - verify(factory).close(); - } - -} diff --git a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/api/KotlinAssertionsTests.kt b/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/api/KotlinAssertionsTests.kt deleted file mode 100644 index 3386acf06b5d..000000000000 --- a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/api/KotlinAssertionsTests.kt +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2015-2023 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ -package org.junit.jupiter.api - -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals -import org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith -import org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.DynamicContainer.dynamicContainer -import org.junit.jupiter.api.DynamicTest.dynamicTest -import org.opentest4j.AssertionFailedError -import org.opentest4j.MultipleFailuresError -import java.util.stream.Stream -import kotlin.reflect.KClass - -/** - * Unit tests for JUnit Jupiter [org.junit.jupiter.api] top-level assertion functions. - */ -class KotlinAssertionsTests { - - // Bonus: no null check tests as these get handled by the compiler! - - @Test - fun `assertAll with functions that do not throw exceptions`() { - assertAll(Stream.of({ assertTrue(true) }, { assertFalse(false) })) - assertAll("heading", Stream.of({ assertTrue(true) }, { assertFalse(false) })) - assertAll(setOf({ assertTrue(true) }, { assertFalse(false) })) - assertAll("heading", setOf({ assertTrue(true) }, { assertFalse(false) })) - assertAll({ assertTrue(true) }, { assertFalse(false) }) - assertAll("heading", { assertTrue(true) }, { assertFalse(false) }) - } - - @Test - fun `assertAll with functions that throw AssertionErrors`() { - val multipleFailuresError = assertThrows { - assertAll( - { assertFalse(true) }, - { assertFalse(true) } - ) - } - assertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError::class, AssertionFailedError::class) - } - - @Test - fun `assertThrows and fail`() { - assertThrows { fail("message") } - assertThrows { fail("message", AssertionError()) } - assertThrows { fail("message", null) } - assertThrows("should fail") { fail({ "message" }) } - assertThrows({ "should fail" }) { fail(AssertionError()) } - assertThrows({ "should fail" }) { fail(null as Throwable?) } - } - - @Test - fun `expected context exception testing`() = runBlocking { - assertThrows("Should fail async") { - suspend { fail("Should fail async") }() - } - } - - @TestFactory - fun assertDoesNotThrow(): Stream = Stream.of( - dynamicContainer( - "succeeds when no exception thrown", - Stream.of( - dynamicTest("for no arguments variant") { - val actual = assertDoesNotThrow { 1 } - assertEquals(1, actual) - }, - dynamicTest("for no arguments variant (suspended)") { - runBlocking { - val actual = assertDoesNotThrow { suspend { 1 }() } - assertEquals(1, actual) - } - }, - dynamicTest("for message variant") { - val actual = assertDoesNotThrow("message") { 2 } - assertEquals(2, actual) - }, - dynamicTest("for message variant (suspended)") { - runBlocking { - val actual = assertDoesNotThrow("message") { suspend { 2 }() } - assertEquals(2, actual) - } - }, - dynamicTest("for message supplier variant") { - val actual = assertDoesNotThrow({ "message" }) { 3 } - assertEquals(3, actual) - }, - dynamicTest("for message supplier variant (suspended)") { - runBlocking { - val actual = assertDoesNotThrow({ "message" }) { suspend { 3 }() } - assertEquals(3, actual) - } - } - ) - ), - dynamicContainer( - "fails when an exception is thrown", - Stream.of( - dynamicTest("for no arguments variant") { - val exception = assertThrows { - assertDoesNotThrow { - fail("fail") - } - } - assertMessageEquals( - exception, - "Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" - ) - }, - dynamicTest("for no arguments variant (suspended)") { - runBlocking { - val exception = assertThrows { - assertDoesNotThrow { - suspend { fail("fail") }() - } - } - assertMessageEquals( - exception, - "Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" - ) - } - }, - dynamicTest("for message variant") { - val exception = assertThrows { - assertDoesNotThrow("Does not throw") { - fail("fail") - } - } - assertMessageEquals( - exception, - "Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" - ) - }, - dynamicTest("for message variant (suspended)") { - runBlocking { - val exception = assertThrows { - assertDoesNotThrow("Does not throw") { - suspend { fail("fail") }() - } - } - assertMessageEquals( - exception, - "Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" - ) - } - }, - dynamicTest("for message supplier variant") { - val exception = assertThrows { - assertDoesNotThrow({ "Does not throw" }) { - fail("fail") - } - } - assertMessageEquals( - exception, - "Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" - ) - }, - dynamicTest("for message supplier variant (suspended)") { - runBlocking { - val exception = assertThrows { - assertDoesNotThrow({ "Does not throw" }) { - suspend { fail("fail") }() - } - } - assertMessageEquals( - exception, - "Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" - ) - } - } - ) - ) - ) - - @Test - fun `assertAll with stream of functions that throw AssertionErrors`() { - val multipleFailuresError = assertThrows("Should have thrown multiple errors") { - assertAll(Stream.of({ assertFalse(true) }, { assertFalse(true) })) - } - assertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError::class, AssertionFailedError::class) - } - - @Test - fun `assertAll with collection of functions that throw AssertionErrors`() { - val multipleFailuresError = assertThrows("Should have thrown multiple errors") { - assertAll(setOf({ assertFalse(true) }, { assertFalse(true) })) - } - assertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError::class, AssertionFailedError::class) - } - - @Test - fun `assertThrows with function that does not throw an exception`() { - val assertionMessage = "This will not throw an exception" - val error = assertThrows("assertThrows did not throw the correct exception") { - assertThrows(assertionMessage) { } - // This should never execute: - expectAssertionFailedError() - } - assertMessageStartsWith(error, assertionMessage) - } - - companion object { - fun assertExpectedExceptionTypes( - multipleFailuresError: MultipleFailuresError, - vararg exceptionTypes: KClass - ) = - AssertAllAssertionsTests.assertExpectedExceptionTypes( - multipleFailuresError, - *exceptionTypes.map { it.java }.toTypedArray() - ) - } -} diff --git a/junit-jupiter-engine/src/testFixtures/java/org/junit/jupiter/engine/discovery/JupiterUniqueIdBuilder.java b/junit-jupiter-engine/src/testFixtures/java/org/junit/jupiter/engine/discovery/JupiterUniqueIdBuilder.java index e80608fb41b4..8f2699706b80 100644 --- a/junit-jupiter-engine/src/testFixtures/java/org/junit/jupiter/engine/discovery/JupiterUniqueIdBuilder.java +++ b/junit-jupiter-engine/src/testFixtures/java/org/junit/jupiter/engine/discovery/JupiterUniqueIdBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/junit-jupiter-migrationsupport.gradle.kts b/junit-jupiter-migrationsupport/junit-jupiter-migrationsupport.gradle.kts index d3779d057e4f..0f28afd9ea94 100644 --- a/junit-jupiter-migrationsupport/junit-jupiter-migrationsupport.gradle.kts +++ b/junit-jupiter-migrationsupport/junit-jupiter-migrationsupport.gradle.kts @@ -1,7 +1,6 @@ plugins { id("junitbuild.java-library-conventions") id("junitbuild.junit4-compatibility") - id("junitbuild.testing-conventions") } description = "JUnit Jupiter Migration Support" @@ -13,11 +12,6 @@ dependencies { compileOnlyApi(libs.apiguardian) - testImplementation(projects.junitJupiterEngine) - testImplementation(projects.junitPlatformLauncher) - testImplementation(projects.junitPlatformSuiteEngine) - testImplementation(projects.junitPlatformTestkit) - osgiVerification(projects.junitJupiterEngine) osgiVerification(projects.junitPlatformLauncher) } diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/EnableJUnit4MigrationSupport.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/EnableJUnit4MigrationSupport.java index a5ee8bc8824c..ed34832b0fbd 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/EnableJUnit4MigrationSupport.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/EnableJUnit4MigrationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/conditions/IgnoreCondition.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/conditions/IgnoreCondition.java index c73546afc46e..c1fa55f955e9 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/conditions/IgnoreCondition.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/conditions/IgnoreCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupport.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupport.java index c215ecc808a6..4bd82f1d6488 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupport.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupport.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupport.java index cd25a1190ff0..3b15203a9ffe 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupport.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupport.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupport.java index 43ceaea7ed32..8d9bae138aa6 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupport.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/TestRuleSupport.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/TestRuleSupport.java index 99a24d9b438b..90026b7a1bd4 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/TestRuleSupport.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/TestRuleSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/VerifierSupport.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/VerifierSupport.java index 73781be2910b..420e817061c9 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/VerifierSupport.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/VerifierSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/AbstractTestRuleAdapter.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/AbstractTestRuleAdapter.java index 223f609e225c..219c24a90b55 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/AbstractTestRuleAdapter.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/AbstractTestRuleAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/ExpectedExceptionAdapter.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/ExpectedExceptionAdapter.java index 1b1971b2d04d..15c114b9dc39 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/ExpectedExceptionAdapter.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/ExpectedExceptionAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/ExternalResourceAdapter.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/ExternalResourceAdapter.java index b6905f42da2d..f35a5de25d2b 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/ExternalResourceAdapter.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/ExternalResourceAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/GenericBeforeAndAfterAdvice.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/GenericBeforeAndAfterAdvice.java index ad384ce9f47a..329086883026 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/GenericBeforeAndAfterAdvice.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/GenericBeforeAndAfterAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/VerifierAdapter.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/VerifierAdapter.java index ec00fc823e2c..986e6894a4f7 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/VerifierAdapter.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/VerifierAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/AbstractTestRuleAnnotatedMember.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/AbstractTestRuleAnnotatedMember.java index c7dcdd6830ce..f93dc00a97a1 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/AbstractTestRuleAnnotatedMember.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/AbstractTestRuleAnnotatedMember.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedField.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedField.java index 7a8dd0c501ba..ef277f590fcd 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedField.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedField.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedMember.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedMember.java index 0004b905fdf5..48bed90147d8 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedMember.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedMember.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedMethod.java b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedMethod.java index 75fc5ad6ee5c..13042a928da6 100644 --- a/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedMethod.java +++ b/junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/README.md b/junit-jupiter-migrationsupport/src/test/README.md new file mode 100644 index 000000000000..86ed9e49af1d --- /dev/null +++ b/junit-jupiter-migrationsupport/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `jupiter-tests` project. diff --git a/junit-jupiter-migrationsupport/src/test/resources/log4j2-test.xml b/junit-jupiter-migrationsupport/src/test/resources/log4j2-test.xml deleted file mode 100644 index 1c1cbb8d6acd..000000000000 --- a/junit-jupiter-migrationsupport/src/test/resources/log4j2-test.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/junit-jupiter-params/junit-jupiter-params.gradle.kts b/junit-jupiter-params/junit-jupiter-params.gradle.kts index ac430302010c..d05a3b72e3e4 100644 --- a/junit-jupiter-params/junit-jupiter-params.gradle.kts +++ b/junit-jupiter-params/junit-jupiter-params.gradle.kts @@ -1,7 +1,7 @@ plugins { id("junitbuild.kotlin-library-conventions") id("junitbuild.shadow-conventions") - id("junitbuild.testing-conventions") + id("junitbuild.jmh-conventions") } description = "JUnit Jupiter Params" @@ -14,15 +14,7 @@ dependencies { shadowed(libs.univocity.parsers) - testImplementation(projects.junitPlatformTestkit) - testImplementation(projects.junitJupiterEngine) - testImplementation(projects.junitPlatformLauncher) - testImplementation(projects.junitPlatformSuiteEngine) - testImplementation(testFixtures(projects.junitPlatformCommons)) - testImplementation(testFixtures(projects.junitJupiterEngine)) - compileOnly(kotlin("stdlib")) - testImplementation(kotlin("stdlib")) osgiVerification(projects.junitJupiterEngine) osgiVerification(projects.junitPlatformLauncher) diff --git a/junit-jupiter-params/src/jmh/java/org/junit/jupiter/params/ParameterizedTestNameFormatterBenchmarks.java b/junit-jupiter-params/src/jmh/java/org/junit/jupiter/params/ParameterizedTestNameFormatterBenchmarks.java new file mode 100644 index 000000000000..bc296054ec36 --- /dev/null +++ b/junit-jupiter-params/src/jmh/java/org/junit/jupiter/params/ParameterizedTestNameFormatterBenchmarks.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params; + +import java.util.List; +import java.util.stream.IntStream; + +import org.junit.jupiter.params.provider.Arguments; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +@State(Scope.Benchmark) +@Fork(1) +@Warmup(iterations = 1, time = 2) +@Measurement(iterations = 3, time = 2) +public class ParameterizedTestNameFormatterBenchmarks { + + @Param({ "1", "2", "4", "10", "100", "1000" }) + private int numberOfParameters; + + List argumentsList; + + @Setup + public void setUp() { + argumentsList = IntStream.range(0, numberOfParameters) // + .mapToObj(i -> Arguments.argumentSet(String.valueOf(i), i)) // + .toList(); + } + + @Benchmark + public void formatTestNames(Blackhole blackhole) throws Exception { + var formatter = new ParameterizedTestNameFormatter( + ParameterizedTest.DISPLAY_NAME_PLACEHOLDER + " " + ParameterizedTest.DEFAULT_DISPLAY_NAME + " ({0})", + "displayName", + new ParameterizedTestMethodContext(TestCase.class.getDeclaredMethod("parameterizedTest", int.class)), 512); + for (int i = 0; i < argumentsList.size(); i++) { + Arguments arguments = argumentsList.get(i); + blackhole.consume(formatter.format(i, arguments, arguments.get())); + } + } + + static class TestCase { + @SuppressWarnings("unused") + void parameterizedTest(int param) { + } + } +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java index 4464c67c3363..bf45c87fd71b 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,7 @@ package org.junit.jupiter.params; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; import java.lang.annotation.Documented; @@ -28,7 +29,7 @@ * *

Such methods must not be {@code private} or {@code static}. * - *

Argument Providers and Sources

+ *

Arguments Providers and Sources

* *

{@code @ParameterizedTest} methods must specify at least one * {@link org.junit.jupiter.params.provider.ArgumentsProvider ArgumentsProvider} @@ -79,6 +80,14 @@ * to create a custom composed annotation that inherits the semantics * of {@code @ParameterizedTest}. * + *

Inheritance

+ * + *

{@code @ParameterizedTest} methods are inherited from superclasses as long + * as they are not overridden according to the visibility rules of the + * Java language. Similarly, {@code @ParameterizedTest} methods declared as + * interface default methods are inherited as long as they are not + * overridden. + * *

Test Execution Order

* *

By default, test methods will be ordered using an algorithm that is @@ -140,6 +149,7 @@ * * @since 5.3 * @see #name + * @see #DEFAULT_DISPLAY_NAME */ String INDEX_PLACEHOLDER = "{index}"; @@ -165,9 +175,43 @@ * * @since 5.6 * @see #name + * @see #ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER */ String ARGUMENTS_WITH_NAMES_PLACEHOLDER = "{argumentsWithNames}"; + /** + * Placeholder for the name of the argument set for the current invocation + * of a {@code @ParameterizedTest} method: {argumentSetName}. + * + *

This placeholder can be used when the current set of arguments was created via + * {@link org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...) + * argumentSet()}. + * + * @since 5.11 + * @see #name + * @see #ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER + * @see org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...) + */ + @API(status = EXPERIMENTAL, since = "5.11") + String ARGUMENT_SET_NAME_PLACEHOLDER = "{argumentSetName}"; + + /** + * Placeholder for either {@link #ARGUMENT_SET_NAME_PLACEHOLDER} or + * {@link #ARGUMENTS_WITH_NAMES_PLACEHOLDER}, depending on whether the + * current set of arguments was created via + * {@link org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...) + * argumentSet()}: {argumentSetNameOrArgumentsWithNames}. + * + * @since 5.11 + * @see #name + * @see #ARGUMENT_SET_NAME_PLACEHOLDER + * @see #ARGUMENTS_WITH_NAMES_PLACEHOLDER + * @see #DEFAULT_DISPLAY_NAME + * @see org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...) + */ + @API(status = EXPERIMENTAL, since = "5.11") + String ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER = "{argumentSetNameOrArgumentsWithNames}"; + /** * Default display name pattern for the current invocation of a * {@code @ParameterizedTest} method: {@value} @@ -180,17 +224,19 @@ * @see #name * @see #DISPLAY_NAME_PLACEHOLDER * @see #INDEX_PLACEHOLDER - * @see #ARGUMENTS_WITH_NAMES_PLACEHOLDER + * @see #ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER */ - String DEFAULT_DISPLAY_NAME = "[" + INDEX_PLACEHOLDER + "] " + ARGUMENTS_WITH_NAMES_PLACEHOLDER; + String DEFAULT_DISPLAY_NAME = "[" + INDEX_PLACEHOLDER + "] " + + ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER; /** * The display name to be used for individual invocations of the * parameterized test; never blank or consisting solely of whitespace. * - *

Defaults to {default_display_name}. + *

Defaults to {@value ParameterizedTestExtension#DEFAULT_DISPLAY_NAME}. * - *

If the default display name flag ({default_display_name}) + *

If the default display name flag + * ({@value ParameterizedTestExtension#DEFAULT_DISPLAY_NAME}) * is not overridden, JUnit will: *

    *
  • Look up the {@value ParameterizedTestExtension#DISPLAY_NAME_PATTERN_KEY} @@ -199,30 +245,32 @@ * Gradle and Maven), a JVM system property, or the JUnit Platform configuration * file (i.e., a file named {@code junit-platform.properties} in the root of * the class path). Consult the User Guide for further information.
  • - *
  • Otherwise, the value of the {@link #DEFAULT_DISPLAY_NAME} constant will - * be used.
  • + *
  • Otherwise, {@value #DEFAULT_DISPLAY_NAME} will be used.
  • *
* *

Supported placeholders

*
    - *
  • {@link #DISPLAY_NAME_PLACEHOLDER}
  • - *
  • {@link #INDEX_PLACEHOLDER}
  • - *
  • {@link #ARGUMENTS_PLACEHOLDER}
  • - *
  • {@link #ARGUMENTS_WITH_NAMES_PLACEHOLDER}
  • - *
  • {0}, {1}, etc.: an individual argument (0-based)
  • + *
  • {@value #DISPLAY_NAME_PLACEHOLDER}
  • + *
  • {@value #INDEX_PLACEHOLDER}
  • + *
  • {@value #ARGUMENT_SET_NAME_PLACEHOLDER}
  • + *
  • {@value #ARGUMENTS_PLACEHOLDER}
  • + *
  • {@value #ARGUMENTS_WITH_NAMES_PLACEHOLDER}
  • + *
  • {@value #ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER}
  • + *
  • "{0}", "{1}", etc.: an individual argument (0-based)
  • *
* *

For the latter, you may use {@link java.text.MessageFormat} patterns - * to customize formatting. Please note that the original arguments are - * passed when formatting, regardless of any implicit or explicit argument - * conversions. + * to customize formatting (for example, {@code {0,number,#.###}}). Please + * note that the original arguments are passed when formatting, regardless + * of any implicit or explicit argument conversions. * - *

Note that {default_display_name} is a flag rather than a - * placeholder. + *

Note that + * {@value ParameterizedTestExtension#DEFAULT_DISPLAY_NAME} is + * a flag rather than a placeholder. * * @see java.text.MessageFormat */ - String name() default "{default_display_name}"; + String name() default ParameterizedTestExtension.DEFAULT_DISPLAY_NAME; /** * Configure whether all arguments of the parameterized test that implement {@link AutoCloseable} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java index 0aa5664cafc3..aa7cd4251720 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -15,7 +15,6 @@ import static org.junit.platform.commons.util.AnnotationUtils.isAnnotated; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; @@ -39,7 +38,7 @@ class ParameterizedTestExtension implements TestTemplateInvocationContextProvide private static final String METHOD_CONTEXT_KEY = "context"; static final String ARGUMENT_MAX_LENGTH_KEY = "junit.jupiter.params.displayname.argument.maxlength"; - private static final String DEFAULT_DISPLAY_NAME = "{default_display_name}"; + static final String DEFAULT_DISPLAY_NAME = "{default_display_name}"; static final String DISPLAY_NAME_PATTERN_KEY = "junit.jupiter.params.displayname.default"; @Override @@ -88,8 +87,6 @@ public Stream provideTestTemplateInvocationContex .map(this::instantiateArgumentsProvider) .map(provider -> AnnotationConsumerInitializer.initialize(templateMethod, provider)) .flatMap(provider -> arguments(provider, extensionContext)) - .map(Arguments::get) - .map(arguments -> consumedArguments(arguments, methodContext)) .map(arguments -> { invocationCount.incrementAndGet(); return createInvocationContext(formatter, methodContext, arguments, invocationCount.intValue()); @@ -122,12 +119,14 @@ private ExtensionContext.Store getStore(ExtensionContext context) { } private TestTemplateInvocationContext createInvocationContext(ParameterizedTestNameFormatter formatter, - ParameterizedTestMethodContext methodContext, Object[] arguments, int invocationIndex) { + ParameterizedTestMethodContext methodContext, Arguments arguments, int invocationIndex) { + return new ParameterizedTestInvocationContext(formatter, methodContext, arguments, invocationIndex); } private ParameterizedTestNameFormatter createNameFormatter(ExtensionContext extensionContext, Method templateMethod, ParameterizedTestMethodContext methodContext, String displayName, int argumentMaxLength) { + ParameterizedTest parameterizedTest = findAnnotation(templateMethod, ParameterizedTest.class).get(); String pattern = parameterizedTest.name().equals(DEFAULT_DISPLAY_NAME) ? extensionContext.getConfigurationParameter(DISPLAY_NAME_PATTERN_KEY).orElse( @@ -149,12 +148,4 @@ protected static Stream arguments(ArgumentsProvider provide } } - private Object[] consumedArguments(Object[] arguments, ParameterizedTestMethodContext methodContext) { - if (methodContext.hasAggregator()) { - return arguments; - } - int parameterCount = methodContext.getParameterCount(); - return arguments.length > parameterCount ? Arrays.copyOf(arguments, parameterCount) : arguments; - } - } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java index c30892adfade..c0ac83e78717 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,10 +12,12 @@ import static java.util.Collections.singletonList; +import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.extension.Extension; import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.params.provider.Arguments; /** * @since 5.0 @@ -24,26 +26,37 @@ class ParameterizedTestInvocationContext implements TestTemplateInvocationContex private final ParameterizedTestNameFormatter formatter; private final ParameterizedTestMethodContext methodContext; - private final Object[] arguments; + private final Arguments arguments; + private final Object[] consumedArguments; private final int invocationIndex; ParameterizedTestInvocationContext(ParameterizedTestNameFormatter formatter, - ParameterizedTestMethodContext methodContext, Object[] arguments, int invocationIndex) { + ParameterizedTestMethodContext methodContext, Arguments arguments, int invocationIndex) { + this.formatter = formatter; this.methodContext = methodContext; this.arguments = arguments; + this.consumedArguments = consumedArguments(methodContext, arguments.get()); this.invocationIndex = invocationIndex; } @Override public String getDisplayName(int invocationIndex) { - return this.formatter.format(invocationIndex, this.arguments); + return this.formatter.format(invocationIndex, this.arguments, this.consumedArguments); } @Override public List getAdditionalExtensions() { return singletonList( - new ParameterizedTestParameterResolver(this.methodContext, this.arguments, this.invocationIndex)); + new ParameterizedTestParameterResolver(this.methodContext, this.consumedArguments, this.invocationIndex)); + } + + private static Object[] consumedArguments(ParameterizedTestMethodContext methodContext, Object[] arguments) { + if (methodContext.hasAggregator()) { + return arguments; + } + int parameterCount = methodContext.getParameterCount(); + return arguments.length > parameterCount ? Arrays.copyOf(arguments, parameterCount) : arguments; } } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestMethodContext.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestMethodContext.java index 43223b0d3da7..3e20899dddfc 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestMethodContext.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestMethodContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestNameFormatter.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestNameFormatter.java index a505bd81f91e..ecbea24fd04c 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestNameFormatter.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestNameFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,15 +13,30 @@ import static java.util.stream.Collectors.joining; import static org.junit.jupiter.params.ParameterizedTest.ARGUMENTS_PLACEHOLDER; import static org.junit.jupiter.params.ParameterizedTest.ARGUMENTS_WITH_NAMES_PLACEHOLDER; +import static org.junit.jupiter.params.ParameterizedTest.ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER; +import static org.junit.jupiter.params.ParameterizedTest.ARGUMENT_SET_NAME_PLACEHOLDER; import static org.junit.jupiter.params.ParameterizedTest.DISPLAY_NAME_PLACEHOLDER; import static org.junit.jupiter.params.ParameterizedTest.INDEX_PLACEHOLDER; +import static org.junit.platform.commons.util.StringUtils.isNotBlank; +import java.text.FieldPosition; import java.text.Format; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; import java.util.stream.IntStream; import org.junit.jupiter.api.Named; +import org.junit.jupiter.api.extension.ExtensionConfigurationException; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.Arguments.ArgumentSet; import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.util.StringUtils; @@ -30,40 +45,39 @@ */ class ParameterizedTestNameFormatter { - private static final char ELLIPSIS = '\u2026'; - private static final String TEMPORARY_DISPLAY_NAME_PLACEHOLDER = "~~~JUNIT_DISPLAY_NAME~~~"; - - private final String pattern; - private final String displayName; - private final ParameterizedTestMethodContext methodContext; - private final int argumentMaxLength; + private final PartialFormatter[] partialFormatters; ParameterizedTestNameFormatter(String pattern, String displayName, ParameterizedTestMethodContext methodContext, int argumentMaxLength) { - this.pattern = pattern; - this.displayName = displayName; - this.methodContext = methodContext; - this.argumentMaxLength = argumentMaxLength; + try { + this.partialFormatters = parse(pattern, displayName, methodContext, argumentMaxLength); + } + catch (Exception ex) { + String message = "The display name pattern defined for the parameterized test is invalid. " + + "See nested exception for further details."; + throw new JUnitException(message, ex); + } } - String format(int invocationIndex, Object... arguments) { + String format(int invocationIndex, Arguments arguments, Object[] consumedArguments) { try { - return formatSafely(invocationIndex, arguments); + return formatSafely(invocationIndex, arguments, consumedArguments); } catch (Exception ex) { - String message = "The display name pattern defined for the parameterized test is invalid. " + String message = "Failed to format display name for parameterized test. " + "See nested exception for further details."; throw new JUnitException(message, ex); } } - private String formatSafely(int invocationIndex, Object[] arguments) { - Object[] namedArguments = extractNamedArguments(arguments); - String pattern = prepareMessageFormatPattern(invocationIndex, namedArguments); - MessageFormat format = new MessageFormat(pattern); - Object[] humanReadableArguments = makeReadable(format, namedArguments); - String formatted = format.format(humanReadableArguments); - return formatted.replace(TEMPORARY_DISPLAY_NAME_PLACEHOLDER, this.displayName); + private String formatSafely(int invocationIndex, Arguments arguments, Object[] consumedArguments) { + ArgumentsContext context = new ArgumentsContext(invocationIndex, arguments, + extractNamedArguments(consumedArguments)); + StringBuffer result = new StringBuffer(); // used instead of StringBuilder so MessageFormat can append directly + for (PartialFormatter partialFormatter : this.partialFormatters) { + partialFormatter.append(context, result); + } + return result.toString(); } private Object[] extractNamedArguments(Object[] arguments) { @@ -72,51 +86,207 @@ private Object[] extractNamedArguments(Object[] arguments) { .toArray(); } - private String prepareMessageFormatPattern(int invocationIndex, Object[] arguments) { - String result = pattern// - .replace(DISPLAY_NAME_PLACEHOLDER, TEMPORARY_DISPLAY_NAME_PLACEHOLDER)// - .replace(INDEX_PLACEHOLDER, String.valueOf(invocationIndex)); + private PartialFormatter[] parse(String pattern, String displayName, ParameterizedTestMethodContext methodContext, + int argumentMaxLength) { + + List result = new ArrayList<>(); + PartialFormatters formatters = createPartialFormatters(displayName, methodContext, argumentMaxLength); + String unparsedSegment = pattern; - if (result.contains(ARGUMENTS_WITH_NAMES_PLACEHOLDER)) { - result = result.replace(ARGUMENTS_WITH_NAMES_PLACEHOLDER, argumentsWithNamesPattern(arguments)); + while (isNotBlank(unparsedSegment)) { + PlaceholderPosition position = findFirstPlaceholder(formatters, unparsedSegment); + if (position == null) { + result.add(determineNonPlaceholderFormatter(unparsedSegment, argumentMaxLength)); + break; + } + if (position.index > 0) { + String before = unparsedSegment.substring(0, position.index); + result.add(determineNonPlaceholderFormatter(before, argumentMaxLength)); + } + result.add(formatters.get(position.placeholder)); + unparsedSegment = unparsedSegment.substring(position.index + position.placeholder.length()); } - if (result.contains(ARGUMENTS_PLACEHOLDER)) { - result = result.replace(ARGUMENTS_PLACEHOLDER, argumentsPattern(arguments)); + return result.toArray(new PartialFormatter[0]); + } + + private static PlaceholderPosition findFirstPlaceholder(PartialFormatters formatters, String segment) { + if (segment.length() < formatters.minimumPlaceholderLength) { + return null; } + PlaceholderPosition minimum = null; + for (String placeholder : formatters.placeholders()) { + int index = segment.indexOf(placeholder); + if (index >= 0) { + if (index < formatters.minimumPlaceholderLength) { + return new PlaceholderPosition(index, placeholder); + } + else if (minimum == null || index < minimum.index) { + minimum = new PlaceholderPosition(index, placeholder); + } + } + } + return minimum; + } + + private static PartialFormatter determineNonPlaceholderFormatter(String segment, int argumentMaxLength) { + return segment.contains("{") // + ? new MessageFormatPartialFormatter(segment, argumentMaxLength) // + : (context, result) -> result.append(segment); + } - return result; + private PartialFormatters createPartialFormatters(String displayName, ParameterizedTestMethodContext methodContext, + int argumentMaxLength) { + + PartialFormatter argumentsWithNamesFormatter = new CachingByArgumentsLengthPartialFormatter( + length -> new MessageFormatPartialFormatter(argumentsWithNamesPattern(length, methodContext), + argumentMaxLength)); + + PartialFormatters formatters = new PartialFormatters(); + formatters.put(INDEX_PLACEHOLDER, PartialFormatter.INDEX); + formatters.put(DISPLAY_NAME_PLACEHOLDER, (context, result) -> result.append(displayName)); + formatters.put(ARGUMENT_SET_NAME_PLACEHOLDER, PartialFormatter.ARGUMENT_SET_NAME); + formatters.put(ARGUMENTS_WITH_NAMES_PLACEHOLDER, argumentsWithNamesFormatter); + formatters.put(ARGUMENTS_PLACEHOLDER, new CachingByArgumentsLengthPartialFormatter( + length -> new MessageFormatPartialFormatter(argumentsPattern(length), argumentMaxLength))); + formatters.put(ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER, (context, result) -> { + PartialFormatter formatterToUse = context.arguments instanceof ArgumentSet // + ? PartialFormatter.ARGUMENT_SET_NAME // + : argumentsWithNamesFormatter; + formatterToUse.append(context, result); + }); + return formatters; } - private String argumentsWithNamesPattern(Object[] arguments) { - return IntStream.range(0, arguments.length) // + private static String argumentsWithNamesPattern(int length, ParameterizedTestMethodContext methodContext) { + return IntStream.range(0, length) // .mapToObj(index -> methodContext.getParameterName(index).map(name -> name + "=").orElse("") + "{" + index + "}") // .collect(joining(", ")); } - private String argumentsPattern(Object[] arguments) { - return IntStream.range(0, arguments.length) // + private static String argumentsPattern(int length) { + return IntStream.range(0, length) // .mapToObj(index -> "{" + index + "}") // .collect(joining(", ")); } - private Object[] makeReadable(MessageFormat format, Object[] arguments) { - Format[] formats = format.getFormatsByArgumentIndex(); - Object[] result = Arrays.copyOf(arguments, Math.min(arguments.length, formats.length), Object[].class); - for (int i = 0; i < result.length; i++) { - if (formats[i] == null) { - result[i] = truncateIfExceedsMaxLength(StringUtils.nullSafeToString(arguments[i])); + private static class PlaceholderPosition { + + final int index; + final String placeholder; + + PlaceholderPosition(int index, String placeholder) { + this.index = index; + this.placeholder = placeholder; + } + + } + + private static class ArgumentsContext { + + private final int invocationIndex; + private final Arguments arguments; + private final Object[] consumedArguments; + + ArgumentsContext(int invocationIndex, Arguments arguments, Object[] consumedArguments) { + this.invocationIndex = invocationIndex; + this.arguments = arguments; + this.consumedArguments = consumedArguments; + } + } + + @FunctionalInterface + private interface PartialFormatter { + + PartialFormatter INDEX = (context, result) -> result.append(context.invocationIndex); + + PartialFormatter ARGUMENT_SET_NAME = (context, result) -> { + if (!(context.arguments instanceof ArgumentSet)) { + throw new ExtensionConfigurationException( + String.format("When the display name pattern for a @ParameterizedTest contains %s, " + + "the arguments must be supplied as an ArgumentSet.", + ARGUMENT_SET_NAME_PLACEHOLDER)); + } + result.append(((ArgumentSet) context.arguments).getName()); + }; + + void append(ArgumentsContext context, StringBuffer result); + } + + private static class MessageFormatPartialFormatter implements PartialFormatter { + + @SuppressWarnings("UnnecessaryUnicodeEscape") + private static final char ELLIPSIS = '\u2026'; + + private final MessageFormat messageFormat; + private final int argumentMaxLength; + + MessageFormatPartialFormatter(String pattern, int argumentMaxLength) { + this.messageFormat = new MessageFormat(pattern); + this.argumentMaxLength = argumentMaxLength; + } + + // synchronized because MessageFormat is not thread-safe + @Override + public synchronized void append(ArgumentsContext context, StringBuffer result) { + this.messageFormat.format(makeReadable(context.consumedArguments), result, new FieldPosition(0)); + } + + private Object[] makeReadable(Object[] arguments) { + Format[] formats = messageFormat.getFormatsByArgumentIndex(); + Object[] result = Arrays.copyOf(arguments, Math.min(arguments.length, formats.length), Object[].class); + for (int i = 0; i < result.length; i++) { + if (formats[i] == null) { + result[i] = truncateIfExceedsMaxLength(StringUtils.nullSafeToString(arguments[i])); + } + } + return result; + } + + private String truncateIfExceedsMaxLength(String argument) { + if (argument != null && argument.length() > this.argumentMaxLength) { + return argument.substring(0, this.argumentMaxLength - 1) + ELLIPSIS; } + return argument; + } + } + + private static class CachingByArgumentsLengthPartialFormatter implements PartialFormatter { + + private final ConcurrentMap cache = new ConcurrentHashMap<>(1); + private final Function factory; + + CachingByArgumentsLengthPartialFormatter(Function factory) { + this.factory = factory; + } + + @Override + public void append(ArgumentsContext context, StringBuffer result) { + cache.computeIfAbsent(context.consumedArguments.length, factory).append(context, result); } - return result; } - private String truncateIfExceedsMaxLength(String argument) { - if (argument != null && argument.length() > argumentMaxLength) { - return argument.substring(0, argumentMaxLength - 1) + ELLIPSIS; + private static class PartialFormatters { + + private final Map formattersByPlaceholder = new LinkedHashMap<>(); + private int minimumPlaceholderLength = Integer.MAX_VALUE; + + void put(String placeholder, PartialFormatter formatter) { + formattersByPlaceholder.put(placeholder, formatter); + int newPlaceholderLength = placeholder.length(); + if (newPlaceholderLength < minimumPlaceholderLength) { + minimumPlaceholderLength = newPlaceholderLength; + } + } + + PartialFormatter get(String placeholder) { + return formattersByPlaceholder.get(placeholder); + } + + Set placeholders() { + return formattersByPlaceholder.keySet(); } - return argument; } } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestParameterResolver.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestParameterResolver.java index 15bd6b34deaa..634405d4a5ce 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestParameterResolver.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -38,6 +38,7 @@ class ParameterizedTestParameterResolver implements ParameterResolver, AfterTest ParameterizedTestParameterResolver(ParameterizedTestMethodContext methodContext, Object[] arguments, int invocationIndex) { + this.methodContext = methodContext; this.arguments = arguments; this.invocationIndex = invocationIndex; diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/AggregateWith.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/AggregateWith.java index 57a0b88f3201..0510e4df844a 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/AggregateWith.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/AggregateWith.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentAccessException.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentAccessException.java index ce439621dae8..07e8b1b8070a 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentAccessException.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentAccessException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAccessor.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAccessor.java index 38d9e21a84c4..59b1710550f7 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAccessor.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAggregationException.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAggregationException.java index 10b96762420f..cb9db4512211 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAggregationException.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAggregationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAggregator.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAggregator.java index 52887ed5de94..e4ed9803f09f 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAggregator.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAggregator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessor.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessor.java index 33064919c351..39ad3c33ca29 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessor.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/AnnotationBasedArgumentConverter.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/AnnotationBasedArgumentConverter.java index e86a1558d128..da892c22818d 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/AnnotationBasedArgumentConverter.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/AnnotationBasedArgumentConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ArgumentConversionException.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ArgumentConversionException.java index 0bd9513c9cbf..dbcd5b4c1b28 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ArgumentConversionException.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ArgumentConversionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ArgumentConverter.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ArgumentConverter.java index d17589655ae4..46f7e9a84b79 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ArgumentConverter.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ArgumentConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ConvertWith.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ConvertWith.java index 42be3c88f65d..b42145b39fa3 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ConvertWith.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ConvertWith.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java index fe697c5696e3..c02fd58dba41 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,10 +10,7 @@ package org.junit.jupiter.params.converter; -import static java.util.Arrays.asList; -import static java.util.Collections.unmodifiableList; import static org.apiguardian.api.API.Status.INTERNAL; -import static org.junit.platform.commons.util.ReflectionUtils.getWrapperType; import java.io.File; import java.math.BigDecimal; @@ -21,13 +18,13 @@ import java.net.URI; import java.net.URL; import java.util.Currency; -import java.util.List; import java.util.Locale; -import java.util.Optional; import java.util.UUID; import org.apiguardian.api.API; import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.platform.commons.support.conversion.ConversionException; +import org.junit.platform.commons.support.conversion.ConversionSupport; import org.junit.platform.commons.util.ClassLoaderUtils; import org.junit.platform.commons.util.ReflectionUtils; @@ -47,23 +44,13 @@ * * @since 5.0 * @see org.junit.jupiter.params.converter.ArgumentConverter + * @see org.junit.platform.commons.support.conversion.ConversionSupport */ @API(status = INTERNAL, since = "5.0") public class DefaultArgumentConverter implements ArgumentConverter { public static final DefaultArgumentConverter INSTANCE = new DefaultArgumentConverter(); - private static final List stringToObjectConverters = unmodifiableList(asList( // - new StringToBooleanConverter(), // - new StringToCharacterConverter(), // - new StringToNumberConverter(), // - new StringToClassConverter(), // - new StringToEnumConverter(), // - new StringToJavaTimeConverter(), // - new StringToCommonJavaTypesConverter(), // - new FallbackStringToObjectConverter() // - )); - private DefaultArgumentConverter() { // nothing to initialize } @@ -88,34 +75,19 @@ public final Object convert(Object source, Class targetType, ParameterContext } if (source instanceof String) { - Class targetTypeToUse = toWrapperType(targetType); - Optional converter = stringToObjectConverters.stream().filter( - candidate -> candidate.canConvert(targetTypeToUse)).findFirst(); - if (converter.isPresent()) { - Class declaringClass = context.getDeclaringExecutable().getDeclaringClass(); - ClassLoader classLoader = ClassLoaderUtils.getClassLoader(declaringClass); - try { - return converter.get().convert((String) source, targetTypeToUse, classLoader); - } - catch (Exception ex) { - if (ex instanceof ArgumentConversionException) { - // simply rethrow it - throw (ArgumentConversionException) ex; - } - // else - throw new ArgumentConversionException( - "Failed to convert String \"" + source + "\" to type " + targetType.getTypeName(), ex); - } + Class declaringClass = context.getDeclaringExecutable().getDeclaringClass(); + ClassLoader classLoader = ClassLoaderUtils.getClassLoader(declaringClass); + try { + return ConversionSupport.convert((String) source, targetType, classLoader); + } + catch (ConversionException ex) { + throw new ArgumentConversionException(ex.getMessage(), ex); } } + throw new ArgumentConversionException( String.format("No built-in converter for source type %s and target type %s", source.getClass().getTypeName(), targetType.getTypeName())); } - private static Class toWrapperType(Class targetType) { - Class wrapperType = getWrapperType(targetType); - return wrapperType != null ? wrapperType : targetType; - } - } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverter.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverter.java index 3af740fa7a3c..26fe98e29df0 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverter.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/JavaTimeConversionPattern.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/JavaTimeConversionPattern.java index b6c03e76ca67..d5ec9d509453 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/JavaTimeConversionPattern.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/JavaTimeConversionPattern.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/SimpleArgumentConverter.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/SimpleArgumentConverter.java index b6a0d15045f8..b24c267e5394 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/SimpleArgumentConverter.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/SimpleArgumentConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/TypedArgumentConverter.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/TypedArgumentConverter.java index ba194e446de7..bb3c65c009b5 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/TypedArgumentConverter.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/TypedArgumentConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.java index a4a5971ecff1..f751b35e3c5c 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,6 +13,8 @@ import static org.apiguardian.api.API.Status.EXPERIMENTAL; import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; import java.util.stream.Stream; import org.apiguardian.api.API; @@ -39,17 +41,17 @@ public abstract class AnnotationBasedArgumentsProvider public AnnotationBasedArgumentsProvider() { } - private A annotation; + private final List annotations = new ArrayList<>(); @Override public final void accept(A annotation) { Preconditions.notNull(annotation, "annotation must not be null"); - this.annotation = annotation; + annotations.add(annotation); } @Override public final Stream provideArguments(ExtensionContext context) { - return provideArguments(context, this.annotation); + return annotations.stream().flatMap(annotation -> provideArguments(context, annotation)); } /** diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index da3371f3795c..94b82972d1b4 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,7 @@ package org.junit.jupiter.params.provider; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; import org.apiguardian.api.API; @@ -43,6 +44,7 @@ * to convert some of the arguments from one type to another. * * @since 5.0 + * @see ArgumentSet * @see org.junit.jupiter.params.ParameterizedTest * @see org.junit.jupiter.params.provider.ArgumentsSource * @see org.junit.jupiter.params.provider.ArgumentsProvider @@ -58,7 +60,7 @@ public interface Arguments { * @apiNote If you need a type-safe way to access some or all of the arguments, * please read the {@linkplain Arguments class-level API note}. * - * @return the arguments; must not be {@code null} + * @return the arguments; never {@code null} */ Object[] get(); @@ -70,9 +72,10 @@ public interface Arguments { * method; must not be {@code null} * @return an instance of {@code Arguments}; never {@code null} * @see #arguments(Object...) + * @see #argumentSet(String, Object...) */ static Arguments of(Object... arguments) { - Preconditions.notNull(arguments, "argument array must not be null"); + Preconditions.notNull(arguments, "arguments array must not be null"); return () -> arguments; } @@ -88,9 +91,83 @@ static Arguments of(Object... arguments) { * method; must not be {@code null} * @return an instance of {@code Arguments}; never {@code null} * @since 5.3 + * @see #argumentSet(String, Object...) */ static Arguments arguments(Object... arguments) { return of(arguments); } + /** + * Factory method for creating an {@link ArgumentSet} based on the supplied + * {@code name} and {@code arguments}. + * + *

Favor this method over {@link Arguments#of Arguments.of(...)} and + * {@link Arguments#arguments arguments(...)} when you wish to assign a name + * to the entire set of arguments. + * + *

This method is well suited to be used as a static import — for + * example, via: + * {@code import static org.junit.jupiter.params.provider.Arguments.argumentSet;}. + * + * @param name the name of the argument set; must not be {@code null} or blank + * @param arguments the arguments to be used for an invocation of the test + * method; must not be {@code null} + * @return an {@code ArgumentSet}; never {@code null} + * @since 5.11 + * @see ArgumentSet + * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_PLACEHOLDER + * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER + */ + @API(status = EXPERIMENTAL, since = "5.11") + static ArgumentSet argumentSet(String name, Object... arguments) { + return new ArgumentSet(name, arguments); + } + + /** + * Specialization of {@link Arguments} that associates a {@link #getName() name} + * with a set of {@link #get() arguments}. + * + * @since 5.11 + * @see Arguments#argumentSet(String, Object...) + * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_PLACEHOLDER + * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER + */ + @API(status = EXPERIMENTAL, since = "5.11") + final class ArgumentSet implements Arguments { + + private final String name; + + private final Object[] arguments; + + private ArgumentSet(String name, Object[] arguments) { + Preconditions.notBlank(name, "name must not be null or blank"); + Preconditions.notNull(arguments, "arguments array must not be null"); + this.name = name; + this.arguments = arguments; + } + + /** + * Get the name of this {@code ArgumentSet}. + * @return the name of this {@code ArgumentSet}; never {@code null} or blank + */ + public String getName() { + return this.name; + } + + @Override + public Object[] get() { + return this.arguments; + } + + /** + * Return the {@link #getName() name} of this {@code ArgumentSet}. + * @return the name of this {@code ArgumentSet} + */ + @Override + public String toString() { + return getName(); + } + + } + } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsProvider.java index e57f5423cb5d..689517d607b8 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSource.java index 04f7849d91e0..d3996b704b78 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSource.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -23,7 +23,7 @@ /** * {@code @ArgumentsSource} is a {@linkplain Repeatable repeatable} annotation - * that is used to register {@linkplain ArgumentsProvider argument providers} + * that is used to register {@linkplain ArgumentsProvider arguments providers} * for the annotated test method. * *

{@code @ArgumentsSource} may also be used as a meta-annotation in order to diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSources.java index 320c8ad8232e..ea7dd0494da6 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSources.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSources.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -31,7 +31,7 @@ * @since 5.0 * @see org.junit.jupiter.params.provider.ArgumentsSource */ -@Target(ElementType.METHOD) +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = STABLE, since = "5.7") diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsUtils.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsUtils.java new file mode 100644 index 000000000000..7d99a61cc738 --- /dev/null +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params.provider; + +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import org.junit.platform.commons.util.ReflectionUtils; + +/** + * Collection of utilities for working with {@link Arguments}. + * + * @since 5.11, when it was extracted from {@link MethodArgumentsProvider} + */ +final class ArgumentsUtils { + + private ArgumentsUtils() { + /* no-op */ + } + + /** + * Convert the supplied object into an {@link Arguments} instance. + */ + static Arguments toArguments(Object item) { + // Nothing to do except cast. + if (item instanceof Arguments) { + return (Arguments) item; + } + + // Pass all multidimensional arrays "as is", in contrast to Object[]. + // See https://github.com/junit-team/junit5/issues/1665 + if (ReflectionUtils.isMultidimensionalArray(item)) { + return arguments(item); + } + + // Special treatment for one-dimensional reference arrays. + // See https://github.com/junit-team/junit5/issues/1665 + if (item instanceof Object[]) { + return arguments((Object[]) item); + } + + // Pass everything else "as is". + return arguments(item); + } + +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvArgumentsProvider.java index 2a93e39849c7..a924cf637229 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvArgumentsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileArgumentsProvider.java index 104ec7eed6d4..39d286408aa0 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileArgumentsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSource.java index 4638ea1281e2..77ad9245fc54 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSource.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -14,6 +14,7 @@ import java.lang.annotation.Documented; import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -21,9 +22,9 @@ import org.apiguardian.api.API; /** - * {@code @CsvFileSource} is an {@link ArgumentsSource} which is used to load - * comma-separated value (CSV) files from one or more classpath {@link #resources} - * or {@link #files}. + * {@code @CsvFileSource} is a {@linkplain Repeatable repeatable} + * {@link ArgumentsSource} which is used to load comma-separated value (CSV) + * files from one or more classpath {@link #resources} or {@link #files}. * *

The CSV records parsed from these resources and files will be provided as * arguments to the annotated {@code @ParameterizedTest} method. Note that the @@ -63,6 +64,7 @@ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented +@Repeatable(CsvFileSources.class) @API(status = STABLE, since = "5.7") @ArgumentsSource(CsvFileArgumentsProvider.class) @SuppressWarnings("exports") @@ -204,7 +206,8 @@ /** * The maximum number of characters allowed per CSV column. * - *

Must be a positive number. + *

Must be a positive number or {@code -1} to allow an unlimited number + * of characters. * *

Defaults to {@code 4096}. * diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSources.java new file mode 100644 index 000000000000..92928d0ff715 --- /dev/null +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSources.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params.provider; + +import static org.apiguardian.api.API.Status.STABLE; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @CsvFileSources} is a simple container for one or more + * {@link CsvFileSource} annotations. + * + *

Note, however, that use of the {@code @CsvFileSources} container is completely + * optional since {@code @CsvFileSource} is a {@linkplain java.lang.annotation.Repeatable + * repeatable} annotation. + * + * @since 5.11 + * @see CsvFileSource + * @see java.lang.annotation.Repeatable + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@API(status = STABLE, since = "5.11") +public @interface CsvFileSources { + + /** + * An array of one or more {@link CsvFileSource @CsvFileSource} + * annotations. + */ + CsvFileSource[] value(); +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvParserFactory.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvParserFactory.java index b31f522309f6..d7ffee880cbc 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvParserFactory.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvParserFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -77,8 +77,8 @@ private static CsvParserSettings createParserSettings(String delimiter, String l settings.setAutoConfigurationEnabled(false); settings.setIgnoreLeadingWhitespaces(ignoreLeadingAndTrailingWhitespace); settings.setIgnoreTrailingWhitespaces(ignoreLeadingAndTrailingWhitespace); - Preconditions.condition(maxCharsPerColumn > 0, - () -> "maxCharsPerColumn must be a positive number: " + maxCharsPerColumn); + Preconditions.condition(maxCharsPerColumn > 0 || maxCharsPerColumn == -1, + () -> "maxCharsPerColumn must be a positive number or -1: " + maxCharsPerColumn); settings.setMaxCharsPerColumn(maxCharsPerColumn); // Do not use the built-in support for skipping rows/lines since it will // throw an IllegalArgumentException if the file does not contain at least diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvParsingException.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvParsingException.java index 252670610966..e0d2275b15c4 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvParsingException.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvParsingException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSource.java index 8b3a0bb81fdf..6ee1c92e7c10 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSource.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -14,6 +14,7 @@ import java.lang.annotation.Documented; import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -21,9 +22,10 @@ import org.apiguardian.api.API; /** - * {@code @CsvSource} is an {@link ArgumentsSource} which reads comma-separated - * values (CSV) from one or more CSV records supplied via the {@link #value} - * attribute or {@link #textBlock} attribute. + * {@code @CsvSource} is a {@linkplain Repeatable repeatable} + * {@link ArgumentsSource} which reads comma-separated values (CSV) from one + * or more CSV records supplied via the {@link #value} attribute or + * {@link #textBlock} attribute. * *

The supplied values will be provided as arguments to the annotated * {@code @ParameterizedTest} method. @@ -64,6 +66,7 @@ */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) +@Repeatable(CsvSources.class) @Documented @API(status = STABLE, since = "5.7") @ArgumentsSource(CsvArgumentsProvider.class) @@ -258,7 +261,8 @@ /** * The maximum number of characters allowed per CSV column. * - *

Must be a positive number. + *

Must be a positive number or {@code -1} to allow an unlimited number + * of characters. * *

Defaults to {@code 4096}. * diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSources.java new file mode 100644 index 000000000000..297a4a8ddda5 --- /dev/null +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSources.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params.provider; + +import static org.apiguardian.api.API.Status.STABLE; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @CsvSources} is a simple container for one or more + * {@link CsvSource} annotations. + * + *

Note, however, that use of the {@code @CsvSources} container is completely + * optional since {@code @CsvSource} is a {@linkplain java.lang.annotation.Repeatable + * repeatable} annotation. + * + * @since 5.11 + * @see CsvSource + * @see java.lang.annotation.Repeatable + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@API(status = STABLE, since = "5.11") +public @interface CsvSources { + + /** + * An array of one or more {@link CsvSource @CsvSource} + * annotations. + */ + CsvSource[] value(); +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java index dc0f82b5b37f..101e5bfc6916 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptySource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptySource.java index 4b628171deca..780a91dee02a 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptySource.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumArgumentsProvider.java index 303d3dab71ca..0a525289e566 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumArgumentsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSource.java index ee98d2e5322a..3bf7e9b88e5e 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSource.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -16,6 +16,7 @@ import java.lang.annotation.Documented; import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -29,8 +30,8 @@ import org.junit.platform.commons.util.Preconditions; /** - * {@code @EnumSource} is an {@link ArgumentsSource} for constants of - * an {@link Enum}. + * {@code @EnumSource} is a {@linkplain Repeatable repeatable} + * {@link ArgumentsSource} for constants of an {@link Enum}. * *

The enum constants will be provided as arguments to the annotated * {@code @ParameterizedTest} method. @@ -49,6 +50,7 @@ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented +@Repeatable(EnumSources.class) @API(status = STABLE, since = "5.7") @ArgumentsSource(EnumArgumentsProvider.class) @SuppressWarnings("exports") diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSources.java new file mode 100644 index 000000000000..6b1a30a68ed5 --- /dev/null +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSources.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params.provider; + +import static org.apiguardian.api.API.Status.STABLE; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @EnumSources} is a simple container for one or more + * {@link EnumSource} annotations. + * + *

Note, however, that use of the {@code @EnumSources} container is completely + * optional since {@code @EnumSource} is a {@linkplain java.lang.annotation.Repeatable + * repeatable} annotation. + * + * @since 5.11 + * @see EnumSource + * @see java.lang.annotation.Repeatable + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@API(status = STABLE, since = "5.11") +public @interface EnumSources { + + /** + * An array of one or more {@link EnumSource @EnumSource} + * annotations. + */ + EnumSource[] value(); +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldArgumentsProvider.java new file mode 100644 index 000000000000..c385d1bb3ba9 --- /dev/null +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldArgumentsProvider.java @@ -0,0 +1,159 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params.provider; + +import static java.lang.String.format; +import static java.util.Arrays.stream; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Iterator; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.BaseStream; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.platform.commons.JUnitException; +import org.junit.platform.commons.util.ClassLoaderUtils; +import org.junit.platform.commons.util.CollectionUtils; +import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.commons.util.ReflectionUtils; +import org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode; + +/** + * {@link ArgumentsProvider} for {@link FieldSource @FieldSource}. + * + * @since 5.11 + */ +class FieldArgumentsProvider extends AnnotationBasedArgumentsProvider { + + @Override + protected Stream provideArguments(ExtensionContext context, FieldSource fieldSource) { + Class testClass = context.getRequiredTestClass(); + Object testInstance = context.getTestInstance().orElse(null); + String[] fieldNames = fieldSource.value(); + if (fieldNames.length == 0) { + fieldNames = new String[] { context.getRequiredTestMethod().getName() }; + } + // @formatter:off + return stream(fieldNames) + .map(fieldName -> findField(testClass, fieldName)) + .map(field -> validateField(field, testInstance)) + .map(field -> readField(field, testInstance)) + .flatMap(fieldValue -> { + if (fieldValue instanceof Supplier) { + fieldValue = ((Supplier) fieldValue).get(); + } + return CollectionUtils.toStream(fieldValue); + }) + .map(ArgumentsUtils::toArguments); + // @formatter:on + } + + // package-private for testing + static Field findField(Class testClass, String fieldName) { + Preconditions.notBlank(fieldName, "Field name must not be blank"); + fieldName = fieldName.trim(); + + Class clazz = testClass; + if (fieldName.contains("#") || fieldName.contains(".")) { + String[] fieldParts = ReflectionUtils.parseFullyQualifiedFieldName(fieldName); + String className = fieldParts[0]; + fieldName = fieldParts[1]; + ClassLoader classLoader = ClassLoaderUtils.getClassLoader(testClass); + clazz = ReflectionUtils.loadRequiredClass(className, classLoader); + } + + Class resolvedClass = clazz; + String resolvedFieldName = fieldName; + Predicate nameMatches = field -> field.getName().equals(resolvedFieldName); + Field field = ReflectionUtils.streamFields(resolvedClass, nameMatches, HierarchyTraversalMode.BOTTOM_UP)// + .findFirst()// + .orElse(null); + + Preconditions.notNull(field, + () -> format("Could not find field named [%s] in class [%s]", resolvedFieldName, resolvedClass.getName())); + return field; + } + + private static Field validateField(Field field, Object testInstance) { + Preconditions.condition(field.getDeclaringClass().isInstance(testInstance) || ReflectionUtils.isStatic(field), + () -> format("Field '%s' must be static: local @FieldSource fields must be static " + + "unless the PER_CLASS @TestInstance lifecycle mode is used; " + + "external @FieldSource fields must always be static.", + field.toGenericString())); + return field; + } + + private static Object readField(Field field, Object testInstance) { + Object value = ReflectionUtils.tryToReadFieldValue(field, testInstance).getOrThrow( + cause -> new JUnitException(format("Could not read field [%s]", field.getName()), cause)); + + String fieldName = field.getName(); + String declaringClass = field.getDeclaringClass().getName(); + + Preconditions.notNull(value, + () -> format("The value of field [%s] in class [%s] must not be null", fieldName, declaringClass)); + + Preconditions.condition(!(value instanceof BaseStream), + () -> format("The value of field [%s] in class [%s] must not be a stream", fieldName, declaringClass)); + + Preconditions.condition(!(value instanceof Iterator), + () -> format("The value of field [%s] in class [%s] must not be an Iterator", fieldName, declaringClass)); + + Preconditions.condition(isConvertibleToStream(field, value), + () -> format("The value of field [%s] in class [%s] must be convertible to a Stream", fieldName, + declaringClass)); + + return value; + } + + /** + * Determine if the supplied value can be converted into a {@code Stream} or + * if the declared type of the supplied field is a {@link Supplier} of a type + * that can be converted into a {@code Stream}. + */ + private static boolean isConvertibleToStream(Field field, Object value) { + // Check actual value type. + if (CollectionUtils.isConvertibleToStream(value.getClass())) { + return true; + } + + // Check declared type T of Supplier. + if (Supplier.class.isAssignableFrom(field.getType())) { + Type genericType = field.getGenericType(); + if (genericType instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) genericType; + Type[] typeArguments = parameterizedType.getActualTypeArguments(); + if (typeArguments.length == 1) { + Type type = typeArguments[0]; + // Handle cases such as Supplier + if (type instanceof Class) { + Class clazz = (Class) type; + return CollectionUtils.isConvertibleToStream(clazz); + } + // Handle cases such as Supplier> + if (type instanceof ParameterizedType) { + Type rawType = ((ParameterizedType) type).getRawType(); + if (rawType instanceof Class) { + Class clazz = (Class) rawType; + return CollectionUtils.isConvertibleToStream(clazz); + } + } + } + } + } + return false; + } + +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSource.java new file mode 100644 index 000000000000..77680a00b7d1 --- /dev/null +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSource.java @@ -0,0 +1,140 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params.provider; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; +import org.junit.jupiter.params.ParameterizedTest; + +/** + * {@code @FieldSource} is a {@linkplain Repeatable repeatable} + * {@link ArgumentsSource} which provides access to values of + * {@linkplain #value() fields} of the class in which this annotation is declared + * or from static fields in external classes referenced by fully qualified + * field name. + * + *

Each field must be able to supply a stream of arguments, + * and each set of "arguments" within the "stream" will be provided as the physical + * arguments for individual invocations of the annotated + * {@link ParameterizedTest @ParameterizedTest} method. + * + *

In this context, a "stream" is anything that JUnit can reliably convert to + * a {@link java.util.stream.Stream Stream}; however, the actual concrete field + * type can take on many forms. Generally speaking this translates to a + * {@link java.util.Collection Collection}, an {@link Iterable}, a + * {@link java.util.function.Supplier Supplier} of a stream + * ({@link java.util.stream.Stream Stream}, + * {@link java.util.stream.DoubleStream DoubleStream}, + * {@link java.util.stream.LongStream LongStream}, or + * {@link java.util.stream.IntStream IntStream}), a {@code Supplier} of an + * {@link java.util.Iterator Iterator}, an array of objects, or an array of + * primitives. Each set of "arguments" within the "stream" can be supplied as an + * instance of {@link Arguments}, an array of objects (for example, {@code Object[]}, + * {@code String[]}, etc.), or a single value if the parameterized test + * method accepts a single argument. + * + *

In contrast to the supported return types for {@link MethodSource @MethodSource} + * factory methods, the value of a {@code @FieldSource} field cannot be an instance of + * {@link java.util.stream.Stream Stream}, + * {@link java.util.stream.DoubleStream DoubleStream}, + * {@link java.util.stream.LongStream LongStream}, + * {@link java.util.stream.IntStream IntStream}, or + * {@link java.util.Iterator Iterator}, since the values of such types are + * consumed the first time they are processed. However, if you wish to + * use one of these types, you can wrap it in a {@code Supplier} — for + * example, {@code Supplier}. + * + *

Please note that a one-dimensional array of objects supplied as a set of + * "arguments" will be handled differently than other types of arguments. + * Specifically, all of the elements of a one-dimensional array of objects will + * be passed as individual physical arguments to the {@code @ParameterizedTest} + * method. This behavior can be seen in the table below for the + * {@code Supplier> objectArrayStreamSupplier} field: the + * {@code @ParameterizedTest} method accepts individual {@code String} and + * {@code int} arguments rather than a single {@code Object[]} array. In contrast, + * any multidimensional array supplied as a set of "arguments" will be passed as + * a single physical argument to the {@code @ParameterizedTest} method without + * modification. This behavior can be seen in the table below for the + * {@code Supplier> twoDimensionalIntArrayStreamSupplier} and + * {@code Supplier> twoDimensionalObjectArrayStreamSupplier} + * fields: the {@code @ParameterizedTest} methods for those fields accept individual + * {@code int[][]} and {@code Object[][]} arguments, respectively. + * + *

Examples

+ * + *

The following table displays compatible method signatures for parameterized + * test methods and their corresponding {@code @FieldSource} fields. + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Compatible method signatures and field declarations
{@code @ParameterizedTest} method{@code @FieldSource} field
{@code void test(String)}{@code static List listOfStrings}
{@code void test(String)}{@code static String[] arrayOfStrings}
{@code void test(int)}{@code static int[] intArray}
{@code void test(int[])}{@code static int[][] twoDimensionalIntArray}
{@code void test(String, String)}{@code static String[][] twoDimensionalStringArray}
{@code void test(String, int)}{@code static Object[][] twoDimensionalObjectArray}
{@code void test(int)}{@code static Supplier intStreamSupplier}
{@code void test(String)}{@code static Supplier> stringStreamSupplier}
{@code void test(String, int)}{@code static Supplier> objectArrayStreamSupplier}
{@code void test(String, int)}{@code static Supplier> argumentsStreamSupplier}
{@code void test(int[])}{@code static Supplier> intArrayStreamSupplier}
{@code void test(int[][])}{@code static Supplier> twoDimensionalIntArrayStreamSupplier}
{@code void test(Object[][])}{@code static Supplier> twoDimensionalObjectArrayStreamSupplier}
+ * + *

Fields within the test class must be {@code static} unless the + * {@link org.junit.jupiter.api.TestInstance.Lifecycle#PER_CLASS PER_CLASS} + * test instance lifecycle mode is used; whereas, fields in external classes must + * always be {@code static}. + * + * @since 5.11 + * @see MethodSource + * @see Arguments + * @see ArgumentsSource + * @see ParameterizedTest + * @see org.junit.jupiter.api.TestInstance + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Repeatable(FieldSources.class) +@API(status = EXPERIMENTAL, since = "5.11") +@ArgumentsSource(FieldArgumentsProvider.class) +@SuppressWarnings("exports") +public @interface FieldSource { + + /** + * The names of fields within the test class or in external classes to use + * as sources for arguments. + * + *

Fields in external classes must be referenced by fully qualified + * field name — for example, + * {@code "com.example.WebUtils#httpMethodNames"} or + * {@code "com.example.TopLevelClass$NestedClass#numbers"} for a field in a + * static nested class. + * + *

If no field names are declared, a field within the test class that has + * the same name as the test method will be used as the field by default. + * + *

For further information, see the {@linkplain FieldSource class-level Javadoc}. + */ + String[] value() default {}; + +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSources.java new file mode 100644 index 000000000000..36e9cd57d604 --- /dev/null +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSources.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params.provider; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @FieldSources} is a simple container for one or more + * {@link FieldSource} annotations. + * + *

Note, however, that use of the {@code @FieldSources} container is completely + * optional since {@code @FieldSource} is a {@linkplain java.lang.annotation.Repeatable + * repeatable} annotation. + * + * @since 5.11 + * @see FieldSource + * @see java.lang.annotation.Repeatable + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@API(status = EXPERIMENTAL, since = "5.11") +public @interface FieldSources { + + /** + * An array of one or more {@link FieldSource @FieldSource} + * annotations. + */ + FieldSource[] value(); +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java index ed5ba8db51ca..5915602fba95 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,7 +13,6 @@ import static java.lang.String.format; import static java.util.Arrays.stream; import static java.util.stream.Collectors.toList; -import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.junit.platform.commons.util.AnnotationUtils.isAnnotated; import static org.junit.platform.commons.util.CollectionUtils.isConvertibleToStream; @@ -26,7 +25,6 @@ import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.util.ClassLoaderUtils; import org.junit.platform.commons.util.CollectionUtils; @@ -54,7 +52,7 @@ protected Stream provideArguments(ExtensionContext context, .map(factoryMethod -> validateFactoryMethod(factoryMethod, testInstance)) .map(factoryMethod -> context.getExecutableInvoker().invoke(factoryMethod, testInstance)) .flatMap(CollectionUtils::toStream) - .map(MethodArgumentsProvider::toArguments); + .map(ArgumentsUtils::toArguments); // @formatter:on } @@ -68,12 +66,12 @@ private static Method findFactoryMethod(Class testClass, Method testMethod, S return findFactoryMethodBySimpleName(testClass, testMethod, factoryMethodName); } - // Convert local factory method name to fully-qualified method name. + // Convert local factory method name to fully qualified method name. if (!looksLikeAFullyQualifiedMethodName(factoryMethodName)) { factoryMethodName = testClass.getName() + "#" + factoryMethodName; } - // Find factory method using fully-qualified name. + // Find factory method using fully qualified name. Method factoryMethod = findFactoryMethodByFullyQualifiedName(testClass, testMethod, factoryMethodName); // Ensure factory method has a valid return type and is not a test method. @@ -98,9 +96,9 @@ private static boolean looksLikeAFullyQualifiedMethodName(String factoryMethodNa return indexOfFirstDot < indexOfLastOpeningParenthesis; } // If we get this far, we conclude the supplied factory method name "looks" - // like it was intended to be a fully-qualified method name, even if the + // like it was intended to be a fully qualified method name, even if the // syntax is invalid. We do this in order to provide better diagnostics for - // the user when a fully-qualified method name is in fact invalid. + // the user when a fully qualified method name is in fact invalid. return true; } @@ -112,7 +110,7 @@ static Method findFactoryMethodByFullyQualifiedName(Class testClass, Method t String methodName = methodParts[1]; String methodParameters = methodParts[2]; ClassLoader classLoader = ClassLoaderUtils.getClassLoader(testClass); - Class clazz = loadRequiredClass(className, classLoader); + Class clazz = ReflectionUtils.loadRequiredClass(className, classLoader); // Attempt to find an exact match first. Method factoryMethod = ReflectionUtils.findMethod(clazz, methodName, methodParameters).orElse(null); @@ -174,11 +172,6 @@ private static boolean isTestMethod(Method candidate) { || isAnnotated(candidate, TestFactory.class); } - private static Class loadRequiredClass(String className, ClassLoader classLoader) { - return ReflectionUtils.tryToLoadClass(className, classLoader).getOrThrow( - cause -> new JUnitException(format("Could not load class [%s]", className), cause)); - } - private static Method validateFactoryMethod(Method factoryMethod, Object testInstance) { Preconditions.condition( factoryMethod.getDeclaringClass().isInstance(testInstance) || ReflectionUtils.isStatic(factoryMethod), @@ -189,27 +182,4 @@ private static Method validateFactoryMethod(Method factoryMethod, Object testIns return factoryMethod; } - private static Arguments toArguments(Object item) { - - // Nothing to do except cast. - if (item instanceof Arguments) { - return (Arguments) item; - } - - // Pass all multidimensional arrays "as is", in contrast to Object[]. - // See https://github.com/junit-team/junit5/issues/1665 - if (ReflectionUtils.isMultidimensionalArray(item)) { - return arguments(item); - } - - // Special treatment for one-dimensional reference arrays. - // See https://github.com/junit-team/junit5/issues/1665 - if (item instanceof Object[]) { - return arguments((Object[]) item); - } - - // Pass everything else "as is". - return arguments(item); - } - } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSource.java index 3c8a39a0898e..977e7555a5d2 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSource.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -14,6 +14,7 @@ import java.lang.annotation.Documented; import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -22,10 +23,11 @@ import org.junit.jupiter.params.ParameterizedTest; /** - * {@code @MethodSource} is an {@link ArgumentsSource} which provides access - * to values returned from {@linkplain #value() factory methods} of the class in - * which this annotation is declared or from static factory methods in external - * classes referenced by fully qualified method name. + * {@code @MethodSource} is a {@linkplain Repeatable repeatable} + * {@link ArgumentsSource} which provides access to values returned from + * {@linkplain #value() factory methods} of the class in which this annotation + * is declared or from static factory methods in external classes referenced + * by fully qualified method name. * *

Each factory method must generate a stream of arguments, * and each set of "arguments" within the "stream" will be provided as the physical @@ -94,6 +96,7 @@ * implementations of {@link org.junit.jupiter.api.extension.ParameterResolver}. * * @since 5.0 + * @see FieldSource * @see Arguments * @see ArgumentsSource * @see ParameterizedTest @@ -102,6 +105,7 @@ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented +@Repeatable(MethodSources.class) @API(status = STABLE, since = "5.7") @ArgumentsSource(MethodArgumentsProvider.class) @SuppressWarnings("exports") @@ -112,7 +116,7 @@ * to use as sources for arguments. * *

Factory methods in external classes must be referenced by - * fully-qualified method name — for example, + * fully qualified method name — for example, * {@code "com.example.StringsProviders#blankStrings"} or * {@code "com.example.TopLevelClass$NestedClass#classMethod"} for a factory * method in a static nested class. @@ -122,7 +126,7 @@ * you can supply the formal parameter list in the qualified method name to * disambiguate between overloaded variants of the factory method. For example, * {@code "blankStrings(int)"} for a local qualified method name or - * {@code "com.example.StringsProviders#blankStrings(int)"} for a fully-qualified + * {@code "com.example.StringsProviders#blankStrings(int)"} for a fully qualified * method name. * *

If no factory method names are declared, a method within the test class diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSources.java new file mode 100644 index 000000000000..33fa077567ee --- /dev/null +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSources.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params.provider; + +import static org.apiguardian.api.API.Status.STABLE; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @MethodSources} is a simple container for one or more + * {@link MethodSource} annotations. + * + *

Note, however, that use of the {@code @MethodSources} container is completely + * optional since {@code @MethodSource} is a {@linkplain java.lang.annotation.Repeatable + * repeatable} annotation. + * + * @since 5.11 + * @see MethodSource + * @see java.lang.annotation.Repeatable + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@API(status = STABLE, since = "5.11") +public @interface MethodSources { + + /** + * An array of one or more {@link MethodSource @MethodSource} + * annotations. + */ + MethodSource[] value(); +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullAndEmptySource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullAndEmptySource.java index ebbd4162ae63..c5571d3e52f0 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullAndEmptySource.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullAndEmptySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullArgumentsProvider.java index 569dffded498..e654ed6b2b47 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullArgumentsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullEnum.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullEnum.java index f040c26c9c83..1d722287b430 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullEnum.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullEnum.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullSource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullSource.java index c1b82b4842e4..f842cbde2a6c 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullSource.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueArgumentsProvider.java index 21ef1f176cba..e42d448a7415 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueArgumentsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSource.java index e349a07893ff..bc0ed303e935 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSource.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -14,6 +14,7 @@ import java.lang.annotation.Documented; import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -21,8 +22,8 @@ import org.apiguardian.api.API; /** - * {@code @ValueSource} is an {@link ArgumentsSource} which provides access to - * an array of literal values. + * {@code @ValueSource} is a {@linkplain Repeatable repeatable} + * {@link ArgumentsSource} which provides access to an array of literal values. * *

Supported types include {@link #shorts}, {@link #bytes}, {@link #ints}, * {@link #longs}, {@link #floats}, {@link #doubles}, {@link #chars}, @@ -40,6 +41,7 @@ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented +@Repeatable(ValueSources.class) @API(status = STABLE, since = "5.7") @ArgumentsSource(ValueArgumentsProvider.class) @SuppressWarnings("exports") diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSources.java new file mode 100644 index 000000000000..9b3a489c6a66 --- /dev/null +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSources.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params.provider; + +import static org.apiguardian.api.API.Status.STABLE; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @ValueSources} is a simple container for one or more + * {@link ValueSource} annotations. + * + *

Note, however, that use of the {@code @ValueSources} container is completely + * optional since {@code @ValueSource} is a {@linkplain java.lang.annotation.Repeatable + * repeatable} annotation. + * + * @since 5.11 + * @see ValueSource + * @see java.lang.annotation.Repeatable + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@API(status = STABLE, since = "5.11") +public @interface ValueSources { + + /** + * An array of one or more {@link ValueSource @ValueSource} + * annotations. + */ + ValueSource[] value(); +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/AnnotationConsumer.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/AnnotationConsumer.java index 31141c242dad..6d53313477ec 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/AnnotationConsumer.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/AnnotationConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/AnnotationConsumerInitializer.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/AnnotationConsumerInitializer.java index b495f79c5b50..9296c70deada 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/AnnotationConsumerInitializer.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/AnnotationConsumerInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,19 +11,23 @@ package org.junit.jupiter.params.support; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; import static org.apiguardian.api.API.Status.INTERNAL; +import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation; +import static org.junit.platform.commons.util.AnnotationUtils.findRepeatableAnnotations; import static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP; import static org.junit.platform.commons.util.ReflectionUtils.findMethods; import java.lang.annotation.Annotation; +import java.lang.annotation.Repeatable; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; +import java.util.Collections; import java.util.List; import java.util.function.Predicate; import org.apiguardian.api.API; import org.junit.platform.commons.JUnitException; -import org.junit.platform.commons.util.AnnotationUtils; /** * {@code AnnotationConsumerInitializer} is an internal helper class for @@ -47,14 +51,27 @@ private AnnotationConsumerInitializer() { public static T initialize(AnnotatedElement annotatedElement, T annotationConsumerInstance) { if (annotationConsumerInstance instanceof AnnotationConsumer) { Class annotationType = findConsumedAnnotationType(annotationConsumerInstance); - Annotation annotation = AnnotationUtils.findAnnotation(annotatedElement, annotationType) // - .orElseThrow(() -> new JUnitException(annotationConsumerInstance.getClass().getName() - + " must be used with an annotation of type " + annotationType.getName())); - initializeAnnotationConsumer((AnnotationConsumer) annotationConsumerInstance, annotation); + List annotations = findAnnotations(annotatedElement, annotationType); + + if (annotations.isEmpty()) { + throw new JUnitException(annotationConsumerInstance.getClass().getName() + + " must be used with an annotation of type " + annotationType.getName()); + } + + annotations.forEach(annotation -> initializeAnnotationConsumer( + (AnnotationConsumer) annotationConsumerInstance, annotation)); } return annotationConsumerInstance; } + private static List findAnnotations(AnnotatedElement annotatedElement, + Class annotationType) { + + return annotationType.isAnnotationPresent(Repeatable.class) + ? findRepeatableAnnotations(annotatedElement, annotationType) + : findAnnotation(annotatedElement, annotationType).map(Collections::singletonList).orElse(emptyList()); + } + private static Class findConsumedAnnotationType(T annotationConsumerInstance) { Predicate consumesAnnotation = annotationConsumingMethodSignatures.stream() // .map(signature -> (Predicate) signature::isMatchingWith) // diff --git a/junit-jupiter-params/src/main/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessor.kt b/junit-jupiter-params/src/main/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessor.kt index bd1ef56d1169..0c27b52ec08a 100644 --- a/junit-jupiter-params/src/main/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessor.kt +++ b/junit-jupiter-params/src/main/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/README.md b/junit-jupiter-params/src/test/README.md new file mode 100644 index 000000000000..86ed9e49af1d --- /dev/null +++ b/junit-jupiter-params/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `jupiter-tests` project. diff --git a/junit-jupiter-params/src/test/resources/log4j2-test.xml b/junit-jupiter-params/src/test/resources/log4j2-test.xml deleted file mode 100644 index 1c1cbb8d6acd..000000000000 --- a/junit-jupiter-params/src/test/resources/log4j2-test.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/junit-platform-commons/junit-platform-commons.gradle.kts b/junit-platform-commons/junit-platform-commons.gradle.kts index bb6a7c336eab..ef52ce77ab56 100644 --- a/junit-platform-commons/junit-platform-commons.gradle.kts +++ b/junit-platform-commons/junit-platform-commons.gradle.kts @@ -1,9 +1,8 @@ -import junitbuild.java.ExecJarAction +import junitbuild.java.UpdateJarAction plugins { id("junitbuild.java-library-conventions") id("junitbuild.java-multi-release-sources") - id("junitbuild.java-repackage-jars") `java-test-fixtures` } @@ -18,10 +17,9 @@ dependencies { tasks.jar { val release9ClassesDir = sourceSets.mainRelease9.get().output.classesDirs.singleFile inputs.dir(release9ClassesDir).withPathSensitivity(PathSensitivity.RELATIVE) - doLast(objects.newInstance(ExecJarAction::class).apply { + doLast(objects.newInstance(UpdateJarAction::class).apply { javaLauncher = javaToolchains.launcherFor(java.toolchain) args.addAll( - "--update", "--file", archiveFile.get().asFile.absolutePath, "--release", "9", "-C", release9ClassesDir.absolutePath, "." @@ -31,6 +29,7 @@ tasks.jar { tasks.codeCoverageClassesJar { exclude("org/junit/platform/commons/util/ModuleUtils.class") + exclude("org/junit/platform/commons/util/PackageNameUtils.class") } eclipse { diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/JUnitException.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/JUnitException.java index 20c6d94370d4..1b8bf98a7f1e 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/JUnitException.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/JUnitException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/PreconditionViolationException.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/PreconditionViolationException.java index e858ad26b13d..1c2f63ac5967 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/PreconditionViolationException.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/PreconditionViolationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/annotation/Testable.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/annotation/Testable.java index 476289b28110..0adb7c211964 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/annotation/Testable.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/annotation/Testable.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/function/Try.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/function/Try.java index 9d7c5997b532..84d5c7a173e9 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/function/Try.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/function/Try.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LogRecordListener.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LogRecordListener.java index 3b0c357f5090..1368ab3d834e 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LogRecordListener.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LogRecordListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/Logger.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/Logger.java index 5283e18d3b25..016317e7289c 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/Logger.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/Logger.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LoggerFactory.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LoggerFactory.java index 5e06b26fb32d..eabab7e93ae6 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LoggerFactory.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LoggerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/AnnotationSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/AnnotationSupport.java index 8764d2d70670..92ee263134c8 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/AnnotationSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/AnnotationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -262,8 +262,8 @@ public static List findPublicAnnotatedFields(Class clazz, Class fie } /** - * Find all {@linkplain Field fields} of the supplied class or interface - * that are annotated or meta-annotated with the specified + * Find all distinct {@linkplain Field fields} of the supplied class or + * interface that are annotated or meta-annotated with the specified * {@code annotationType}, using top-down search semantics within the type * hierarchy. * @@ -289,8 +289,8 @@ public static List findAnnotatedFields(Class clazz, Classmeta-annotated with the specified + * Find all distinct {@linkplain Field fields} of the supplied class or + * interface that are annotated or meta-annotated with the specified * {@code annotationType} and match the specified {@code predicate}, using * top-down search semantics within the type hierarchy. * @@ -318,8 +318,8 @@ public static List findAnnotatedFields(Class clazz, Classmeta-annotated with the specified + * Find all distinct {@linkplain Field fields} of the supplied class or + * interface that are annotated or meta-annotated with the specified * {@code annotationType} and match the specified {@code predicate}, using * the supplied hierarchy traversal mode. * @@ -496,8 +496,8 @@ public static List findAnnotatedFieldValues(Class clazz, Classmeta-annotated with the specified + * Find all distinct {@linkplain Method methods} of the supplied class or + * interface that are annotated or meta-annotated with the specified * {@code annotationType}. * * @param clazz the class or interface in which to find the methods; never {@code null} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ClassSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ClassSupport.java index 5039fd87c7e1..afd5d7583404 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ClassSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ClassSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/HierarchyTraversalMode.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/HierarchyTraversalMode.java index aefcb1e41bf6..7d3aaa26e604 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/HierarchyTraversalMode.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/HierarchyTraversalMode.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -33,6 +33,6 @@ public enum HierarchyTraversalMode { /** * Traverse the hierarchy using bottom-up semantics. */ - BOTTOM_UP; + BOTTOM_UP } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ModifierSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ModifierSupport.java index 7dbb748931fa..da57dcb22986 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ModifierSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ModifierSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java index 21259a93a58a..6d63e015fccc 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -67,7 +67,6 @@ private ReflectionSupport() { */ @API(status = DEPRECATED, since = "1.4") @Deprecated - @SuppressWarnings("deprecation") public static Optional> loadClass(String name) { return ReflectionUtils.loadClass(name); } @@ -137,6 +136,28 @@ public static List> findAllClassesInClasspathRoot(URI root, PredicateThe classpath scanning algorithm searches recursively in subpackages + * beginning with the root of the classpath. + * + * @param root the URI for the classpath root in which to scan; never + * {@code null} + * @param resourceFilter the resource type filter; never {@code null} + * @return an immutable list of all such resources found; never {@code null} + * but potentially empty + * @since 1.11 + * @see #findAllResourcesInPackage(String, Predicate) + * @see #findAllResourcesInModule(String, Predicate) + */ + @API(status = EXPERIMENTAL, since = "1.11") + public static List findAllResourcesInClasspathRoot(URI root, Predicate resourceFilter) { + + return ReflectionUtils.findAllResourcesInClasspathRoot(root, resourceFilter); + } + /** * Find all {@linkplain Class classes} in the supplied classpath {@code root} * that match the specified {@code classFilter} and {@code classNameFilter} @@ -162,6 +183,28 @@ public static Stream> streamAllClassesInClasspathRoot(URI root, Predica return ReflectionUtils.streamAllClassesInClasspathRoot(root, classFilter, classNameFilter); } + /** + * Find all {@linkplain Resource resources} in the supplied classpath {@code root} + * that match the specified {@code resourceFilter} predicate. + * + *

The classpath scanning algorithm searches recursively in subpackages + * beginning with the root of the classpath. + * + * @param root the URI for the classpath root in which to scan; never + * {@code null} + * @param resourceFilter the resource type filter; never {@code null} + * @return a stream of all such classes found; never {@code null} + * but potentially empty + * @since 1.11 + * @see #streamAllResourcesInPackage(String, Predicate) + * @see #streamAllResourcesInModule(String, Predicate) + */ + @API(status = EXPERIMENTAL, since = "1.11") + public static Stream streamAllResourcesInClasspathRoot(URI root, Predicate resourceFilter) { + + return ReflectionUtils.streamAllResourcesInClasspathRoot(root, resourceFilter); + } + /** * Find all {@linkplain Class classes} in the supplied {@code basePackageName} * that match the specified {@code classFilter} and {@code classNameFilter} @@ -186,6 +229,29 @@ public static List> findAllClassesInPackage(String basePackageName, Pre return ReflectionUtils.findAllClassesInPackage(basePackageName, classFilter, classNameFilter); } + /** + * Find all {@linkplain Resource resources} in the supplied {@code basePackageName} + * that match the specified {@code resourceFilter} predicate. + * + *

The classpath scanning algorithm searches recursively in subpackages + * beginning within the supplied base package. + * + * @param basePackageName the name of the base package in which to start + * scanning; must not be {@code null} and must be valid in terms of Java + * syntax + * @param resourceFilter the resource type filter; never {@code null} + * @return an immutable list of all such classes found; never {@code null} + * but potentially empty + * @since 1.11 + * @see #findAllResourcesInClasspathRoot(URI, Predicate) + * @see #findAllResourcesInModule(String, Predicate) + */ + @API(status = EXPERIMENTAL, since = "1.11") + public static List findAllResourcesInPackage(String basePackageName, Predicate resourceFilter) { + + return ReflectionUtils.findAllResourcesInPackage(basePackageName, resourceFilter); + } + /** * Find all {@linkplain Class classes} in the supplied {@code basePackageName} * that match the specified {@code classFilter} and {@code classNameFilter} @@ -212,6 +278,30 @@ public static Stream> streamAllClassesInPackage(String basePackageName, return ReflectionUtils.streamAllClassesInPackage(basePackageName, classFilter, classNameFilter); } + /** + * Find all {@linkplain Resource resources} in the supplied {@code basePackageName} + * that match the specified {@code resourceFilter} predicate. + * + *

The classpath scanning algorithm searches recursively in subpackages + * beginning within the supplied base package. + * + * @param basePackageName the name of the base package in which to start + * scanning; must not be {@code null} and must be valid in terms of Java + * syntax + * @param resourceFilter the resource type filter; never {@code null} + * @return a stream of all such resources found; never {@code null} + * but potentially empty + * @since 1.11 + * @see #streamAllResourcesInClasspathRoot(URI, Predicate) + * @see #streamAllResourcesInModule(String, Predicate) + */ + @API(status = EXPERIMENTAL, since = "1.11") + public static Stream streamAllResourcesInPackage(String basePackageName, + Predicate resourceFilter) { + + return ReflectionUtils.streamAllResourcesInPackage(basePackageName, resourceFilter); + } + /** * Find all {@linkplain Class classes} in the supplied {@code moduleName} * that match the specified {@code classFilter} and {@code classNameFilter} @@ -236,6 +326,28 @@ public static List> findAllClassesInModule(String moduleName, Predicate return ReflectionUtils.findAllClassesInModule(moduleName, classFilter, classNameFilter); } + /** + * Find all {@linkplain Resource resources} in the supplied {@code moduleName} + * that match the specified {@code resourceFilter} predicate. + * + *

The module-path scanning algorithm searches recursively in all + * packages contained in the module. + * + * @param moduleName the name of the module to scan; never {@code null} or + * empty + * @param resourceFilter the resource type filter; never {@code null} + * @return an immutable list of all such resources found; never {@code null} + * but potentially empty + * @since 1.11 + * @see #findAllResourcesInClasspathRoot(URI, Predicate) + * @see #findAllResourcesInPackage(String, Predicate) + */ + @API(status = EXPERIMENTAL, since = "1.11") + public static List findAllResourcesInModule(String moduleName, Predicate resourceFilter) { + + return ReflectionUtils.findAllResourcesInModule(moduleName, resourceFilter); + } + /** * Find all {@linkplain Class classes} in the supplied {@code moduleName} * that match the specified {@code classFilter} and {@code classNameFilter} @@ -261,6 +373,28 @@ public static Stream> streamAllClassesInModule(String moduleName, Predi return ReflectionUtils.streamAllClassesInModule(moduleName, classFilter, classNameFilter); } + /** + * Find all {@linkplain Resource resources} in the supplied {@code moduleName} + * that match the specified {@code resourceFilter} predicate. + * + *

The module-path scanning algorithm searches recursively in all + * packages contained in the module. + * + * @param moduleName the name of the module to scan; never {@code null} or + * empty + * @param resourceFilter the resource type filter; never {@code null} + * @return a stream of all such resources found; never {@code null} + * but potentially empty + * @since 1.11 + * @see #streamAllResourcesInClasspathRoot(URI, Predicate) + * @see #streamAllResourcesInPackage(String, Predicate) + */ + @API(status = EXPERIMENTAL, since = "1.11") + public static Stream streamAllResourcesInModule(String moduleName, Predicate resourceFilter) { + + return ReflectionUtils.streamAllResourcesInModule(moduleName, resourceFilter); + } + /** * Create a new instance of the specified {@link Class} by invoking * the constructor whose argument list matches the types of the supplied @@ -298,13 +432,13 @@ public static Object invokeMethod(Method method, Object target, Object... args) } /** - * Find all {@linkplain Field fields} of the supplied class or interface - * that match the specified {@code predicate}. + * Find all distinct {@linkplain Field fields} of the supplied class or + * interface that match the specified {@code predicate}. * *

Fields declared in the same class or interface will be ordered using * an algorithm that is deterministic but intentionally nonobvious. * - *

The results will not contain fields that are hidden or + *

The results will not contain fields that are * {@linkplain Field#isSynthetic() synthetic}. * * @param clazz the class or interface in which to find the fields; never {@code null} @@ -325,13 +459,13 @@ public static List findFields(Class clazz, Predicate predicate, } /** - * Find all {@linkplain Field fields} of the supplied class or interface - * that match the specified {@code predicate}. + * Find all distinct {@linkplain Field fields} of the supplied class or + * interface that match the specified {@code predicate}. * *

Fields declared in the same class or interface will be ordered using * an algorithm that is deterministic but intentionally nonobvious. * - *

The results will not contain fields that are hidden or + *

The results will not contain fields that are * {@linkplain Field#isSynthetic() synthetic}. * * @param clazz the class or interface in which to find the fields; never {@code null} @@ -416,8 +550,7 @@ public static Optional findMethod(Class clazz, String methodName, Cla * Find all distinct {@linkplain Method methods} of the supplied class or * interface that match the specified {@code predicate}. * - *

The results will not contain instance methods that are overridden - * or {@code static} methods that are hidden. + *

The results will not contain methods that are overridden. * *

If you are looking for methods annotated with a certain annotation * type, consider using @@ -442,8 +575,7 @@ public static List findMethods(Class clazz, Predicate predica * Find all distinct {@linkplain Method methods} of the supplied class or * interface that match the specified {@code predicate}. * - *

The results will not contain instance methods that are overridden - * or {@code static} methods that are hidden. + *

The results will not contain methods that are overridden. * *

If you are looking for methods annotated with a certain annotation * type, consider using diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/Resource.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/Resource.java new file mode 100644 index 000000000000..c9587c3a5ca6 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/Resource.java @@ -0,0 +1,61 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.function.Predicate; + +import org.apiguardian.api.API; + +/** + * Represents a resource on the classpath. + * @since 1.11 + * @see ReflectionSupport#findAllResourcesInClasspathRoot(URI, Predicate) + * @see ReflectionSupport#findAllResourcesInPackage(String, Predicate) + * @see ReflectionSupport#findAllResourcesInModule(String, Predicate) + * @see ReflectionSupport#streamAllResourcesInClasspathRoot(URI, Predicate) + * @see ReflectionSupport#streamAllResourcesInPackage(String, Predicate) + * @see ReflectionSupport#streamAllResourcesInModule(String, Predicate) + */ +@API(status = EXPERIMENTAL, since = "1.11") +public interface Resource { + + /** + * Get the resource name. + *

+ * The resource name is a {@code /}-separated path. The path is relative to + * the classpath root in which the resource is located. + * + * @return the resource name; never {@code null} + */ + String getName(); + + /** + * Get URI to a resource. + * + * @return the uri of the resource; never {@code null} + */ + URI getUri(); + + /** + * Returns an input stream for reading this resource. + * + * @return an input stream for this resource; never {@code null} + * @throws IOException if an I/O exception occurs + */ + default InputStream getInputStream() throws IOException { + return getUri().toURL().openStream(); + } +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/SearchOption.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/SearchOption.java index 55b5ea5e2df9..8ade7401ed45 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/SearchOption.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/SearchOption.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -38,6 +38,6 @@ public enum SearchOption { * but also search the {@linkplain Class#getEnclosingClass() enclosing class} * hierarchy for inner classes (i.e., a non-static member classes). */ - INCLUDE_ENCLOSING_CLASSES; + INCLUDE_ENCLOSING_CLASSES } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionException.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionException.java new file mode 100644 index 000000000000..54ddc8e11093 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support.conversion; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import org.apiguardian.api.API; +import org.junit.platform.commons.JUnitException; + +/** + * {@code ConversionException} is an exception that can occur when an + * object is converted to another object. + * + * @since 1.11 + */ +@API(status = EXPERIMENTAL, since = "1.11") +public class ConversionException extends JUnitException { + + private static final long serialVersionUID = 1L; + + public ConversionException(String message) { + super(message); + } + + public ConversionException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionSupport.java new file mode 100644 index 000000000000..8845d617c7e0 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionSupport.java @@ -0,0 +1,144 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support.conversion; + +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.junit.platform.commons.util.ReflectionUtils.getWrapperType; + +import java.util.List; +import java.util.Optional; + +import org.apiguardian.api.API; +import org.junit.platform.commons.util.ClassLoaderUtils; + +/** + * {@code ConversionSupport} provides static utility methods for converting a + * given object into an instance of a specified type. + * + * @since 1.11 + */ +@API(status = EXPERIMENTAL, since = "1.11") +public final class ConversionSupport { + + private static final List stringToObjectConverters = unmodifiableList(asList( // + new StringToBooleanConverter(), // + new StringToCharacterConverter(), // + new StringToNumberConverter(), // + new StringToClassConverter(), // + new StringToEnumConverter(), // + new StringToJavaTimeConverter(), // + new StringToCommonJavaTypesConverter(), // + new FallbackStringToObjectConverter() // + )); + + private ConversionSupport() { + /* no-op */ + } + + /** + * Convert the supplied source {@code String} into an instance of the specified + * target type. + * + *

If the target type is {@code String}, the source {@code String} will not + * be modified. + * + *

Some forms of conversion require a {@link ClassLoader}. If none is + * provided, the {@linkplain ClassLoaderUtils#getDefaultClassLoader() default + * ClassLoader} will be used. + * + *

This method is able to convert strings into primitive types and their + * corresponding wrapper types ({@link Boolean}, {@link Character}, {@link Byte}, + * {@link Short}, {@link Integer}, {@link Long}, {@link Float}, and + * {@link Double}), enum constants, date and time types from the + * {@code java.time} package, as well as common Java types such as {@link Class}, + * {@link java.io.File}, {@link java.nio.file.Path}, {@link java.nio.charset.Charset}, + * {@link java.math.BigDecimal}, {@link java.math.BigInteger}, + * {@link java.util.Currency}, {@link java.util.Locale}, {@link java.util.UUID}, + * {@link java.net.URI}, and {@link java.net.URL}. + * + *

If the target type is not covered by any of the above, a convention-based + * conversion strategy will be used to convert the source {@code String} into the + * given target type by invoking a static factory method or factory constructor + * defined in the target type. The search algorithm used in this strategy is + * outlined below. + * + *

Search Algorithm

+ * + *
    + *
  1. Search for a single, non-private static factory method in the target + * type that converts from a String to the target type. Use the factory method + * if present.
  2. + *
  3. Search for a single, non-private constructor in the target type that + * accepts a String. Use the constructor if present.
  4. + *
+ * + *

If multiple suitable factory methods are discovered they will be ignored. + * If neither a single factory method nor a single constructor is found, the + * convention-based conversion strategy will not apply. + * + * @param source the source {@code String} to convert; may be {@code null} + * but only if the target type is a reference type + * @param targetType the target type the source should be converted into; + * never {@code null} + * @param classLoader the {@code ClassLoader} to use; may be {@code null} to + * use the default {@code ClassLoader} + * @param the type of the target + * @return the converted object; may be {@code null} but only if the target + * type is a reference type + * + * @since 1.11 + */ + @SuppressWarnings("unchecked") + public static T convert(String source, Class targetType, ClassLoader classLoader) { + if (source == null) { + if (targetType.isPrimitive()) { + throw new ConversionException( + "Cannot convert null to primitive value of type " + targetType.getTypeName()); + } + return null; + } + + if (String.class.equals(targetType)) { + return (T) source; + } + + Class targetTypeToUse = toWrapperType(targetType); + Optional converter = stringToObjectConverters.stream().filter( + candidate -> candidate.canConvertTo(targetTypeToUse)).findFirst(); + if (converter.isPresent()) { + try { + ClassLoader classLoaderToUse = classLoader != null ? classLoader + : ClassLoaderUtils.getDefaultClassLoader(); + return (T) converter.get().convert(source, targetTypeToUse, classLoaderToUse); + } + catch (Exception ex) { + if (ex instanceof ConversionException) { + // simply rethrow it + throw (ConversionException) ex; + } + // else + throw new ConversionException( + String.format("Failed to convert String \"%s\" to type %s", source, targetType.getTypeName()), ex); + } + } + + throw new ConversionException( + "No built-in converter for source type java.lang.String and target type " + targetType.getTypeName()); + } + + private static Class toWrapperType(Class targetType) { + Class wrapperType = getWrapperType(targetType); + return wrapperType != null ? wrapperType : targetType; + } + +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/FallbackStringToObjectConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverter.java similarity index 96% rename from junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/FallbackStringToObjectConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverter.java index 79f0028e58a6..680c6aa4712b 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/FallbackStringToObjectConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.jupiter.params.converter; +package org.junit.platform.commons.support.conversion; import static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP; import static org.junit.platform.commons.util.ReflectionUtils.findConstructors; @@ -48,8 +48,8 @@ * If neither a single factory method nor a single constructor is found, this * converter acts as a no-op. * - * @since 5.1 - * @see DefaultArgumentConverter + * @since 1.11 + * @see ConversionSupport */ class FallbackStringToObjectConverter implements StringToObjectConverter { @@ -70,7 +70,7 @@ class FallbackStringToObjectConverter implements StringToObjectConverter { = new ConcurrentHashMap<>(64); @Override - public boolean canConvert(Class targetType) { + public boolean canConvertTo(Class targetType) { return findFactoryExecutable(targetType) != NULL_EXECUTABLE; } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToBooleanConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToBooleanConverter.java similarity index 81% rename from junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToBooleanConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToBooleanConverter.java index 9d911825809b..fcab61e3a468 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToBooleanConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToBooleanConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -8,14 +8,14 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.jupiter.params.converter; +package org.junit.platform.commons.support.conversion; import org.junit.platform.commons.util.Preconditions; class StringToBooleanConverter implements StringToObjectConverter { @Override - public boolean canConvert(Class targetType) { + public boolean canConvertTo(Class targetType) { return targetType == Boolean.class; } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToCharacterConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCharacterConverter.java similarity index 79% rename from junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToCharacterConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCharacterConverter.java index b3849051e45d..548819a2641d 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToCharacterConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCharacterConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -8,14 +8,14 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.jupiter.params.converter; +package org.junit.platform.commons.support.conversion; import org.junit.platform.commons.util.Preconditions; class StringToCharacterConverter implements StringToObjectConverter { @Override - public boolean canConvert(Class targetType) { + public boolean canConvertTo(Class targetType) { return targetType == Character.class; } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToClassConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToClassConverter.java similarity index 81% rename from junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToClassConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToClassConverter.java index debefc342ed0..36ec2bb211a7 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToClassConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToClassConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -8,14 +8,14 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.jupiter.params.converter; +package org.junit.platform.commons.support.conversion; import org.junit.platform.commons.util.ReflectionUtils; class StringToClassConverter implements StringToObjectConverter { @Override - public boolean canConvert(Class targetType) { + public boolean canConvertTo(Class targetType) { return targetType == Class.class; } @@ -28,7 +28,7 @@ public Object convert(String source, Class targetType) throws Exception { public Object convert(String className, Class targetType, ClassLoader classLoader) throws Exception { // @formatter:off return ReflectionUtils.tryToLoadClass(className, classLoader) - .getOrThrow(cause -> new ArgumentConversionException( + .getOrThrow(cause -> new ConversionException( "Failed to convert String \"" + className + "\" to type java.lang.Class", cause)); // @formatter:on } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToCommonJavaTypesConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCommonJavaTypesConverter.java similarity index 86% rename from junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToCommonJavaTypesConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCommonJavaTypesConverter.java index b9ac124aded8..f89e67b5fc38 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToCommonJavaTypesConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCommonJavaTypesConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.jupiter.params.converter; +package org.junit.platform.commons.support.conversion; import static java.util.Collections.unmodifiableMap; @@ -49,7 +49,7 @@ class StringToCommonJavaTypesConverter implements StringToObjectConverter { } @Override - public boolean canConvert(Class targetType) { + public boolean canConvertTo(Class targetType) { return CONVERTERS.containsKey(targetType); } @@ -63,7 +63,7 @@ private static URL toURL(String url) { return URI.create(url).toURL(); } catch (MalformedURLException ex) { - throw new ArgumentConversionException("Failed to convert String \"" + url + "\" to type java.net.URL", ex); + throw new ConversionException("Failed to convert String \"" + url + "\" to type java.net.URL", ex); } } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToEnumConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToEnumConverter.java similarity index 77% rename from junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToEnumConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToEnumConverter.java index f20d1487d5ee..087bb653a974 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToEnumConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToEnumConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -8,12 +8,12 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.jupiter.params.converter; +package org.junit.platform.commons.support.conversion; class StringToEnumConverter implements StringToObjectConverter { @Override - public boolean canConvert(Class targetType) { + public boolean canConvertTo(Class targetType) { return targetType.isEnum(); } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToJavaTimeConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToJavaTimeConverter.java similarity index 92% rename from junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToJavaTimeConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToJavaTimeConverter.java index b1851ecc323d..753cfba69d89 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToJavaTimeConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToJavaTimeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.jupiter.params.converter; +package org.junit.platform.commons.support.conversion; import static java.util.Collections.unmodifiableMap; @@ -53,7 +53,7 @@ class StringToJavaTimeConverter implements StringToObjectConverter { } @Override - public boolean canConvert(Class targetType) { + public boolean canConvertTo(Class targetType) { return CONVERTERS.containsKey(targetType); } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToNumberConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToNumberConverter.java similarity index 90% rename from junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToNumberConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToNumberConverter.java index ca278019a488..55c373f7153c 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToNumberConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToNumberConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.jupiter.params.converter; +package org.junit.platform.commons.support.conversion; import static java.util.Collections.unmodifiableMap; @@ -38,7 +38,7 @@ class StringToNumberConverter implements StringToObjectConverter { } @Override - public boolean canConvert(Class targetType) { + public boolean canConvertTo(Class targetType) { return CONVERTERS.containsKey(targetType); } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToObjectConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToObjectConverter.java similarity index 78% rename from junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToObjectConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToObjectConverter.java index 2a60202dba81..b4247f0af832 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/StringToObjectConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToObjectConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.jupiter.params.converter; +package org.junit.platform.commons.support.conversion; /** * Internal API for converting arguments of type {@link String} to a specified @@ -21,12 +21,15 @@ interface StringToObjectConverter { * supplied target type (which is guaranteed to be a wrapper type for * primitives — for example, {@link Integer} instead of {@code int}). */ - boolean canConvert(Class targetType); + boolean canConvertTo(Class targetType); /** * Convert the supplied {@link String} to the supplied target type (which is * guaranteed to be a wrapper type for primitives — for example, * {@link Integer} instead of {@code int}). + * + *

This method will only be invoked in {@link #canConvertTo(Class)} + * returned {@code true} for the same target type. */ Object convert(String source, Class targetType) throws Exception; @@ -35,6 +38,9 @@ interface StringToObjectConverter { * guaranteed to be a wrapper type for primitives — for example, * {@link Integer} instead of {@code int}). * + *

This method will only be invoked in {@link #canConvertTo(Class)} + * returned {@code true} for the same target type. + * *

The default implementation simply delegates to {@link #convert(String, Class)}. * Can be overridden by concrete implementations of this interface that need * access to the supplied {@link ClassLoader}. diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/package-info.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/package-info.java new file mode 100644 index 000000000000..e51977179941 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/package-info.java @@ -0,0 +1,5 @@ +/** + * Maintained conversion APIs provided by the JUnit Platform. + */ + +package org.junit.platform.commons.support.conversion; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/AnnotationUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/AnnotationUtils.java index 2cd1bbcee225..5e7157b45c9b 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/AnnotationUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/AnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/BlacklistedExceptions.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/BlacklistedExceptions.java index 2c82dd500dd3..e03fa9d54029 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/BlacklistedExceptions.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/BlacklistedExceptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassFilter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassFilter.java index 63c15fb70da3..8b0728213055 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassFilter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassLoaderUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassLoaderUtils.java index 58c18302e41a..d90942e77c84 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassLoaderUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassLoaderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassNamePatternFilterUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassNamePatternFilterUtils.java index 020376d34545..6c713d8c6cb4 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassNamePatternFilterUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassNamePatternFilterUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassUtils.java index 41c44ddbf54e..720df1994b93 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassFileVisitor.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFileVisitor.java similarity index 54% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassFileVisitor.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFileVisitor.java index a6844206fe0d..7f2ad8195085 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassFileVisitor.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFileVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -17,32 +17,33 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; -import java.util.function.Consumer; +import java.util.function.BiConsumer; +import java.util.function.Predicate; import org.junit.platform.commons.logging.Logger; import org.junit.platform.commons.logging.LoggerFactory; /** - * @since 1.0 + * @since 1.11 */ -class ClassFileVisitor extends SimpleFileVisitor { +class ClasspathFileVisitor extends SimpleFileVisitor { - private static final Logger logger = LoggerFactory.getLogger(ClassFileVisitor.class); + private static final Logger logger = LoggerFactory.getLogger(ClasspathFileVisitor.class); - static final String CLASS_FILE_SUFFIX = ".class"; - private static final String PACKAGE_INFO_FILE_NAME = "package-info" + CLASS_FILE_SUFFIX; - private static final String MODULE_INFO_FILE_NAME = "module-info" + CLASS_FILE_SUFFIX; + private final Path basePath; + private final BiConsumer consumer; + private final Predicate filter; - private final Consumer classFileConsumer; - - ClassFileVisitor(Consumer classFileConsumer) { - this.classFileConsumer = classFileConsumer; + ClasspathFileVisitor(Path basePath, Predicate filter, BiConsumer consumer) { + this.basePath = basePath; + this.filter = filter; + this.consumer = consumer; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) { - if (isNotPackageInfo(file) && isNotModuleInfo(file) && isClassFile(file)) { - classFileConsumer.accept(file); + if (filter.test(file)) { + consumer.accept(basePath, file); } return CONTINUE; } @@ -61,16 +62,4 @@ public FileVisitResult postVisitDirectory(Path dir, IOException ex) { return CONTINUE; } - private static boolean isNotPackageInfo(Path path) { - return !path.endsWith(PACKAGE_INFO_FILE_NAME); - } - - private static boolean isNotModuleInfo(Path path) { - return !path.endsWith(MODULE_INFO_FILE_NAME); - } - - private static boolean isClassFile(Path file) { - return file.getFileName().toString().endsWith(CLASS_FILE_SUFFIX); - } - } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFilters.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFilters.java new file mode 100644 index 000000000000..7ad6cd3b0682 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFilters.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util; + +import java.nio.file.Path; +import java.util.function.Predicate; + +/** + * @since 1.11 + */ +class ClasspathFilters { + + static final String CLASS_FILE_SUFFIX = ".class"; + private static final String PACKAGE_INFO_FILE_NAME = "package-info" + CLASS_FILE_SUFFIX; + private static final String MODULE_INFO_FILE_NAME = "module-info" + CLASS_FILE_SUFFIX; + + static Predicate classFiles() { + return file -> isNotPackageInfo(file) && isNotModuleInfo(file) && isClassFile(file); + } + + static Predicate resourceFiles() { + return file -> !isClassFile(file); + } + + private static boolean isNotPackageInfo(Path path) { + return !path.endsWith(PACKAGE_INFO_FILE_NAME); + } + + private static boolean isNotModuleInfo(Path path) { + return !path.endsWith(MODULE_INFO_FILE_NAME); + } + + private static boolean isClassFile(Path file) { + return file.getFileName().toString().endsWith(CLASS_FILE_SUFFIX); + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathResource.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathResource.java new file mode 100644 index 000000000000..720c5166c297 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathResource.java @@ -0,0 +1,55 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util; + +import java.net.URI; +import java.util.Objects; + +import org.junit.platform.commons.support.Resource; + +/** + * @since 1.11 + */ +class ClasspathResource implements Resource { + + private final String name; + private final URI uri; + + ClasspathResource(String name, URI uri) { + this.name = Preconditions.notNull(name, "name must not be null"); + this.uri = Preconditions.notNull(uri, "uri must not be null"); + } + + @Override + public String getName() { + return name; + } + + @Override + public URI getUri() { + return uri; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ClasspathResource that = (ClasspathResource) o; + return name.equals(that.name) && uri.equals(that.uri); + } + + @Override + public int hashCode() { + return Objects.hash(name, uri); + } +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathScanner.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathScanner.java index c4589503d12a..19bec125b93c 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathScanner.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathScanner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -14,7 +14,7 @@ import static java.util.Collections.emptyList; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; -import static org.junit.platform.commons.util.ClassFileVisitor.CLASS_FILE_SUFFIX; +import static org.junit.platform.commons.util.ClasspathFilters.CLASS_FILE_SUFFIX; import static org.junit.platform.commons.util.StringUtils.isNotBlank; import java.io.IOException; @@ -28,8 +28,10 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; @@ -37,6 +39,7 @@ import org.junit.platform.commons.function.Try; import org.junit.platform.commons.logging.Logger; import org.junit.platform.commons.logging.LoggerFactory; +import org.junit.platform.commons.support.Resource; /** *

DISCLAIMER

@@ -80,8 +83,8 @@ List> scanForClassesInPackage(String basePackageName, ClassFilter class Preconditions.notNull(classFilter, "classFilter must not be null"); basePackageName = basePackageName.trim(); - return findClassesForUris(getRootUrisForPackageNameOnClassPathAndModulePath(basePackageName), basePackageName, - classFilter); + List roots = getRootUrisForPackageNameOnClassPathAndModulePath(basePackageName); + return findClassesForUris(roots, basePackageName, classFilter); } List> scanForClassesInClasspathRoot(URI root, ClassFilter classFilter) { @@ -91,8 +94,26 @@ List> scanForClassesInClasspathRoot(URI root, ClassFilter classFilter) return findClassesForUri(root, PackageUtils.DEFAULT_PACKAGE_NAME, classFilter); } + List scanForResourcesInPackage(String basePackageName, Predicate resourceFilter) { + Preconditions.condition( + PackageUtils.DEFAULT_PACKAGE_NAME.equals(basePackageName) || isNotBlank(basePackageName), + "basePackageName must not be null or blank"); + Preconditions.notNull(resourceFilter, "resourceFilter must not be null"); + basePackageName = basePackageName.trim(); + + List roots = getRootUrisForPackageNameOnClassPathAndModulePath(basePackageName); + return findResourcesForUris(roots, basePackageName, resourceFilter); + } + + List scanForResourcesInClasspathRoot(URI root, Predicate resourceFilter) { + Preconditions.notNull(root, "root must not be null"); + Preconditions.notNull(resourceFilter, "resourceFilter must not be null"); + + return findResourcesForUri(root, PackageUtils.DEFAULT_PACKAGE_NAME, resourceFilter); + } + /** - * Recursively scan for classes in all of the supplied source directories. + * Recursively scan for classes in all the supplied source directories. */ private List> findClassesForUris(List baseUris, String basePackageName, ClassFilter classFilter) { // @formatter:off @@ -105,32 +126,59 @@ private List> findClassesForUris(List baseUris, String basePackage } private List> findClassesForUri(URI baseUri, String basePackageName, ClassFilter classFilter) { + List> classes = new ArrayList<>(); + // @formatter:off + walkFilesForUri(baseUri, ClasspathFilters.classFiles(), + (baseDir, file) -> + processClassFileSafely(baseDir, basePackageName, classFilter, file, classes::add)); + // @formatter:on + return classes; + } + + /** + * Recursively scan for resources in all the supplied source directories. + */ + private List findResourcesForUris(List baseUris, String basePackageName, + Predicate resourceFilter) { + // @formatter:off + return baseUris.stream() + .map(baseUri -> findResourcesForUri(baseUri, basePackageName, resourceFilter)) + .flatMap(Collection::stream) + .distinct() + .collect(toList()); + // @formatter:on + } + + private List findResourcesForUri(URI baseUri, String basePackageName, + Predicate resourceFilter) { + List resources = new ArrayList<>(); + // @formatter:off + walkFilesForUri(baseUri, ClasspathFilters.resourceFiles(), + (baseDir, file) -> + processResourceFileSafely(baseDir, basePackageName, resourceFilter, file, resources::add)); + // @formatter:on + return resources; + } + + private static void walkFilesForUri(URI baseUri, Predicate filter, BiConsumer consumer) { try (CloseablePath closeablePath = CloseablePath.create(baseUri)) { Path baseDir = closeablePath.getPath(); - return findClassesForPath(baseDir, basePackageName, classFilter); + Preconditions.condition(Files.exists(baseDir), () -> "baseDir must exist: " + baseDir); + try { + Files.walkFileTree(baseDir, new ClasspathFileVisitor(baseDir, filter, consumer)); + } + catch (IOException ex) { + logger.warn(ex, () -> "I/O error scanning files in " + baseDir); + } } catch (PreconditionViolationException ex) { throw ex; } catch (Exception ex) { logger.warn(ex, () -> "Error scanning files for URI " + baseUri); - return emptyList(); } } - private List> findClassesForPath(Path baseDir, String basePackageName, ClassFilter classFilter) { - Preconditions.condition(Files.exists(baseDir), () -> "baseDir must exist: " + baseDir); - List> classes = new ArrayList<>(); - try { - Files.walkFileTree(baseDir, new ClassFileVisitor( - classFile -> processClassFileSafely(baseDir, basePackageName, classFilter, classFile, classes::add))); - } - catch (IOException ex) { - logger.warn(ex, () -> "I/O error scanning files in " + baseDir); - } - return classes; - } - private void processClassFileSafely(Path baseDir, String basePackageName, ClassFilter classFilter, Path classFile, Consumer> classConsumer) { try { @@ -140,7 +188,8 @@ private void processClassFileSafely(Path baseDir, String basePackageName, ClassF // @formatter:off loadClass.apply(fullyQualifiedClassName, getClassLoader()) .toOptional() - .filter(classFilter) // Always use ".filter(classFilter)" to include future predicates. + // Always use ".filter(classFilter)" to include future predicates. + .filter(classFilter) .ifPresent(classConsumer); // @formatter:on } @@ -154,6 +203,22 @@ private void processClassFileSafely(Path baseDir, String basePackageName, ClassF } } + private void processResourceFileSafely(Path baseDir, String basePackageName, Predicate resourceFilter, + Path resourceFile, Consumer resourceConsumer) { + try { + String fullyQualifiedResourceName = determineFullyQualifiedResourceName(baseDir, basePackageName, + resourceFile); + Resource resource = new ClasspathResource(fullyQualifiedResourceName, resourceFile.toUri()); + if (resourceFilter.test(resource)) { + resourceConsumer.accept(resource); + } + // @formatter:on + } + catch (Throwable throwable) { + handleThrowable(resourceFile, throwable); + } + } + private String determineFullyQualifiedClassName(Path baseDir, String basePackageName, Path classFile) { // @formatter:off return Stream.of( @@ -166,11 +231,34 @@ private String determineFullyQualifiedClassName(Path baseDir, String basePackage // @formatter:on } + /** + * The fully qualified resource name is a {@code /}-separated path. + *

+ * The path is relative to the classpath root in which the resource is located. + + * @return the resource name; never {@code null} + */ + private String determineFullyQualifiedResourceName(Path baseDir, String basePackageName, Path resourceFile) { + // @formatter:off + return Stream.of( + packagePath(basePackageName), + packagePath(determineSubpackageName(baseDir, resourceFile)), + determineSimpleResourceName(resourceFile) + ) + .filter(value -> !value.isEmpty()) // Handle default package appropriately. + .collect(joining(CLASSPATH_RESOURCE_PATH_SEPARATOR_STRING)); + // @formatter:on + } + private String determineSimpleClassName(Path classFile) { String fileName = classFile.getFileName().toString(); return fileName.substring(0, fileName.length() - CLASS_FILE_SUFFIX.length()); } + private String determineSimpleResourceName(Path resourceFile) { + return resourceFile.getFileName().toString(); + } + private String determineSubpackageName(Path baseDir, Path classFile) { Path relativePath = baseDir.relativize(classFile.getParent()); String pathSeparator = baseDir.getFileSystem().getSeparator(); @@ -208,9 +296,9 @@ private void logMalformedClassName(Path classFile, String fullyQualifiedClassNam } } - private void logGenericFileProcessingException(Path classFile, Throwable throwable) { - logger.debug(throwable, () -> format("Failed to load java.lang.Class for path [%s] during classpath scanning.", - classFile.toAbsolutePath())); + private void logGenericFileProcessingException(Path classpathFile, Throwable throwable) { + logger.debug(throwable, + () -> format("Failed to load [%s] during classpath scanning.", classpathFile.toAbsolutePath())); } private ClassLoader getClassLoader() { diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CloseablePath.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CloseablePath.java index e6bb3ddd27df..c1da5bacd819 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CloseablePath.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CloseablePath.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -23,6 +23,7 @@ import java.nio.file.Paths; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; @@ -32,35 +33,45 @@ final class CloseablePath implements Closeable { private static final String FILE_URI_SCHEME = "file"; - private static final String JAR_URI_SCHEME = "jar"; + static final String JAR_URI_SCHEME = "jar"; private static final String JAR_FILE_EXTENSION = ".jar"; - private static final String JAR_URI_SEPARATOR = "!"; + private static final String JAR_URI_SEPARATOR = "!/"; private static final Closeable NULL_CLOSEABLE = () -> { }; private static final ConcurrentMap MANAGED_FILE_SYSTEMS = new ConcurrentHashMap<>(); + private final AtomicBoolean closed = new AtomicBoolean(); + private final Path path; private final Closeable delegate; static CloseablePath create(URI uri) throws URISyntaxException { + return create(uri, it -> FileSystems.newFileSystem(it, emptyMap())); + } + + static CloseablePath create(URI uri, FileSystemProvider fileSystemProvider) throws URISyntaxException { if (JAR_URI_SCHEME.equals(uri.getScheme())) { - String[] parts = uri.toString().split(JAR_URI_SEPARATOR); - String jarUri = parts[0]; - String jarEntry = parts[1]; - return createForJarFileSystem(new URI(jarUri), fileSystem -> fileSystem.getPath(jarEntry)); + // Parsing: jar:!/[], see java.net.JarURLConnection + String uriString = uri.toString(); + int lastJarUriSeparator = uriString.lastIndexOf(JAR_URI_SEPARATOR); + String jarUri = uriString.substring(0, lastJarUriSeparator); + String jarEntry = uriString.substring(lastJarUriSeparator + 1); + return createForJarFileSystem(new URI(jarUri), fileSystem -> fileSystem.getPath(jarEntry), + fileSystemProvider); } - if (uri.getScheme().equals(FILE_URI_SCHEME) && uri.getPath().endsWith(JAR_FILE_EXTENSION)) { + if (FILE_URI_SCHEME.equals(uri.getScheme()) && uri.getPath().endsWith(JAR_FILE_EXTENSION)) { return createForJarFileSystem(new URI(JAR_URI_SCHEME + ':' + uri), - fileSystem -> fileSystem.getRootDirectories().iterator().next()); + fileSystem -> fileSystem.getRootDirectories().iterator().next(), fileSystemProvider); } return new CloseablePath(Paths.get(uri), NULL_CLOSEABLE); } - private static CloseablePath createForJarFileSystem(URI jarUri, Function pathProvider) { + private static CloseablePath createForJarFileSystem(URI jarUri, Function pathProvider, + FileSystemProvider fileSystemProvider) { ManagedFileSystem managedFileSystem = MANAGED_FILE_SYSTEMS.compute(jarUri, - (__, oldValue) -> oldValue == null ? new ManagedFileSystem(jarUri) : oldValue.retain()); + (__, oldValue) -> oldValue == null ? new ManagedFileSystem(jarUri, fileSystemProvider) : oldValue.retain()); Path path = pathProvider.apply(managedFileSystem.fileSystem); return new CloseablePath(path, () -> MANAGED_FILE_SYSTEMS.compute(jarUri, (__, ___) -> managedFileSystem.release())); @@ -77,7 +88,9 @@ public Path getPath() { @Override public void close() throws IOException { - delegate.close(); + if (closed.compareAndSet(false, true)) { + delegate.close(); + } } private static class ManagedFileSystem { @@ -86,10 +99,10 @@ private static class ManagedFileSystem { private final FileSystem fileSystem; private final URI jarUri; - ManagedFileSystem(URI jarUri) { + ManagedFileSystem(URI jarUri, FileSystemProvider fileSystemProvider) { this.jarUri = jarUri; try { - fileSystem = FileSystems.newFileSystem(jarUri, emptyMap()); + fileSystem = fileSystemProvider.newFileSystem(jarUri); } catch (IOException e) { throw new UncheckedIOException("Failed to create file system for " + jarUri, e); @@ -118,4 +131,8 @@ private void close() { } } } + + interface FileSystemProvider { + FileSystem newFileSystem(URI uri) throws IOException; + } } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java index 3d08f95a8d35..20061af9fd2e 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -25,6 +25,7 @@ import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collector; @@ -55,7 +56,7 @@ private CollectionUtils() { } /** - * Read the only element of a collection of size 1. + * Get the only element of a collection of size 1. * * @param collection the collection to get the element from * @return the only element of the collection @@ -66,7 +67,29 @@ public static T getOnlyElement(Collection collection) { Preconditions.notNull(collection, "collection must not be null"); Preconditions.condition(collection.size() == 1, () -> "collection must contain exactly one element: " + collection); - return collection.iterator().next(); + return firstElement(collection); + } + + /** + * Get the first element of the supplied collection unless it's empty. + * + * @param collection the collection to get the element from + * @return the first element of the collection; empty if the collection is empty + * @throws PreconditionViolationException if the collection is {@code null} + * @since 1.11 + */ + @API(status = INTERNAL, since = "1.11") + public static Optional getFirstElement(Collection collection) { + Preconditions.notNull(collection, "collection must not be null"); + return collection.isEmpty() // + ? Optional.empty() // + : Optional.ofNullable(firstElement(collection)); + } + + private static T firstElement(Collection collection) { + return collection instanceof List // + ? ((List) collection).get(0) // + : collection.iterator().next(); } /** diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java index ce698dbbdcd6..b4493a76f64f 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -71,14 +71,13 @@ private ExceptionUtils() { */ public static RuntimeException throwAsUncheckedException(Throwable t) { Preconditions.notNull(t, "Throwable must not be null"); - ExceptionUtils.throwAs(t); - - // Appeasing the compiler: the following line will never be executed. - return null; + // The following line will never actually return an exception but rather + // throw t masked as a RuntimeException. + return ExceptionUtils.throwAs(t); } @SuppressWarnings("unchecked") - private static void throwAs(Throwable t) throws T { + private static T throwAs(Throwable t) throws T { throw (T) t; } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/FunctionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/FunctionUtils.java index 492df5c6b3ff..4231c77588e0 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/FunctionUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/FunctionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/LruCache.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/LruCache.java index dc95539624fa..1598826739e9 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/LruCache.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/LruCache.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java index b230c9213471..d24b977d71eb 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -17,10 +17,12 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import org.apiguardian.api.API; import org.junit.platform.commons.logging.Logger; import org.junit.platform.commons.logging.LoggerFactory; +import org.junit.platform.commons.support.Resource; /** * Collection of utilities for working with {@code java.lang.Module} @@ -97,4 +99,23 @@ public static List> findAllClassesInModule(String moduleName, ClassFilt return emptyList(); } + /** + * Find all resources for the given module name. + * + * @param moduleName the name of the module to scan; never {@code null} or + * empty + * @param filter the class filter to apply; never {@code null} + * @return an immutable list of all such resources found; never {@code null} + * but potentially empty + * @since 1.11 + */ + @API(status = INTERNAL, since = "1.11") + public static List findAllResourcesInModule(String moduleName, Predicate filter) { + Preconditions.notBlank(moduleName, "Module name must not be null or empty"); + Preconditions.notNull(filter, "Resource filter must not be null"); + + logger.config(() -> "Basic version of findAllResourcesInModule() always returns an empty list!"); + return emptyList(); + } + } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/PackageNameUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/PackageNameUtils.java new file mode 100644 index 000000000000..9a018363a4a1 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/PackageNameUtils.java @@ -0,0 +1,38 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util; + +import static org.junit.platform.commons.util.PackageUtils.DEFAULT_PACKAGE_NAME; + +/** + * Collection of utilities for working with package names. + * + *

DISCLAIMER

+ * + *

These utilities are intended solely for usage within the JUnit framework + * itself. Any usage by external parties is not supported. + * Use at your own risk! + * + * @since 1.11.3 + */ +class PackageNameUtils { + + static String getPackageName(Class clazz) { + Package p = clazz.getPackage(); + if (p != null) { + return p.getName(); + } + String className = clazz.getName(); + int index = className.lastIndexOf('.'); + return index == -1 ? DEFAULT_PACKAGE_NAME : className.substring(0, index); + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/PackageUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/PackageUtils.java index 2156b2df739c..6b23a5324bac 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/PackageUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/PackageUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -99,4 +99,22 @@ public static Optional getAttribute(Class type, String name) { return Optional.empty(); } } + + /** + * Get the module or implementation version for the supplied {@code type}. + *

+ * The former is only available if the type is part of a versioned module on + * the module path; the latter only if the type is part of a JAR file with a + * manifest that contains an {@code Implementation-Version} attribute. + * + * @since 1.11 + */ + @API(status = INTERNAL, since = "1.11") + public static Optional getModuleOrImplementationVersion(Class type) { + Optional moduleVersion = ModuleUtils.getModuleVersion(type); + if (moduleVersion.isPresent()) { + return moduleVersion; + } + return getAttribute(type, Package::getImplementationVersion); + } } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/PreconditionViolationException.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/PreconditionViolationException.java index 67dcdde4cbe7..208db0f299c9 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/PreconditionViolationException.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/PreconditionViolationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/Preconditions.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/Preconditions.java index 6e64150c2db0..33ecd1e91c08 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/Preconditions.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/Preconditions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,6 @@ package org.junit.platform.commons.util; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.INTERNAL; import java.util.Arrays; @@ -81,7 +80,7 @@ public static T notNull(T object, Supplier messageSupplier) throws P * @since 1.9 * @see #condition(boolean, String) */ - @API(status = EXPERIMENTAL, since = "1.9") + @API(status = INTERNAL, since = "1.11") public static int[] notEmpty(int[] array, String message) throws PreconditionViolationException { condition(array != null && array.length > 0, message); return array; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java index 816b7fadd104..fcc44e95843d 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,6 +11,7 @@ package org.junit.platform.commons.util; import static java.lang.String.format; +import static java.util.Collections.synchronizedMap; import static java.util.stream.Collectors.toCollection; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; @@ -18,13 +19,14 @@ import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import static org.junit.platform.commons.util.CollectionUtils.toUnmodifiableList; +import static org.junit.platform.commons.util.PackageNameUtils.getPackageName; import static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP; import static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.TOP_DOWN; import java.io.File; -import java.lang.reflect.AccessibleObject; import java.lang.reflect.Array; import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.InvocationTargetException; @@ -58,6 +60,7 @@ import org.junit.platform.commons.function.Try; import org.junit.platform.commons.logging.Logger; import org.junit.platform.commons.logging.LoggerFactory; +import org.junit.platform.commons.support.Resource; /** * Collection of utilities for working with the Java reflection APIs. @@ -77,6 +80,27 @@ @API(status = INTERNAL, since = "1.0") public final class ReflectionUtils { + /** + * Property name used to signal that legacy semantics should be used when + * searching for fields and methods within a type hierarchy: {@value}. + * + *

Value must be either {@code true} or {@code false} (ignoring case); + * defaults to {@code false}. + * + *

When set to {@code false} (either explicitly or implicitly), field and + * method searches will adhere to Java semantics regarding whether a given + * field or method is visible or overridden, where the latter only applies + * to methods. When set to {@code true}, the semantics used in JUnit 5 prior + * to JUnit 5.11 (JUnit Platform 1.11) will be used, which means that fields + * and methods can hide, shadow, or supersede fields and methods in supertypes + * based solely on the field's name or the method's signature, disregarding + * the actual Java language semantics for visibility and whether a method + * overrides another method. + * + * @since 1.11 + */ + private static final String USE_LEGACY_SEARCH_SEMANTICS_PROPERTY_NAME = "junit.platform.reflection.search.useLegacySemantics"; + private static final Logger logger = LoggerFactory.getLogger(ReflectionUtils.class); private ReflectionUtils() { @@ -97,7 +121,7 @@ public enum HierarchyTraversalMode { /** * Traverse the hierarchy using bottom-up semantics. */ - BOTTOM_UP; + BOTTOM_UP } // Pattern: "[Ljava.lang.String;", "[[[[Ljava.lang.String;", etc. @@ -125,6 +149,13 @@ public enum HierarchyTraversalMode { private static final ClasspathScanner classpathScanner = new ClasspathScanner( ClassLoaderUtils::getDefaultClassLoader, ReflectionUtils::tryToLoadClass); + /** + * Cache for equivalent methods on an interface implemented by the declaring class. + * @since 1.11 + * @see #getInterfaceMethodIfPossible(Method, Class) + */ + private static final Map interfaceMethodCache = synchronizedMap(new LruCache<>(255)); + /** * Set of fully qualified class names for which no cycles have been detected * in inner class hierarchies. @@ -230,6 +261,8 @@ public enum HierarchyTraversalMode { primitiveToWrapperMap = Collections.unmodifiableMap(primitivesToWrappers); } + static volatile boolean useLegacySearchSemantics = getLegacySearchSemanticsFlag(); + public static boolean isPublic(Class clazz) { Preconditions.notNull(clazz, "Class must not be null"); return Modifier.isPublic(clazz.getModifiers()); @@ -341,8 +374,14 @@ public static boolean isInnerClass(Class clazz) { return !isStatic(clazz) && clazz.isMemberClass(); } - public static boolean returnsVoid(Method method) { - return method.getReturnType().equals(Void.TYPE); + /** + * Determine if the return type of the supplied method is primitive {@code void}. + * + * @param method the method to test; never {@code null} + * @return {@code true} if the method's return type is {@code void} + */ + public static boolean returnsPrimitiveVoid(Method method) { + return method.getReturnType() == void.class; } /** @@ -770,6 +809,25 @@ public static Optional> loadClass(String name, ClassLoader classLoader) return tryToLoadClass(name, classLoader).toOptional(); } + /** + * Load a class by its primitive name or fully qualified name, + * using the supplied {@link ClassLoader}. + * + *

See {@link org.junit.platform.commons.support.ReflectionSupport#tryToLoadClass(String)} + * for details on support for class names for arrays. + * + * @param name the name of the class to load; never {@code null} or blank + * @param classLoader the {@code ClassLoader} to use; never {@code null} + * @throws JUnitException if the class could not be loaded + * @since 1.11 + * @see #tryToLoadClass(String, ClassLoader) + */ + @API(status = INTERNAL, since = "1.11") + public static Class loadRequiredClass(String name, ClassLoader classLoader) throws JUnitException { + return tryToLoadClass(name, classLoader).getOrThrow( + cause -> new JUnitException(format("Could not load class [%s]", name), cause)); + } + /** * Try to load a class by its primitive name or fully qualified * name, using the supplied {@link ClassLoader}. @@ -873,13 +931,34 @@ public static String getFullyQualifiedMethodName(Class clazz, Method method) * @param methodName the name of the method; never {@code null} or blank * @param parameterTypes the parameter types of the method; may be {@code null} or empty * @return fully qualified method name; never {@code null} - * @see #getFullyQualifiedMethodName(Class, Method) */ public static String getFullyQualifiedMethodName(Class clazz, String methodName, Class... parameterTypes) { Preconditions.notNull(clazz, "Class must not be null"); Preconditions.notBlank(methodName, "Method name must not be null or blank"); - return String.format("%s#%s(%s)", clazz.getName(), methodName, ClassUtils.nullSafeToString(parameterTypes)); + return getFullyQualifiedMethodName(clazz.getName(), methodName, ClassUtils.nullSafeToString(parameterTypes)); + } + + /** + * Build the fully qualified method name for the method described by the + * supplied class, method name, and parameter types. + * + *

Note that the class is not necessarily the class in which the method is + * declared. + * + * @param className the name of the class from which the method should be referenced; never {@code null} + * @param methodName the name of the method; never {@code null} or blank + * @param parameterTypeNames the parameter type names of the method; may be empty but not {@code null} + * @return fully qualified method name; never {@code null} + * @since 1.11 + */ + @API(status = INTERNAL, since = "1.11") + public static String getFullyQualifiedMethodName(String className, String methodName, String parameterTypeNames) { + Preconditions.notBlank(className, "Class name must not be null or blank"); + Preconditions.notBlank(methodName, "Method name must not be null or blank"); + Preconditions.notNull(parameterTypeNames, "Parameter type names must not be null"); + + return String.format("%s#%s(%s)", className, methodName, parameterTypeNames); } /** @@ -928,54 +1007,30 @@ else if (methodPart.endsWith(")")) { } /** - * Get the outermost instance of the required type, searching recursively - * through enclosing instances. + * Parse the supplied fully qualified field name into a 2-element + * {@code String[]} with the following content. * - *

If the supplied inner object is of the required type, it will be - * returned. + *

    + *
  • index {@code 0}: the fully qualified class name
  • + *
  • index {@code 1}: the name of the field
  • + *
* - * @param inner the inner object from which to begin the search; never {@code null} - * @param requiredType the required type of the outermost instance; never {@code null} - * @return an {@code Optional} containing the outermost instance; never {@code null} - * but potentially empty - * @deprecated Please discontinue use of this method since it relies on internal - * implementation details of the JDK that may not work in the future. + * @param fullyQualifiedFieldName a fully qualified field name, + * never {@code null} or blank + * @return a 2-element array of strings containing the parsed values + * @since 1.11 */ - @API(status = DEPRECATED, since = "1.4") - @Deprecated - public static Optional getOutermostInstance(Object inner, Class requiredType) { - Preconditions.notNull(inner, "inner object must not be null"); - Preconditions.notNull(requiredType, "requiredType must not be null"); - - if (requiredType.isInstance(inner)) { - return Optional.of(inner); - } - - Optional candidate = getOuterInstance(inner); - if (candidate.isPresent()) { - return getOutermostInstance(candidate.get(), requiredType); - } - - return Optional.empty(); - } + @API(status = INTERNAL, since = "1.11") + public static String[] parseFullyQualifiedFieldName(String fullyQualifiedFieldName) { + Preconditions.notBlank(fullyQualifiedFieldName, "fullyQualifiedFieldName must not be null or blank"); - private static Optional getOuterInstance(Object inner) { - // This is risky since it depends on the name of the field which is nowhere guaranteed - // but has been stable so far in all JDKs - - // @formatter:off - return Arrays.stream(inner.getClass().getDeclaredFields()) - .filter(field -> field.getName().startsWith("this$")) - .findFirst() - .map(field -> { - try { - return makeAccessible(field).get(inner); - } - catch (Throwable t) { - throw ExceptionUtils.throwAsUncheckedException(t); - } - }); - // @formatter:on + int indexOfHashtag = fullyQualifiedFieldName.indexOf('#'); + boolean validSyntax = (indexOfHashtag > 0) && (indexOfHashtag < fullyQualifiedFieldName.length() - 1); + Preconditions.condition(validSyntax, + () -> "[" + fullyQualifiedFieldName + "] is not a valid fully qualified field name: " + + "it must start with a fully qualified class name followed by a '#' " + + "and then the field name."); + return fullyQualifiedFieldName.split("#"); } public static Set getAllClasspathRootDirectories() { @@ -1014,6 +1069,13 @@ public static List> findAllClassesInClasspathRoot(URI root, ClassFilter return Collections.unmodifiableList(classpathScanner.scanForClassesInClasspathRoot(root, classFilter)); } + /** + * @since 1.11 + */ + public static List findAllResourcesInClasspathRoot(URI root, Predicate resourceFilter) { + return Collections.unmodifiableList(classpathScanner.scanForResourcesInClasspathRoot(root, resourceFilter)); + } + /** * @since 1.10 */ @@ -1021,6 +1083,13 @@ public static Stream> streamAllClassesInClasspathRoot(URI root, ClassFi return findAllClassesInClasspathRoot(root, classFilter).stream(); } + /** + * @since 1.11 + */ + public static Stream streamAllResourcesInClasspathRoot(URI root, Predicate resourceFilter) { + return findAllResourcesInClasspathRoot(root, resourceFilter).stream(); + } + /** * @see org.junit.platform.commons.support.ReflectionSupport#findAllClassesInPackage(String, Predicate, Predicate) */ @@ -1046,6 +1115,14 @@ public static List> findAllClassesInPackage(String basePackageName, Cla return Collections.unmodifiableList(classpathScanner.scanForClassesInPackage(basePackageName, classFilter)); } + /** + * @since 1.11 + */ + public static List findAllResourcesInPackage(String basePackageName, Predicate resourceFilter) { + return Collections.unmodifiableList( + classpathScanner.scanForResourcesInPackage(basePackageName, resourceFilter)); + } + /** * @since 1.10 */ @@ -1053,6 +1130,14 @@ public static Stream> streamAllClassesInPackage(String basePackageName, return findAllClassesInPackage(basePackageName, classFilter).stream(); } + /** + * @since 1.11 + */ + public static Stream streamAllResourcesInPackage(String basePackageName, + Predicate resourceFilter) { + return findAllResourcesInPackage(basePackageName, resourceFilter).stream(); + } + /** * @since 1.1.1 * @see org.junit.platform.commons.support.ReflectionSupport#findAllClassesInModule(String, Predicate, Predicate) @@ -1079,6 +1164,13 @@ public static List> findAllClassesInModule(String moduleName, ClassFilt return Collections.unmodifiableList(ModuleUtils.findAllClassesInModule(moduleName, classFilter)); } + /** + * @since 1.11 + */ + public static List findAllResourcesInModule(String moduleName, Predicate resourceFilter) { + return Collections.unmodifiableList(ModuleUtils.findAllResourcesInModule(moduleName, resourceFilter)); + } + /** * @since 1.10 */ @@ -1086,6 +1178,13 @@ public static Stream> streamAllClassesInModule(String moduleName, Class return findAllClassesInModule(moduleName, classFilter).stream(); } + /** + * @since 1.11 + */ + public static Stream streamAllResourcesInModule(String moduleName, Predicate resourceFilter) { + return findAllResourcesInModule(moduleName, resourceFilter).stream(); + } + /** * @see org.junit.platform.commons.support.ReflectionSupport#findNestedClasses(Class, Predicate) */ @@ -1238,6 +1337,7 @@ public static List> findConstructors(Class clazz, Predicate findFields(Class clazz, Predicate predicate, HierarchyTraversalMode traversalMode) { + return streamFields(clazz, predicate, traversalMode).collect(toUnmodifiableList()); } @@ -1252,7 +1352,11 @@ public static Stream streamFields(Class clazz, Predicate predic Preconditions.notNull(predicate, "Predicate must not be null"); Preconditions.notNull(traversalMode, "HierarchyTraversalMode must not be null"); - return findAllFieldsInHierarchy(clazz, traversalMode).stream().filter(predicate); + // @formatter:off + return findAllFieldsInHierarchy(clazz, traversalMode).stream() + .filter(predicate) + .distinct(); + // @formatter:on } private static List findAllFieldsInHierarchy(Class clazz, HierarchyTraversalMode traversalMode) { @@ -1352,6 +1456,53 @@ public static Try tryToGetMethod(Class clazz, String methodName, Clas return Try.call(() -> clazz.getMethod(methodName, parameterTypes)); } + /** + * Determine a corresponding interface method for the given method handle, if possible. + *

This is particularly useful for arriving at a public exported type on the Java + * Module System which can be reflectively invoked without an illegal access warning. + * @param method the method to be invoked, potentially from an implementation class; + * never {@code null} + * @param targetClass the target class to check for declared interfaces; + * potentially {@code null} + * @return the corresponding interface method, or the original method if none found + * @since 1.11 + */ + @API(status = INTERNAL, since = "1.11") + public static Method getInterfaceMethodIfPossible(Method method, Class targetClass) { + if (!isPublic(method) || method.getDeclaringClass().isInterface()) { + return method; + } + // Try cached version of method in its declaring class + Method result = interfaceMethodCache.computeIfAbsent(method, + m -> findInterfaceMethodIfPossible(m, m.getParameterTypes(), m.getDeclaringClass(), Object.class)); + if (result == method && targetClass != null) { + // No interface method found yet -> try given target class (possibly a subclass of the + // declaring class, late-binding a base class method to a subclass-declared interface: + // see e.g. HashMap.HashIterator.hasNext) + result = findInterfaceMethodIfPossible(method, method.getParameterTypes(), targetClass, + method.getDeclaringClass()); + } + return result; + } + + private static Method findInterfaceMethodIfPossible(Method method, Class[] parameterTypes, Class startClass, + Class endClass) { + + Class current = startClass; + while (current != null && current != endClass) { + for (Class ifc : current.getInterfaces()) { + try { + return ifc.getMethod(method.getName(), parameterTypes); + } + catch (NoSuchMethodException ex) { + // ignore + } + } + current = current.getSuperclass(); + } + return method; + } + /** * @see org.junit.platform.commons.support.ReflectionSupport#findMethod(Class, String, String) */ @@ -1456,8 +1607,7 @@ public static Method getRequiredMethod(Class clazz, String methodName, Class< * that match the specified {@code predicate}, using top-down search semantics * within the type hierarchy. * - *

The results will not contain instance methods that are overridden - * or {@code static} methods that are hidden. + *

The results will not contain instance methods that are overridden. * * @param clazz the class or interface in which to find the methods; never {@code null} * @param predicate the method filter; never {@code null} @@ -1509,10 +1659,10 @@ private static List findAllMethodsInHierarchy(Class clazz, HierarchyT .filter(method -> !method.isSynthetic()) .collect(toList()); List superclassMethods = getSuperclassMethods(clazz, traversalMode).stream() - .filter(method -> !isMethodShadowedByLocalMethods(method, localMethods)) + .filter(method -> !isMethodOverriddenByLocalMethods(method, localMethods)) .collect(toList()); List interfaceMethods = getInterfaceMethods(clazz, traversalMode).stream() - .filter(method -> !isMethodShadowedByLocalMethods(method, localMethods)) + .filter(method -> !isMethodOverriddenByLocalMethods(method, localMethods)) .collect(toList()); // @formatter:on @@ -1657,7 +1807,7 @@ private static List getInterfaceMethods(Class clazz, HierarchyTravers .collect(toList()); List superinterfaceMethods = getInterfaceMethods(ifc, traversalMode).stream() - .filter(method -> !isMethodShadowedByLocalMethods(method, localInterfaceMethods)) + .filter(method -> !isMethodOverriddenByLocalMethods(method, localInterfaceMethods)) .collect(toList()); // @formatter:on @@ -1703,7 +1853,10 @@ private static List getSuperclassFields(Class clazz, HierarchyTraversa } private static boolean isFieldShadowedByLocalFields(Field field, List localFields) { - return localFields.stream().anyMatch(local -> local.getName().equals(field.getName())); + if (useLegacySearchSemantics) { + return localFields.stream().anyMatch(local -> local.getName().equals(field.getName())); + } + return false; } private static List getSuperclassMethods(Class clazz, HierarchyTraversalMode traversalMode) { @@ -1714,14 +1867,42 @@ private static List getSuperclassMethods(Class clazz, HierarchyTraver return findAllMethodsInHierarchy(superclass, traversalMode); } - private static boolean isMethodShadowedByLocalMethods(Method method, List localMethods) { - return localMethods.stream().anyMatch(local -> isMethodShadowedBy(method, local)); + private static boolean isMethodOverriddenByLocalMethods(Method method, List localMethods) { + return localMethods.stream().anyMatch(local -> isMethodOverriddenBy(method, local)); } - private static boolean isMethodShadowedBy(Method upper, Method lower) { + private static boolean isMethodOverriddenBy(Method upper, Method lower) { + // If legacy search semantics are enabled, skip to hasCompatibleSignature() check. + if (!useLegacySearchSemantics) { + // A static method cannot override anything. + if (Modifier.isStatic(lower.getModifiers())) { + return false; + } + + // Cannot override a private, static, or final method. + int modifiers = upper.getModifiers(); + if (Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) { + return false; + } + + // Cannot override a package-private method in another package. + if (isPackagePrivate(upper) && !declaredInSamePackage(upper, lower)) { + return false; + } + } + return hasCompatibleSignature(upper, lower.getName(), lower.getParameterTypes()); } + private static boolean isPackagePrivate(Member member) { + int modifiers = member.getModifiers(); + return !(Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers) || Modifier.isPrivate(modifiers)); + } + + private static boolean declaredInSamePackage(Method m1, Method m2) { + return getPackageName(m1.getDeclaringClass()).equals(getPackageName(m2.getDeclaringClass())); + } + /** * Determine if the supplied candidate method (typically a method higher in * the type hierarchy) has a signature that is compatible with a method that @@ -1735,15 +1916,16 @@ private static boolean hasCompatibleSignature(Method candidate, String methodNam if (parameterTypes.length != candidate.getParameterCount()) { return false; } + Class[] candidateParameterTypes = candidate.getParameterTypes(); // trivial case: parameter types exactly match - if (Arrays.equals(parameterTypes, candidate.getParameterTypes())) { + if (Arrays.equals(parameterTypes, candidateParameterTypes)) { return true; } // param count is equal, but types do not match exactly: check for method sub-signatures // https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.2 for (int i = 0; i < parameterTypes.length; i++) { Class lowerType = parameterTypes[i]; - Class upperType = candidate.getParameterTypes()[i]; + Class upperType = candidateParameterTypes[i]; if (!upperType.isAssignableFrom(lowerType)) { return false; } @@ -1764,12 +1946,22 @@ private static boolean isGeneric(Type type) { return type instanceof TypeVariable || type instanceof GenericArrayType; } + @API(status = INTERNAL, since = "1.11") @SuppressWarnings("deprecation") // "AccessibleObject.isAccessible()" is deprecated in Java 9 - public static T makeAccessible(T object) { - if (!object.isAccessible()) { - object.setAccessible(true); + public static T makeAccessible(T executable) { + if ((!isPublic(executable) || !isPublic(executable.getDeclaringClass())) && !executable.isAccessible()) { + executable.setAccessible(true); } - return object; + return executable; + } + + @API(status = INTERNAL, since = "1.11") + @SuppressWarnings("deprecation") // "AccessibleObject.isAccessible()" is deprecated in Java 9 + public static Field makeAccessible(Field field) { + if ((!isPublic(field) || !isPublic(field.getDeclaringClass()) || isFinal(field)) && !field.isAccessible()) { + field.setAccessible(true); + } + return field; } /** @@ -1826,4 +2018,16 @@ private static Throwable getUnderlyingCause(Throwable t) { return t; } + private static boolean getLegacySearchSemanticsFlag() { + String rawValue = System.getProperty(USE_LEGACY_SEARCH_SEMANTICS_PROPERTY_NAME); + if (StringUtils.isBlank(rawValue)) { + return false; + } + String value = rawValue.trim().toLowerCase(); + boolean isTrue = "true".equals(value); + Preconditions.condition(isTrue || "false".equals(value), () -> USE_LEGACY_SEARCH_SEMANTICS_PROPERTY_NAME + + " property must be 'true' or 'false' (ignoring case): " + rawValue); + return isTrue; + } + } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/RuntimeUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/RuntimeUtils.java index cdbdc1fb2cd2..786c5997b2d2 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/RuntimeUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/RuntimeUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/StringUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/StringUtils.java index 11155c0c822f..42dcdeef863b 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/StringUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/StringUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -14,6 +14,9 @@ import static org.apiguardian.api.API.Status.INTERNAL; import java.util.Arrays; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; import java.util.regex.Pattern; import org.apiguardian.api.API; @@ -256,4 +259,106 @@ public static String replaceWhitespaceCharacters(String str, String replacement) return str == null ? null : WHITESPACE_PATTERN.matcher(str).replaceAll(replacement); } + /** + * Split the supplied {@link String} into up to two parts using the supplied + * separator character. + * + * @param separator the separator character + * @param value the value to split; never {@code null} + * @since 1.11 + */ + @API(status = INTERNAL, since = "1.11") + public static TwoPartSplitResult splitIntoTwo(char separator, String value) { + Preconditions.notNull(value, "value must not be null"); + return splitIntoTwo(value, value.indexOf(separator), 1); + } + + /** + * Split the supplied {@link String} into up to two parts using the supplied + * separator string. + * + * @param separator the separator string; never {@code null} + * @param value the value to split; never {@code null} + * @since 1.11 + */ + @API(status = INTERNAL, since = "1.11") + public static TwoPartSplitResult splitIntoTwo(String separator, String value) { + Preconditions.notNull(separator, "separator must not be null"); + Preconditions.notNull(value, "value must not be null"); + return splitIntoTwo(value, value.indexOf(separator), separator.length()); + } + + private static TwoPartSplitResult splitIntoTwo(String value, int index, int length) { + if (index == -1) { + return new OnePart(value); + } + return new TwoParts(value.substring(0, index), value.substring(index + length)); + } + + /** + * The result of splitting a string into up to two parts. + * + * @since 1.11 + * @see StringUtils#splitIntoTwo(char, String) + * @see StringUtils#splitIntoTwo(String, String) + */ + @API(status = INTERNAL, since = "1.11") + public interface TwoPartSplitResult { + + /** + * Maps the result of splitting a string into two parts or throw an exception. + * + * @param onePartExceptionCreator the exception creator to use if the string was split into a single part + * @param twoPartsMapper the mapper to use if the string was split into two parts + */ + default T mapTwo(Supplier onePartExceptionCreator, + BiFunction twoPartsMapper) { + Function onePartMapper = __ -> { + throw onePartExceptionCreator.get(); + }; + return map(onePartMapper, twoPartsMapper); + } + + /** + * Maps the result of splitting a string into up to two parts. + * + * @param onePartMapper the mapper to use if the string was split into a single part + * @param twoPartsMapper the mapper to use if the string was split into two parts + */ + T map(Function onePartMapper, BiFunction twoPartsMapper); + + } + + private static final class OnePart implements TwoPartSplitResult { + + private final String value; + + OnePart(String value) { + this.value = value; + } + + @Override + public T map(Function onePartMapper, + BiFunction twoPartsMapper) { + return onePartMapper.apply(value); + } + } + + private static final class TwoParts implements TwoPartSplitResult { + + private final String first; + private final String second; + + TwoParts(String first, String second) { + this.first = first; + this.second = second; + } + + @Override + public T map(Function onePartMapper, + BiFunction twoPartsMapper) { + return twoPartsMapper.apply(first, second); + } + } + } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ToStringBuilder.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ToStringBuilder.java index 72695f508a73..f16e23bd4cdf 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ToStringBuilder.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ToStringBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/UnrecoverableExceptions.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/UnrecoverableExceptions.java index dd730e30919f..1235a329450d 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/UnrecoverableExceptions.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/UnrecoverableExceptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -49,7 +49,7 @@ private UnrecoverableExceptions() { */ public static void rethrowIfUnrecoverable(Throwable exception) { if (exception instanceof OutOfMemoryError) { - ExceptionUtils.throwAsUncheckedException(exception); + throw ExceptionUtils.throwAsUncheckedException(exception); } } diff --git a/junit-platform-commons/src/main/java9/org/junit/platform/commons/util/ModuleUtils.java b/junit-platform-commons/src/main/java9/org/junit/platform/commons/util/ModuleUtils.java index ff2b056653b2..f3a6bd1a5f33 100644 --- a/junit-platform-commons/src/main/java9/org/junit/platform/commons/util/ModuleUtils.java +++ b/junit-platform-commons/src/main/java9/org/junit/platform/commons/util/ModuleUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -21,6 +21,8 @@ import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; import java.lang.module.ResolvedModule; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; @@ -35,6 +37,7 @@ import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.logging.Logger; import org.junit.platform.commons.logging.LoggerFactory; +import org.junit.platform.commons.support.Resource; /** * Collection of utilities for working with {@code java.lang.Module} @@ -93,7 +96,7 @@ public static Optional getModuleVersion(Class type) { } /** - * Find all classes for the given module name. + * Find all {@linkplain Class classes} for the given module name. * * @param moduleName the name of the module to scan; never {@code null} or * empty @@ -114,6 +117,30 @@ public static List> findAllClassesInModule(String moduleName, ClassFilt return scan(moduleReferences, filter, ModuleUtils.class.getClassLoader()); } + /** + * Find all {@linkplain Resource resources} for the given module name. + * + * @param moduleName the name of the module to scan; never {@code null} or + * empty + * @param filter the class filter to apply; never {@code null} + * @return an immutable list of all such resources found; never {@code null} + * but potentially empty + * @since 1.11 + */ + @API(status = INTERNAL, since = "1.11") + public static List findAllResourcesInModule(String moduleName, Predicate filter) { + Preconditions.notBlank(moduleName, "Module name must not be null or empty"); + Preconditions.notNull(filter, "Resource filter must not be null"); + + logger.debug(() -> "Looking for classes in module: " + moduleName); + // @formatter:off + Set moduleReferences = streamResolvedModules(isEqual(moduleName)) + .map(ResolvedModule::reference) + .collect(toSet()); + // @formatter:on + return scan(moduleReferences, filter, ModuleUtils.class.getClassLoader()); + } + /** * Stream resolved modules from current (or boot) module layer. */ @@ -146,7 +173,7 @@ private static Stream streamResolvedModules(Predicate mo */ private static List> scan(Set references, ClassFilter filter, ClassLoader loader) { logger.debug(() -> "Scanning " + references.size() + " module references: " + references); - ModuleReferenceScanner scanner = new ModuleReferenceScanner(filter, loader); + ModuleReferenceClassScanner scanner = new ModuleReferenceClassScanner(filter, loader); List> classes = new ArrayList<>(); for (ModuleReference reference : references) { classes.addAll(scanner.scan(reference)); @@ -156,14 +183,32 @@ private static List> scan(Set references, ClassFilter } /** - * {@link ModuleReference} scanner. + * Scan for classes using the supplied set of module references, class + * filter, and loader. + */ + private static List scan(Set references, Predicate filter, + ClassLoader loader) { + logger.debug(() -> "Scanning " + references.size() + " module references: " + references); + ModuleReferenceResourceScanner scanner = new ModuleReferenceResourceScanner(filter, loader); + List classes = new ArrayList<>(); + for (ModuleReference reference : references) { + classes.addAll(scanner.scan(reference)); + } + logger.debug(() -> "Found " + classes.size() + " classes: " + classes); + return Collections.unmodifiableList(classes); + } + + /** + * {@link ModuleReference} class scanner. + * + * @since 1.1 */ - static class ModuleReferenceScanner { + static class ModuleReferenceClassScanner { private final ClassFilter classFilter; private final ClassLoader classLoader; - ModuleReferenceScanner(ClassFilter classFilter, ClassLoader classLoader) { + ModuleReferenceClassScanner(ClassFilter classFilter, ClassLoader classLoader) { this.classFilter = classFilter; this.classLoader = classLoader; } @@ -180,7 +225,8 @@ List> scan(ModuleReference reference) { .filter(name -> !name.equals("module-info")) .filter(classFilter::match) .map(this::loadClassUnchecked) - .filter(classFilter::match) + // Always use ".filter(classFilter)" to include future predicates. + .filter(classFilter) .collect(Collectors.toList()); // @formatter:on } @@ -215,4 +261,50 @@ private Class loadClassUnchecked(String binaryName) { } + /** + * {@link ModuleReference} resource class scanner. + * + * @since 1.11 + */ + static class ModuleReferenceResourceScanner { + + private final Predicate resourceFilter; + private final ClassLoader classLoader; + + ModuleReferenceResourceScanner(Predicate resourceFilter, ClassLoader classLoader) { + this.resourceFilter = resourceFilter; + this.classLoader = classLoader; + } + + /** + * Scan module reference for resources that potentially contain testable resources. + */ + List scan(ModuleReference reference) { + try (ModuleReader reader = reference.open()) { + try (Stream names = reader.list()) { + // @formatter:off + return names.filter(name -> !name.endsWith(".class")) + .map(this::loadResourceUnchecked) + .filter(resourceFilter) + .collect(Collectors.toList()); + // @formatter:on + } + } + catch (IOException e) { + throw new JUnitException("Failed to read contents of " + reference + ".", e); + } + } + + private Resource loadResourceUnchecked(String binaryName) { + try { + URI uri = classLoader.getResource(binaryName).toURI(); + return new ClasspathResource(binaryName, uri); + } + catch (URISyntaxException e) { + throw new JUnitException("Failed to load resource with name '" + binaryName + "'.", e); + } + } + + } + } diff --git a/junit-platform-commons/src/main/java9/org/junit/platform/commons/util/PackageNameUtils.java b/junit-platform-commons/src/main/java9/org/junit/platform/commons/util/PackageNameUtils.java new file mode 100644 index 000000000000..84afaf736290 --- /dev/null +++ b/junit-platform-commons/src/main/java9/org/junit/platform/commons/util/PackageNameUtils.java @@ -0,0 +1,30 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util; + +/** + * Collection of utilities for working with package names. + * + *

DISCLAIMER

+ * + *

These utilities are intended solely for usage within the JUnit framework + * itself. Any usage by external parties is not supported. + * Use at your own risk! + * + * @since 1.11.3 + */ +class PackageNameUtils { + + static String getPackageName(Class clazz) { + return clazz.getPackageName(); + } + +} diff --git a/junit-platform-commons/src/module/org.junit.platform.commons/module-info.java b/junit-platform-commons/src/module/org.junit.platform.commons/module-info.java index f33ffd314feb..774684198f9f 100644 --- a/junit-platform-commons/src/module/org.junit.platform.commons/module-info.java +++ b/junit-platform-commons/src/module/org.junit.platform.commons/module-info.java @@ -36,6 +36,7 @@ org.junit.platform.testkit, org.junit.vintage.engine; exports org.junit.platform.commons.support; + exports org.junit.platform.commons.support.conversion; exports org.junit.platform.commons.util to org.junit.jupiter.api, org.junit.jupiter.engine, diff --git a/junit-platform-commons/src/test/README.md b/junit-platform-commons/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-commons/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/ConcurrencyTestingUtils.java b/junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/ConcurrencyTestingUtils.java index 70dabdf010a8..69f22ef08c40 100644 --- a/junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/ConcurrencyTestingUtils.java +++ b/junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/ConcurrencyTestingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/TestClassLoader.java b/junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/TestClassLoader.java index e408068352b1..62965ffc25df 100644 --- a/junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/TestClassLoader.java +++ b/junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/TestClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console-standalone/junit-platform-console-standalone.gradle.kts b/junit-platform-console-standalone/junit-platform-console-standalone.gradle.kts index 93eabb96a553..d3d933e10caa 100644 --- a/junit-platform-console-standalone/junit-platform-console-standalone.gradle.kts +++ b/junit-platform-console-standalone/junit-platform-console-standalone.gradle.kts @@ -10,7 +10,7 @@ description = "JUnit Platform Console Standalone" dependencies { shadowed(projects.junitPlatformReporting) shadowed(projects.junitPlatformConsole) - shadowed(projects.junitPlatformSuite) + shadowed(projects.junitPlatformSuiteEngine) shadowed(projects.junitJupiterEngine) shadowed(projects.junitJupiterParams) shadowed(projects.junitVintageEngine) @@ -29,7 +29,7 @@ tasks { } } val shadowedArtifactsFile by registering(WriteArtifactsFile::class) { - from(configurations.shadowed) + from(configurations.shadowedClasspath) outputFile = layout.buildDirectory.file("shadowed-artifacts") } shadowJar { diff --git a/junit-platform-console/junit-platform-console.gradle.kts b/junit-platform-console/junit-platform-console.gradle.kts index 91acfd91b6d7..be24d568d804 100644 --- a/junit-platform-console/junit-platform-console.gradle.kts +++ b/junit-platform-console/junit-platform-console.gradle.kts @@ -1,8 +1,9 @@ +import junitbuild.java.UpdateJarAction + plugins { id("junitbuild.java-library-conventions") id("junitbuild.shadow-conventions") id("junitbuild.java-multi-release-sources") - id("junitbuild.java-repackage-jars") } description = "JUnit Platform Console" @@ -40,10 +41,9 @@ tasks { into("META-INF") } from(sourceSets.mainRelease9.get().output.classesDirs) - doLast(objects.newInstance(junitbuild.java.ExecJarAction::class).apply { + doLast(objects.newInstance(UpdateJarAction::class).apply { javaLauncher = project.javaToolchains.launcherFor(java.toolchain) args.addAll( - "--update", "--file", archiveFile.get().asFile.absolutePath, "--main-class", "org.junit.platform.console.ConsoleLauncher", "--release", "17", diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java b/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java index 5127c2187d2f..6ffd9ebefa5d 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/OutputOptionsMixin.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/AnsiColorOptionMixin.java similarity index 74% rename from junit-platform-console/src/main/java/org/junit/platform/console/options/OutputOptionsMixin.java rename to junit-platform-console/src/main/java/org/junit/platform/console/options/AnsiColorOptionMixin.java index 6f18f7e06fae..19a29afac5e7 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/OutputOptionsMixin.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/AnsiColorOptionMixin.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -18,22 +18,14 @@ import picocli.CommandLine.Option; import picocli.CommandLine.Spec; -class OutputOptionsMixin { +class AnsiColorOptionMixin { @Spec(MIXEE) CommandSpec commandSpec; - @Option(names = "--disable-banner", description = "Disable print out of the welcome message.") - private boolean disableBanner; - - @Option(names = "-disable-banner", hidden = true) - private boolean disableBanner2; - - private boolean disableAnsiColors; - - public boolean isDisableBanner() { - return disableBanner || disableBanner2; - } + // https://no-color.org + // ANSI is disabled when environment variable NO_COLOR is defined (regardless of its value). + private boolean disableAnsiColors = System.getenv("NO_COLOR") != null; public boolean isDisableAnsiColors() { return disableAnsiColors; diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/BannerOptionMixin.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/BannerOptionMixin.java new file mode 100644 index 000000000000..0517b760ab48 --- /dev/null +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/BannerOptionMixin.java @@ -0,0 +1,27 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.console.options; + +import picocli.CommandLine.Option; + +class BannerOptionMixin { + + @Option(names = "--disable-banner", description = "Disable print out of the welcome message.") + private boolean disableBanner; + + @Option(names = "-disable-banner", hidden = true) + private boolean disableBanner2; + + public boolean isDisableBanner() { + return disableBanner || disableBanner2; + } + +} diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/BaseCommand.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/BaseCommand.java index 70805ebce2db..d484b182399f 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/BaseCommand.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/BaseCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -28,12 +28,19 @@ abstract class BaseCommand implements Callable { CommandSpec commandSpec; @Mixin - OutputOptionsMixin outputOptions; + AnsiColorOptionMixin ansiColorOption; + + @Mixin + BannerOptionMixin bannerOption; @SuppressWarnings("unused") @Option(names = { "-h", "--help" }, usageHelp = true, description = "Display help information.") private boolean helpRequested; + @SuppressWarnings("unused") + @Option(names = "--version", versionHelp = true, description = "Display version information.") + private boolean versionHelpRequested; + void execute(String... args) { toCommandLine().execute(args); } @@ -67,7 +74,7 @@ static CommandLine initialize(CommandLine commandLine) { @Override public final T call() { PrintWriter out = getOut(); - if (!outputOptions.isDisableBanner()) { + if (!bannerOption.isDisableBanner()) { displayBanner(out); } try { diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/ClasspathEntriesConverter.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/ClasspathEntriesConverter.java index 8ea5670c7d39..fcc4524df029 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/ClasspathEntriesConverter.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/ClasspathEntriesConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java index 352e1b265e18..a591a59abb70 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,6 +13,7 @@ import static org.apiguardian.api.API.Status.INTERNAL; import java.io.PrintWriter; +import java.util.Optional; import org.apiguardian.api.API; import org.junit.platform.console.tasks.ConsoleTestExecutor; @@ -33,6 +34,9 @@ public CommandFacade(ConsoleTestExecutor.Factory consoleTestExecutorFactory) { } public CommandResult run(PrintWriter out, PrintWriter err, String[] args) { + Optional version = ManifestVersionProvider.getImplementationVersion(); + System.setProperty("junit.docs.version", + version.map(it -> it.endsWith("-SNAPSHOT") ? "snapshot" : it).orElse("current")); return new MainCommand(consoleTestExecutorFactory).run(out, err, args); } } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandResult.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandResult.java index ea6a361fbba7..6fe178fc627b 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandResult.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/ConsoleUtils.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/ConsoleUtils.java index da43806a57f8..1ff644aabd38 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/ConsoleUtils.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/ConsoleUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/Details.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/Details.java index 3482c6497fa9..8371fa1a7644 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/Details.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/Details.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/DiscoverTestsCommand.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/DiscoverTestsCommand.java index c47d116f9e2c..e1ff06004689 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/DiscoverTestsCommand.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/DiscoverTestsCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -39,8 +39,8 @@ class DiscoverTestsCommand extends BaseCommand { protected Void execute(PrintWriter out) { TestDiscoveryOptions discoveryOptions = this.discoveryOptions.toTestDiscoveryOptions(); TestConsoleOutputOptions testOutputOptions = this.testOutputOptions.toTestConsoleOutputOptions(); - testOutputOptions.setAnsiColorOutputDisabled(outputOptions.isDisableAnsiColors()); - consoleTestExecutorFactory.create(discoveryOptions, testOutputOptions).discover(out); + testOutputOptions.setAnsiColorOutputDisabled(this.ansiColorOption.isDisableAnsiColors()); + this.consoleTestExecutorFactory.create(discoveryOptions, testOutputOptions).discover(out); return null; } } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/ExecuteTestsCommand.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/ExecuteTestsCommand.java index 82c37dc2297c..93e083217de9 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/ExecuteTestsCommand.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/ExecuteTestsCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -77,7 +77,7 @@ TestDiscoveryOptions toTestDiscoveryOptions() { TestConsoleOutputOptions toTestConsoleOutputOptions() { TestConsoleOutputOptions testOutputOptions = this.testOutputOptions.toTestConsoleOutputOptions(); - testOutputOptions.setAnsiColorOutputDisabled(outputOptions.isDisableAnsiColors()); + testOutputOptions.setAnsiColorOutputDisabled(this.ansiColorOption.isDisableAnsiColors()); return testOutputOptions; } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/ListTestEnginesCommand.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/ListTestEnginesCommand.java index 029e76856f19..875bef485f78 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/ListTestEnginesCommand.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/ListTestEnginesCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java index 610094564b9e..d4a86e807785 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -22,6 +22,7 @@ import picocli.CommandLine.Command; import picocli.CommandLine.Help.ColorScheme; import picocli.CommandLine.IExitCodeGenerator; +import picocli.CommandLine.Mixin; import picocli.CommandLine.Model.CommandSpec; import picocli.CommandLine.Option; import picocli.CommandLine.Spec; @@ -37,23 +38,30 @@ description = "Launches the JUnit Platform for test discovery and execution.", // footerHeading = "%n", // footer = "For more information, please refer to the JUnit User Guide at%n" // - + "@|underline https://junit.org/junit5/docs/current/user-guide/|@", // + + "@|underline https://junit.org/junit5/docs/${junit.docs.version}/user-guide/|@", // scope = CommandLine.ScopeType.INHERIT, // exitCodeOnInvalidInput = CommandResult.FAILURE, // - exitCodeOnExecutionException = CommandResult.FAILURE // + exitCodeOnExecutionException = CommandResult.FAILURE, // + versionProvider = ManifestVersionProvider.class // ) class MainCommand implements Callable, IExitCodeGenerator { private final ConsoleTestExecutor.Factory consoleTestExecutorFactory; - @Option(names = { "-h", "--help" }, help = true, hidden = true) + @Option(names = { "-h", "--help" }, help = true, description = "Display help information.") private boolean helpRequested; @Option(names = { "--h", "-help" }, help = true, hidden = true) private boolean helpRequested2; + @Option(names = "--version", versionHelp = true, description = "Display version information.") + private boolean versionHelpRequested; + + @Mixin + AnsiColorOptionMixin ansiColorOption; + @Unmatched - private List allParameters = new ArrayList<>(); + private final List allParameters = new ArrayList<>(); @Spec CommandSpec commandSpec; @@ -71,6 +79,11 @@ public Object call() { commandResult = CommandResult.success(); return null; } + if (versionHelpRequested) { + commandSpec.commandLine().printVersionHelp(commandSpec.commandLine().getOut()); + commandResult = CommandResult.success(); + return null; + } if (allParameters.contains("--list-engines")) { return runCommand("engines", Optional.of("--list-engines")); } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/ManifestVersionProvider.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/ManifestVersionProvider.java new file mode 100644 index 000000000000..9b254727fa2f --- /dev/null +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/ManifestVersionProvider.java @@ -0,0 +1,35 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.console.options; + +import java.util.Optional; + +import org.junit.platform.commons.util.PackageUtils; + +import picocli.CommandLine; + +class ManifestVersionProvider implements CommandLine.IVersionProvider { + + public static Optional getImplementationVersion() { + return PackageUtils.getModuleOrImplementationVersion(ManifestVersionProvider.class); + } + + @Override + public String[] getVersion() { + return new String[] { // + String.format("@|bold JUnit Platform Console Launcher %s|@", + getImplementationVersion().orElse("")), // + "JVM: ${java.version} (${java.vendor} ${java.vm.name} ${java.vm.version})", // + "OS: ${os.name} ${os.version} ${os.arch}" // + }; + } + +} diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/SelectorConverter.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/SelectorConverter.java index 72ea077e67bd..3bb444602cb8 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/SelectorConverter.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/SelectorConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -14,22 +14,17 @@ import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathResource; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectDirectory; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectFile; -import static org.junit.platform.engine.discovery.DiscoverySelectors.selectIteration; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectModule; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectUri; -import java.util.Arrays; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.IntStream; - -import org.junit.platform.commons.util.Preconditions; -import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.engine.DiscoverySelectorIdentifier; import org.junit.platform.engine.discovery.ClassSelector; import org.junit.platform.engine.discovery.ClasspathResourceSelector; import org.junit.platform.engine.discovery.DirectorySelector; +import org.junit.platform.engine.discovery.DiscoverySelectors; import org.junit.platform.engine.discovery.FileSelector; import org.junit.platform.engine.discovery.IterationSelector; import org.junit.platform.engine.discovery.MethodSelector; @@ -99,51 +94,21 @@ public ClasspathResourceSelector convert(String value) { static class Iteration implements ITypeConverter { - public static final Pattern PATTERN = Pattern.compile( - "(?[a-z]+):(?.*)\\[(?(\\d+)(\\.\\.\\d+)?(\\s*,\\s*(\\d+)(\\.\\.\\d+)?)*)]"); - @Override public IterationSelector convert(String value) { - Matcher matcher = PATTERN.matcher(value); - Preconditions.condition(matcher.matches(), "Invalid format: must be TYPE:VALUE[INDEX(,INDEX)*]"); - DiscoverySelector parentSelector = createParentSelector(matcher.group("type"), matcher.group("value")); - int[] iterationIndices = Arrays.stream(matcher.group("indices").split(",")) // - .flatMapToInt(this::parseIndexDefinition) // - .toArray(); - return selectIteration(parentSelector, iterationIndices); + DiscoverySelectorIdentifier identifier = DiscoverySelectorIdentifier.create( + IterationSelector.IdentifierParser.PREFIX, value); + return (IterationSelector) DiscoverySelectors.parse(identifier) // + .orElseThrow(() -> new PreconditionViolationException("Invalid format: Failed to parse selector")); } - private IntStream parseIndexDefinition(String value) { - String[] parts = value.split("\\.\\.", 2); - int firstIndex = Integer.parseInt(parts[0]); - if (parts.length == 2) { - int lastIndex = Integer.parseInt(parts[1]); - return IntStream.rangeClosed(firstIndex, lastIndex); - } - return IntStream.of(firstIndex); - } + } + + static class Identifier implements ITypeConverter { - private DiscoverySelector createParentSelector(String type, String value) { - switch (type) { - case "module": - return selectModule(value); - case "uri": - return selectUri(value); - case "file": - return selectFile(value); - case "directory": - return selectDirectory(value); - case "package": - return selectPackage(value); - case "class": - return selectClass(value); - case "method": - return selectMethod(value); - case "resource": - return selectClasspathResource(value); - default: - throw new IllegalArgumentException("Unknown type: " + type); - } + @Override + public DiscoverySelectorIdentifier convert(String value) { + return DiscoverySelectorIdentifier.parse(value); } } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/TestConsoleOutputOptions.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/TestConsoleOutputOptions.java index 680c2d826bad..c0ff2aa5b77d 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/TestConsoleOutputOptions.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/TestConsoleOutputOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/TestConsoleOutputOptionsMixin.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/TestConsoleOutputOptionsMixin.java index dd24838e3f55..a9f5d00d646e 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/TestConsoleOutputOptionsMixin.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/TestConsoleOutputOptionsMixin.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -39,17 +39,17 @@ static class ConsoleOutputOptions { @Option(names = "--details", paramLabel = "MODE", defaultValue = DEFAULT_DETAILS_NAME, description = "Select an output details mode for when tests are executed. " // + "Use one of: ${COMPLETION-CANDIDATES}. If 'none' is selected, " // + "then only the summary and test failures are shown. Default: ${DEFAULT-VALUE}.") - private Details details = DEFAULT_DETAILS; + private final Details details = DEFAULT_DETAILS; @Option(names = "-details", hidden = true, defaultValue = DEFAULT_DETAILS_NAME) - private Details details2 = DEFAULT_DETAILS; + private final Details details2 = DEFAULT_DETAILS; @Option(names = "--details-theme", paramLabel = "THEME", description = "Select an output details tree theme for when tests are executed. " + "Use one of: ${COMPLETION-CANDIDATES}. Default is detected based on default character encoding.") - private Theme theme = DEFAULT_THEME; + private final Theme theme = DEFAULT_THEME; @Option(names = "-details-theme", hidden = true) - private Theme theme2 = DEFAULT_THEME; + private final Theme theme2 = DEFAULT_THEME; private void applyTo(TestConsoleOutputOptions result) { result.setColorPalettePath(choose(colorPalette, colorPalette2, null)); diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptions.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptions.java index b7949de159f6..64dd75973922 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptions.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -25,9 +25,11 @@ import org.apiguardian.api.API; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; import org.junit.platform.engine.discovery.ClassSelector; import org.junit.platform.engine.discovery.ClasspathResourceSelector; import org.junit.platform.engine.discovery.DirectorySelector; +import org.junit.platform.engine.discovery.DiscoverySelectors; import org.junit.platform.engine.discovery.FileSelector; import org.junit.platform.engine.discovery.IterationSelector; import org.junit.platform.engine.discovery.MethodSelector; @@ -56,6 +58,7 @@ public class TestDiscoveryOptions { private List selectedMethods = emptyList(); private List selectedClasspathResources = emptyList(); private List selectedIterations = emptyList(); + private List selectorIdentifiers = emptyList(); private List includedClassNamePatterns = singletonList(STANDARD_INCLUDE_PATTERN); private List excludedClassNamePatterns = emptyList(); @@ -66,6 +69,7 @@ public class TestDiscoveryOptions { private List includedTagExpressions = emptyList(); private List excludedTagExpressions = emptyList(); + private List configurationParametersResources = emptyList(); private Map configurationParameters = emptyMap(); public boolean isScanModulepath() { @@ -176,6 +180,14 @@ public void setSelectedIterations(List selectedIterations) { this.selectedIterations = selectedIterations; } + public List getSelectorIdentifiers() { + return selectorIdentifiers; + } + + public void setSelectorIdentifiers(List selectorIdentifiers) { + this.selectorIdentifiers = selectorIdentifiers; + } + public List getExplicitSelectors() { List selectors = new ArrayList<>(); selectors.addAll(getSelectedUris()); @@ -187,6 +199,7 @@ public List getExplicitSelectors() { selectors.addAll(getSelectedMethods()); selectors.addAll(getSelectedClasspathResources()); selectors.addAll(getSelectedIterations()); + DiscoverySelectors.parseAll(getSelectorIdentifiers()).forEach(selectors::add); return selectors; } @@ -262,4 +275,12 @@ public void setConfigurationParameters(Map configurationParamete this.configurationParameters = configurationParameters; } + public List getConfigurationParametersResources() { + return this.configurationParametersResources; + } + + public TestDiscoveryOptions setConfigurationParametersResources(List configurationParametersResources) { + this.configurationParametersResources = configurationParametersResources; + return this; + } } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java index 2a8e7751e3a1..a5fce585dd26 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; +import org.junit.platform.engine.DiscoverySelectorIdentifier; import org.junit.platform.engine.discovery.ClassNameFilter; import org.junit.platform.engine.discovery.ClassSelector; import org.junit.platform.engine.discovery.ClasspathResourceSelector; @@ -39,7 +40,9 @@ class TestDiscoveryOptionsMixin { @ArgGroup(validate = false, order = 2, heading = "%n@|bold SELECTORS|@%n%n") SelectorOptions selectorOptions; - @ArgGroup(validate = false, order = 3, heading = "%n@|bold FILTERS|@%n%n") + @ArgGroup(validate = false, order = 3, heading = "%n For more information on selectors including syntax examples, see" + + "%n @|underline https://junit.org/junit5/docs/current/user-guide/#running-tests-discovery-selectors|@" + + "%n%n@|bold FILTERS|@%n%n") FilterOptions filterOptions; @ArgGroup(validate = false, order = 4, heading = "%n@|bold RUNTIME CONFIGURATION|@%n%n") @@ -67,73 +70,81 @@ static class SelectorOptions { @Option(names = { "-u", "--select-uri" }, paramLabel = "URI", arity = "1", converter = SelectorConverter.Uri.class, description = "Select a URI for test discovery. This option can be repeated.") - private List selectedUris = new ArrayList<>(); + private final List selectedUris = new ArrayList<>(); @Option(names = { "--u", "-select-uri" }, arity = "1", hidden = true, converter = SelectorConverter.Uri.class) - private List selectedUris2 = new ArrayList<>(); + private final List selectedUris2 = new ArrayList<>(); @Option(names = { "-f", "--select-file" }, paramLabel = "FILE", arity = "1", converter = SelectorConverter.File.class, description = "Select a file for test discovery. This option can be repeated.") - private List selectedFiles = new ArrayList<>(); + private final List selectedFiles = new ArrayList<>(); @Option(names = { "--f", "-select-file" }, arity = "1", hidden = true, converter = SelectorConverter.File.class) - private List selectedFiles2 = new ArrayList<>(); + private final List selectedFiles2 = new ArrayList<>(); @Option(names = { "-d", "--select-directory" }, paramLabel = "DIR", arity = "1", converter = SelectorConverter.Directory.class, description = "Select a directory for test discovery. This option can be repeated.") - private List selectedDirectories = new ArrayList<>(); + private final List selectedDirectories = new ArrayList<>(); @Option(names = { "--d", "-select-directory" }, arity = "1", hidden = true, converter = SelectorConverter.Directory.class) - private List selectedDirectories2 = new ArrayList<>(); + private final List selectedDirectories2 = new ArrayList<>(); @Option(names = { "-o", "--select-module" }, paramLabel = "NAME", arity = "1", converter = SelectorConverter.Module.class, description = "Select single module for test discovery. This option can be repeated.") - private List selectedModules = new ArrayList<>(); + private final List selectedModules = new ArrayList<>(); @Option(names = { "--o", "-select-module" }, arity = "1", converter = SelectorConverter.Module.class, hidden = true) - private List selectedModules2 = new ArrayList<>(); + private final List selectedModules2 = new ArrayList<>(); @Option(names = { "-p", "--select-package" }, paramLabel = "PKG", arity = "1", converter = SelectorConverter.Package.class, description = "Select a package for test discovery. This option can be repeated.") - private List selectedPackages = new ArrayList<>(); + private final List selectedPackages = new ArrayList<>(); @Option(names = { "--p", "-select-package" }, arity = "1", hidden = true, converter = SelectorConverter.Package.class) - private List selectedPackages2 = new ArrayList<>(); + private final List selectedPackages2 = new ArrayList<>(); @Option(names = { "-c", "--select-class" }, paramLabel = "CLASS", arity = "1", converter = SelectorConverter.Class.class, description = "Select a class for test discovery. This option can be repeated.") - private List selectedClasses = new ArrayList<>(); + private final List selectedClasses = new ArrayList<>(); @Option(names = { "--c", "-select-class" }, arity = "1", hidden = true, converter = SelectorConverter.Class.class) - private List selectedClasses2 = new ArrayList<>(); + private final List selectedClasses2 = new ArrayList<>(); @Option(names = { "-m", "--select-method" }, paramLabel = "NAME", arity = "1", converter = SelectorConverter.Method.class, description = "Select a method for test discovery. This option can be repeated.") - private List selectedMethods = new ArrayList<>(); + private final List selectedMethods = new ArrayList<>(); @Option(names = { "--m", "-select-method" }, arity = "1", hidden = true, converter = SelectorConverter.Method.class) - private List selectedMethods2 = new ArrayList<>(); + private final List selectedMethods2 = new ArrayList<>(); @Option(names = { "-r", "--select-resource" }, paramLabel = "RESOURCE", arity = "1", converter = SelectorConverter.ClasspathResource.class, description = "Select a classpath resource for test discovery. This option can be repeated.") - private List selectedClasspathResources = new ArrayList<>(); + private final List selectedClasspathResources = new ArrayList<>(); @Option(names = { "--r", "-select-resource" }, arity = "1", hidden = true, converter = SelectorConverter.ClasspathResource.class) - private List selectedClasspathResources2 = new ArrayList<>(); + private final List selectedClasspathResources2 = new ArrayList<>(); @Option(names = { "-i", - "--select-iteration" }, paramLabel = "TYPE:VALUE[INDEX(..INDEX)?(,INDEX(..INDEX)?)*]", arity = "1", converter = SelectorConverter.Iteration.class, description = "Select iterations for test discovery (e.g. method:com.acme.Foo#m()[1..2]). This option can be repeated.") - private List selectedIterations = new ArrayList<>(); + "--select-iteration" }, paramLabel = "PREFIX:VALUE[INDEX(..INDEX)?(,INDEX(..INDEX)?)*]", arity = "1", converter = SelectorConverter.Iteration.class, // + description = "Select iterations for test discovery via a prefixed identifier and a list of indexes or index ranges " + + "(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). " + + "This option can be repeated.") + private final List selectedIterations = new ArrayList<>(); @Option(names = { "--i", "-select-iteration" }, arity = "1", hidden = true, converter = SelectorConverter.Iteration.class) - private List selectedIterations2 = new ArrayList<>(); + private final List selectedIterations2 = new ArrayList<>(); + + @Option(names = "--select", paramLabel = "PREFIX:VALUE", arity = "1", converter = SelectorConverter.Identifier.class, // + description = "Select via a prefixed identifier (e.g. method:com.acme.Foo#m selects the m() method in the com.acme.Foo class). " + + "This option can be repeated.") + private final List selectorIdentifiers = new ArrayList<>(); SelectorOptions() { } @@ -152,6 +163,7 @@ private void applyTo(TestDiscoveryOptions result) { result.setSelectedClasspathResources( merge(this.selectedClasspathResources, this.selectedClasspathResources2)); result.setSelectedIterations(merge(this.selectedIterations, this.selectedIterations2)); + result.setSelectorIdentifiers(this.selectorIdentifiers); } } @@ -163,64 +175,64 @@ static class FilterOptions { + "names that begin with \"Test\" or end with \"Test\" or \"Tests\". " // + "When this option is repeated, all patterns will be combined using OR semantics. " // + "Default: ${DEFAULT-VALUE}") - private List includeClassNamePatterns = new ArrayList<>(); + private final List includeClassNamePatterns = new ArrayList<>(); @Option(names = { "--n", "-include-classname" }, arity = "1", hidden = true) - private List includeClassNamePatterns2 = new ArrayList<>(); + private final List includeClassNamePatterns2 = new ArrayList<>(); @Option(names = { "-N", "--exclude-classname" }, paramLabel = "PATTERN", arity = "1", description = "Provide a regular expression to exclude those classes whose fully qualified names match. " // + "When this option is repeated, all patterns will be combined using OR semantics.") - private List excludeClassNamePatterns = new ArrayList<>(); + private final List excludeClassNamePatterns = new ArrayList<>(); @Option(names = { "--N", "-exclude-classname" }, arity = "1", hidden = true) - private List excludeClassNamePatterns2 = new ArrayList<>(); + private final List excludeClassNamePatterns2 = new ArrayList<>(); @Option(names = { "--include-package" }, paramLabel = "PKG", arity = "1", description = "Provide a package to be included in the test run. This option can be repeated.") - private List includePackages = new ArrayList<>(); + private final List includePackages = new ArrayList<>(); @Option(names = { "-include-package" }, arity = "1", hidden = true) - private List includePackages2 = new ArrayList<>(); + private final List includePackages2 = new ArrayList<>(); @Option(names = { "--exclude-package" }, paramLabel = "PKG", arity = "1", description = "Provide a package to be excluded from the test run. This option can be repeated.") - private List excludePackages = new ArrayList<>(); + private final List excludePackages = new ArrayList<>(); @Option(names = { "-exclude-package" }, arity = "1", hidden = true) - private List excludePackages2 = new ArrayList<>(); + private final List excludePackages2 = new ArrayList<>(); @Option(names = { "-t", "--include-tag" }, paramLabel = "TAG", arity = "1", description = "Provide a tag or tag expression to include only tests whose tags match. " + // "When this option is repeated, all patterns will be combined using OR semantics.") - private List includedTags = new ArrayList<>(); + private final List includedTags = new ArrayList<>(); @Option(names = { "--t", "-include-tag" }, arity = "1", hidden = true) - private List includedTags2 = new ArrayList<>(); + private final List includedTags2 = new ArrayList<>(); @Option(names = { "-T", "--exclude-tag" }, paramLabel = "TAG", arity = "1", description = "Provide a tag or tag expression to exclude those tests whose tags match. " + // "When this option is repeated, all patterns will be combined using OR semantics.") - private List excludedTags = new ArrayList<>(); + private final List excludedTags = new ArrayList<>(); @Option(names = { "--T", "-exclude-tag" }, arity = "1", hidden = true) - private List excludedTags2 = new ArrayList<>(); + private final List excludedTags2 = new ArrayList<>(); @Option(names = { "-e", "--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.") - private List includedEngines = new ArrayList<>(); + private final List includedEngines = new ArrayList<>(); @Option(names = { "--e", "-include-engine" }, arity = "1", hidden = true) - private List includedEngines2 = new ArrayList<>(); + private final List includedEngines2 = new ArrayList<>(); @Option(names = { "-E", "--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.") - private List excludedEngines = new ArrayList<>(); + private final List excludedEngines = new ArrayList<>(); @Option(names = { "--E", "-exclude-engine" }, arity = "1", hidden = true) - private List excludedEngines2 = new ArrayList<>(); + private final List excludedEngines2 = new ArrayList<>(); private void applyTo(TestDiscoveryOptions result) { result.setIncludedClassNamePatterns(merge(this.includeClassNamePatterns, this.includeClassNamePatterns2)); @@ -239,14 +251,18 @@ static class RuntimeConfigurationOptions { @Option(names = { "-" + CP_OPTION, "--classpath", "--class-path" }, converter = ClasspathEntriesConverter.class, paramLabel = "PATH", arity = "1", description = "Provide additional classpath entries " + "-- for example, for adding engines and their dependencies. This option can be repeated.") - private List additionalClasspathEntries = new ArrayList<>(); + private final List additionalClasspathEntries = new ArrayList<>(); @Option(names = { "--cp", "-classpath", "-class-path" }, converter = ClasspathEntriesConverter.class, hidden = true) - private List additionalClasspathEntries2 = new ArrayList<>(); + private final List additionalClasspathEntries2 = new ArrayList<>(); // Implementation note: the @Option annotation is on a setter method to allow validation. - private Map configurationParameters = new LinkedHashMap<>(); + private final Map configurationParameters = new LinkedHashMap<>(); + + @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.") + private List configurationParametersResources = new ArrayList<>(); @CommandLine.Spec private CommandLine.Model.CommandSpec spec; @@ -284,6 +300,7 @@ private void validateUnique(String key, String newValue) { private void applyTo(TestDiscoveryOptions result) { result.setAdditionalClasspathEntries(merge(additionalClasspathEntries, additionalClasspathEntries2)); + result.setConfigurationParametersResources(configurationParametersResources); result.setConfigurationParameters(configurationParameters); } } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/Theme.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/Theme.java index 8981def9035e..b41aaea1f55c 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/Theme.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/Theme.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/ColorPalette.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/ColorPalette.java index a587980f362a..790d90a61e44 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/ColorPalette.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/ColorPalette.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/ConsoleTestExecutor.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/ConsoleTestExecutor.java index 694005933cdf..1cdddf814e10 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/ConsoleTestExecutor.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/ConsoleTestExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/CustomContextClassLoaderExecutor.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/CustomContextClassLoaderExecutor.java index 2bd075b15f27..470a983bedf7 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/CustomContextClassLoaderExecutor.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/CustomContextClassLoaderExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DetailsPrintingListener.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DetailsPrintingListener.java index ece60eb52fbf..fc2fbd25da5b 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DetailsPrintingListener.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DetailsPrintingListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java index 89aeec8c3817..32a4090bc4e2 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -54,6 +54,8 @@ LauncherDiscoveryRequest toDiscoveryRequest(TestDiscoveryOptions options) { requestBuilder.selectors(selectors); addFilters(requestBuilder, options, selectors); requestBuilder.configurationParameters(options.getConfigurationParameters()); + requestBuilder.configurationParametersResources( + options.getConfigurationParametersResources().toArray(new String[0])); return requestBuilder.build(); } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/FlatPrintingListener.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/FlatPrintingListener.java index 304cedc27a2f..0cbe2abd3352 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/FlatPrintingListener.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/FlatPrintingListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/Style.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/Style.java index 31f530b85c02..abc1cdd2ce87 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/Style.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/Style.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TestFeedPrintingListener.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TestFeedPrintingListener.java index c7c273c1a92e..f58841d3578c 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TestFeedPrintingListener.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TestFeedPrintingListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreeNode.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreeNode.java index 40fdbf9345ef..8c1217f8cf68 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreeNode.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreeNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreePrinter.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreePrinter.java index 34de4f217854..8e171d48d3b2 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreePrinter.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreePrinter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreePrintingListener.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreePrintingListener.java index 39d8126595fa..437f7b56733e 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreePrintingListener.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/TreePrintingListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/VerboseTreePrintingListener.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/VerboseTreePrintingListener.java index 2dbd7e01d77a..a8df758dc114 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/VerboseTreePrintingListener.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/VerboseTreePrintingListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java17/org/junit/platform/console/options/ConsoleUtils.java b/junit-platform-console/src/main/java17/org/junit/platform/console/options/ConsoleUtils.java index 89698f1019f6..f44f94b41e69 100644 --- a/junit-platform-console/src/main/java17/org/junit/platform/console/options/ConsoleUtils.java +++ b/junit-platform-console/src/main/java17/org/junit/platform/console/options/ConsoleUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/main/java9/org/junit/platform/console/ConsoleLauncherToolProvider.java b/junit-platform-console/src/main/java9/org/junit/platform/console/ConsoleLauncherToolProvider.java index 37dfb476105f..5783bff33454 100644 --- a/junit-platform-console/src/main/java9/org/junit/platform/console/ConsoleLauncherToolProvider.java +++ b/junit-platform-console/src/main/java9/org/junit/platform/console/ConsoleLauncherToolProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-console/src/test/README.md b/junit-platform-console/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-console/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/CompositeFilter.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/CompositeFilter.java index 41522e850bf8..3ab3e6d2f554 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/CompositeFilter.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/CompositeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/ConfigurationParameters.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/ConfigurationParameters.java index 7c859ab31c62..498971894992 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/ConfigurationParameters.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/ConfigurationParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoveryFilter.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoveryFilter.java index 68b5b1d588a0..7872867244d0 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoveryFilter.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoveryFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoverySelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoverySelector.java index 06c222ac6c1c..342f536ca2ce 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoverySelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoverySelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,9 +10,13 @@ package org.junit.platform.engine; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; +import java.util.Optional; + import org.apiguardian.api.API; +import org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser; /** * A selector defines what a {@link TestEngine} can use to discover tests @@ -25,4 +29,21 @@ */ @API(status = STABLE, since = "1.0") public interface DiscoverySelector { + + /** + * Return the {@linkplain DiscoverySelectorIdentifier identifier} of this + * selector. + *

+ * The returned identifier has to be parsable by a corresponding + * {@link DiscoverySelectorIdentifierParser}. + * + * @return the identifier of this selector or empty if it is not supported; + * never {@code null} + * @since 1.11 + */ + @API(status = EXPERIMENTAL, since = "1.11") + default Optional toIdentifier() { + return Optional.empty(); + } + } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoverySelectorIdentifier.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoverySelectorIdentifier.java new file mode 100644 index 000000000000..4fd926e9b27b --- /dev/null +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoverySelectorIdentifier.java @@ -0,0 +1,114 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.engine; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.util.Objects; + +import org.apiguardian.api.API; +import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.commons.util.StringUtils; + +/** + * Identifier for {@link DiscoverySelector DiscoverySelectors} with a specific + * prefix. + *

+ * The {@linkplain #toString() string representation} of an identifier is + * intended to be human-readable and is formatted as {@code prefix:value}. + * + * @since 1.11 + * @see org.junit.platform.engine.discovery.DiscoverySelectors#parse(String) + * @see org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser + */ +@API(status = EXPERIMENTAL, since = "1.11") +public final class DiscoverySelectorIdentifier { + + private final String prefix; + private final String value; + + /** + * Create a new {@link DiscoverySelectorIdentifier} with the supplied prefix and + * value. + * + * @param prefix the prefix; never {@code null} or blank + * @param value the value; never {@code null} or blank + */ + public static DiscoverySelectorIdentifier create(String prefix, String value) { + return new DiscoverySelectorIdentifier(prefix, value); + } + + /** + * Parse the supplied string representation of a + * {@link DiscoverySelectorIdentifier} in the format {@code prefix:value}. + * + * @param string the string representation of a {@link DiscoverySelectorIdentifier} + * @return the parsed {@link DiscoverySelectorIdentifier} + * @throws PreconditionViolationException if the supplied string does not + * conform to the expected format + */ + public static DiscoverySelectorIdentifier parse(String string) { + return StringUtils.splitIntoTwo(':', string).mapTwo( // + () -> new PreconditionViolationException("Identifier string must be 'prefix:value', but was " + string), + DiscoverySelectorIdentifier::new // + ); + } + + private DiscoverySelectorIdentifier(String prefix, String value) { + this.prefix = Preconditions.notBlank(prefix, "prefix must not be blank"); + this.value = Preconditions.notBlank(value, "value must not be blank"); + } + + /** + * Get the prefix of this identifier. + * + * @return the prefix; never {@code null} or blank + */ + public String getPrefix() { + return prefix; + } + + /** + * Get the value of this identifier. + * + * @return the value; never {@code null} or blank + */ + public String getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DiscoverySelectorIdentifier that = (DiscoverySelectorIdentifier) o; + return Objects.equals(prefix, that.prefix) && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(prefix, value); + } + + /** + * Get the string representation of this identifier in the format + * {@code prefix:value}. + */ + @Override + public String toString() { + return String.format("%s:%s", prefix, value); + } +} diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineDiscoveryListener.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineDiscoveryListener.java index 4152ce8f52fa..f5683f5e67a1 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineDiscoveryListener.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineDiscoveryListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineDiscoveryRequest.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineDiscoveryRequest.java index 0ea3b70ce8d7..41f6e3b76f18 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineDiscoveryRequest.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineDiscoveryRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineExecutionListener.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineExecutionListener.java index 315548ff3b98..80727af0e3c2 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineExecutionListener.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/EngineExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/ExecutionRequest.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/ExecutionRequest.java index 2c77f1fd5d66..ce0b9e0c65c7 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/ExecutionRequest.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/ExecutionRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/Filter.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/Filter.java index 2d77ff1efd2b..a18244ee411b 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/Filter.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/Filter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/FilterResult.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/FilterResult.java index 622205aa3bf9..81970ac3daaa 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/FilterResult.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/FilterResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/SelectorResolutionResult.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/SelectorResolutionResult.java index 56ac8d0bce7c..087ed1e93fd7 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/SelectorResolutionResult.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/SelectorResolutionResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java index 0f7f6f20ea49..c1f09a6b4c14 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestEngine.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestEngine.java index 401fa19363c8..67122ccc9a72 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestEngine.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestEngine.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -54,6 +54,8 @@ public interface TestEngine { * and JUnit Jupiter use {@code "junit-vintage"} and {@code "junit-jupiter"}, * respectively. When in doubt, you may use the fully qualified name of your * custom {@code TestEngine} implementation class. + * + * @return the ID of this test engine; never {@code null} or blank */ String getId(); @@ -184,12 +186,7 @@ default Optional getVersion() { if (standalone.isPresent()) { return standalone; } - String fallback = "DEVELOPMENT"; - Optional moduleVersion = ModuleUtils.getModuleVersion(getClass()); - if (moduleVersion.isPresent()) { - return moduleVersion; - } - return Optional.of(PackageUtils.getAttribute(getClass(), Package::getImplementationVersion).orElse(fallback)); + return Optional.of(PackageUtils.getModuleOrImplementationVersion(getClass()).orElse("DEVELOPMENT")); } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestExecutionResult.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestExecutionResult.java index 6509ecd47b41..b3a4b280e36d 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestExecutionResult.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestExecutionResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -53,7 +53,7 @@ public enum Status { /** * Indicates that the execution of a test or container failed. */ - FAILED; + FAILED } @@ -62,6 +62,7 @@ public enum Status { /** * Create a {@code TestExecutionResult} for a successful execution * of a test or container. + * * @return the {@code TestExecutionResult}; never {@code null} */ public static TestExecutionResult successful() { diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestSource.java index 0f3d873869a0..c10479314dfc 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestTag.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestTag.java index 9a32e07f9261..956da67c8742 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestTag.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestTag.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/UniqueId.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/UniqueId.java index 5e4f13b99661..fda16880eeda 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/UniqueId.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/UniqueId.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -118,7 +118,7 @@ final Optional getRoot() { * @see #forEngine(String) */ public final Optional getEngineId() { - return getRoot().filter(segment -> segment.getType().equals(ENGINE_SEGMENT_TYPE)).map(Segment::getValue); + return getRoot().filter(segment -> ENGINE_SEGMENT_TYPE.equals(segment.getType())).map(Segment::getValue); } /** diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/UniqueIdFormat.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/UniqueIdFormat.java index f89dddd98a84..cbae6c62a863 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/UniqueIdFormat.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/UniqueIdFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/AbstractClassNameFilter.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/AbstractClassNameFilter.java index d8512db329f7..b6de7748547f 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/AbstractClassNameFilter.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/AbstractClassNameFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClassNameFilter.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClassNameFilter.java index ae4049899b9c..9d41c36b3f6e 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClassNameFilter.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClassNameFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClassSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClassSelector.java index 284c1ed4ff81..2d263b092453 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClassSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClassSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,9 +11,11 @@ package org.junit.platform.engine.discovery; import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import java.util.Objects; +import java.util.Optional; import org.apiguardian.api.API; import org.junit.platform.commons.PreconditionViolationException; @@ -21,6 +23,7 @@ import org.junit.platform.commons.util.ReflectionUtils; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects a {@link Class} or class name so @@ -133,4 +136,31 @@ public String toString() { // @formatter:on } + @Override + public Optional toIdentifier() { + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.className)); + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for {@link ClassSelector + * ClassSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + private static final String PREFIX = "class"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + return Optional.of(DiscoverySelectors.selectClass(identifier.getValue())); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClasspathResourceSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClasspathResourceSelector.java index b257e6c26ce0..1a17cda9119b 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClasspathResourceSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClasspathResourceSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,14 +10,17 @@ package org.junit.platform.engine.discovery; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import java.util.Objects; import java.util.Optional; import org.apiguardian.api.API; +import org.junit.platform.commons.util.StringUtils; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects the name of a classpath resource @@ -101,4 +104,43 @@ public String toString() { this.position).toString(); } + @Override + public Optional toIdentifier() { + if (this.position == null) { + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.classpathResourceName)); + } + else { + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, + String.format("%s?%s", this.classpathResourceName, this.position.toQueryPart()))); + } + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for + * {@link ClasspathResourceSelector ClasspathResourceSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + private static final String PREFIX = "resource"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + return Optional.of(StringUtils.splitIntoTwo('?', identifier.getValue()).map( // + DiscoverySelectors::selectClasspathResource, // + (resourceName, query) -> { + FilePosition position = FilePosition.fromQuery(query).orElse(null); + return DiscoverySelectors.selectClasspathResource(resourceName, position); + } // + )); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClasspathRootSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClasspathRootSelector.java index b9d03f484737..815edd8373ae 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClasspathRootSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClasspathRootSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,14 +10,22 @@ package org.junit.platform.engine.discovery; +import static java.util.Collections.singleton; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; +import static org.junit.platform.commons.util.CollectionUtils.getFirstElement; import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Objects; +import java.util.Optional; import org.apiguardian.api.API; +import org.junit.platform.commons.util.Preconditions; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects a classpath root so that @@ -42,7 +50,7 @@ public class ClasspathRootSelector implements DiscoverySelector { private final URI classpathRoot; ClasspathRootSelector(URI classpathRoot) { - this.classpathRoot = classpathRoot; + this.classpathRoot = Preconditions.notNull(classpathRoot, "classpathRoot must not be null"); } /** @@ -82,4 +90,32 @@ public String toString() { return new ToStringBuilder(this).append("classpathRoot", this.classpathRoot).toString(); } + @Override + public Optional toIdentifier() { + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.classpathRoot.toString())); + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for + * {@link ClasspathRootSelector ClasspathRootSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + private static final String PREFIX = "classpath-root"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + Path path = Paths.get(URI.create(identifier.getValue())); + return getFirstElement(DiscoverySelectors.selectClasspathRoots(singleton(path))); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DirectorySelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DirectorySelector.java index 167da07b1237..976c20ddb695 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DirectorySelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DirectorySelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,7 @@ package org.junit.platform.engine.discovery; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import java.io.File; @@ -18,10 +19,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Objects; +import java.util.Optional; import org.apiguardian.api.API; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects a directory so that @@ -107,4 +110,31 @@ public String toString() { return new ToStringBuilder(this).append("path", this.path).toString(); } + @Override + public Optional toIdentifier() { + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.path)); + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for + * {@link DirectorySelector DirectorySelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + private static final String PREFIX = "directory"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + return Optional.of(DiscoverySelectors.selectDirectory(identifier.getValue())); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectorIdentifierParser.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectorIdentifierParser.java new file mode 100644 index 000000000000..8570c6d5c3cc --- /dev/null +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectorIdentifierParser.java @@ -0,0 +1,71 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.engine.discovery; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.util.Optional; + +import org.apiguardian.api.API; +import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; + +/** + * Parser for {@link DiscoverySelectorIdentifier DiscoverySelectorIdentifiers} + * with a specific prefix. + *

+ * Implementations of this interface can be registered using the Java service + * loader mechanism to extend the set of supported prefixes for + * {@link DiscoverySelectorIdentifier DiscoverySelectorIdentifiers}. + * + * @since 1.11 + * @see DiscoverySelectors#parse(String) + */ +@API(status = EXPERIMENTAL, since = "1.11") +public interface DiscoverySelectorIdentifierParser { + + /** + * Get the prefix that this parser can handle. + * + * @return the prefix that this parser can handle; never {@code null} + */ + String getPrefix(); + + /** + * Parse the supplied {@link DiscoverySelectorIdentifier}. + *

+ * The JUnit Platform will only invoke this method if the supplied + * {@link DiscoverySelectorIdentifier} has a prefix that matches the value + * returned by {@link #getPrefix()}. + * + * @param identifier the {@link DiscoverySelectorIdentifier} to parse + * @param context the {@link Context} to use for parsing + * @return an {@link Optional} containing the parsed {@link DiscoverySelector}; never {@code null} + */ + Optional parse(DiscoverySelectorIdentifier identifier, Context context); + + /** + * Context for parsing {@link DiscoverySelectorIdentifier DiscoverySelectorIdentifiers}. + */ + interface Context { + + /** + * Parse the supplied selector. + *

+ * This method is intended to be used by implementations of + * {@link DiscoverySelectorIdentifierParser#parse} for selectors that + * contain other selectors. + */ + Optional parse(String selector); + + } + +} diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectorIdentifierParsers.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectorIdentifierParsers.java new file mode 100644 index 000000000000..2f0d79b46735 --- /dev/null +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectorIdentifierParsers.java @@ -0,0 +1,85 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.engine.discovery; + +import static java.util.Collections.unmodifiableMap; +import static java.util.Objects.requireNonNull; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.ServiceLoader; +import java.util.stream.Stream; + +import org.junit.platform.commons.util.ClassLoaderUtils; +import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; + +/** + * Utility class for parsing {@link DiscoverySelectorIdentifier + * DiscoverySelectorIdentifiers}. + * + * @since 1.11 + */ +class DiscoverySelectorIdentifierParsers { + + static Stream parseAll(String... identifiers) { + Preconditions.notNull(identifiers, "identifiers must not be null"); + return Stream.of(identifiers) // + .map(DiscoverySelectorIdentifierParsers::parse) // + .filter(Optional::isPresent) // + .map(Optional::get); + } + + static Stream parseAll(Collection identifiers) { + Preconditions.notNull(identifiers, "identifiers must not be null"); + return identifiers.stream() // + .map(DiscoverySelectorIdentifierParsers::parse) // + .filter(Optional::isPresent) // + .map(Optional::get); + } + + static Optional parse(String identifier) { + Preconditions.notNull(identifier, "identifier must not be null"); + return parse(DiscoverySelectorIdentifier.parse(identifier)); + } + + static Optional parse(DiscoverySelectorIdentifier identifier) { + Preconditions.notNull(identifier, "identifier must not be null"); + DiscoverySelectorIdentifierParser parser = Singleton.INSTANCE.parsersByPrefix.get(identifier.getPrefix()); + Preconditions.notNull(parser, "No parser for prefix: " + identifier.getPrefix()); + + return parser.parse(identifier, DiscoverySelectorIdentifierParsers::parse); + } + + private enum Singleton { + + INSTANCE; + + private final Map parsersByPrefix; + + Singleton() { + Map parsersByPrefix = new HashMap<>(); + Iterable loadedParsers = ServiceLoader.load( + DiscoverySelectorIdentifierParser.class, ClassLoaderUtils.getDefaultClassLoader()); + for (DiscoverySelectorIdentifierParser parser : loadedParsers) { + DiscoverySelectorIdentifierParser previous = parsersByPrefix.put(parser.getPrefix(), parser); + Preconditions.condition(previous == null, + () -> String.format("Duplicate parser for prefix: [%s] candidate a: [%s] candidate b: [%s] ", + parser.getPrefix(), requireNonNull(previous).getClass().getName(), + parser.getClass().getName())); + } + this.parsersByPrefix = unmodifiableMap(parsersByPrefix); + } + } +} diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java index 3439d20d45e3..5bdbb25fd8d5 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -21,14 +21,18 @@ import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.Set; +import java.util.stream.Stream; import org.apiguardian.api.API; import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.util.Preconditions; import org.junit.platform.commons.util.ReflectionUtils; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; import org.junit.platform.engine.UniqueId; /** @@ -48,6 +52,7 @@ * @see NestedClassSelector * @see NestedMethodSelector * @see UniqueIdSelector + * @see DiscoverySelectorIdentifier */ @API(status = STABLE, since = "1.0") public final class DiscoverySelectors { @@ -389,7 +394,7 @@ public static ClassSelector selectClass(Class clazz) { * @see ClassSelector */ public static ClassSelector selectClass(String className) { - return selectClass((ClassLoader) null, className); + return selectClass(null, className); } /** @@ -524,7 +529,7 @@ public static MethodSelector selectMethod(ClassLoader classLoader, String classN * @see MethodSelector */ public static MethodSelector selectMethod(String className, String methodName, String parameterTypeNames) { - return selectMethod((ClassLoader) null, className, methodName, parameterTypeNames); + return selectMethod(null, className, methodName, parameterTypeNames); } /** @@ -608,7 +613,7 @@ public static MethodSelector selectMethod(String className, String methodName, C Preconditions.notBlank(methodName, "Method name must not be null or blank"); Preconditions.notNull(parameterTypes, "Parameter types array must not be null"); Preconditions.containsNoNullElements(parameterTypes, "Parameter types array must not contain null elements"); - return new MethodSelector((ClassLoader) null, className, methodName, parameterTypes); + return new MethodSelector(null, className, methodName, parameterTypes); } /** @@ -673,7 +678,7 @@ public static NestedClassSelector selectNestedClass(List> enclosingClas */ @API(status = STABLE, since = "1.6") public static NestedClassSelector selectNestedClass(List enclosingClassNames, String nestedClassName) { - return selectNestedClass((ClassLoader) null, enclosingClassNames, nestedClassName); + return selectNestedClass(null, enclosingClassNames, nestedClassName); } /** @@ -707,7 +712,7 @@ public static NestedClassSelector selectNestedClass(ClassLoader classLoader, Lis @API(status = STABLE, since = "1.6") public static NestedMethodSelector selectNestedMethod(List enclosingClassNames, String nestedClassName, String methodName) { - return selectNestedMethod((ClassLoader) null, enclosingClassNames, nestedClassName, methodName); + return selectNestedMethod(null, enclosingClassNames, nestedClassName, methodName); } /** @@ -752,8 +757,7 @@ public static NestedMethodSelector selectNestedMethod(ClassLoader classLoader, L @API(status = STABLE, since = "1.6") public static NestedMethodSelector selectNestedMethod(List enclosingClassNames, String nestedClassName, String methodName, String parameterTypeNames) { - return selectNestedMethod((ClassLoader) null, enclosingClassNames, nestedClassName, methodName, - parameterTypeNames); + return selectNestedMethod(null, enclosingClassNames, nestedClassName, methodName, parameterTypeNames); } /** @@ -807,8 +811,7 @@ public static NestedMethodSelector selectNestedMethod(List enclosingClas Preconditions.notBlank(methodName, "Method name must not be null or blank"); Preconditions.notNull(parameterTypes, "Parameter types array must not be null"); Preconditions.containsNoNullElements(parameterTypes, "Parameter types array must not contain null elements"); - return new NestedMethodSelector((ClassLoader) null, enclosingClassNames, nestedClassName, methodName, - parameterTypes); + return new NestedMethodSelector(null, enclosingClassNames, nestedClassName, methodName, parameterTypes); } /** @@ -939,4 +942,58 @@ public static IterationSelector selectIteration(DiscoverySelector parentSelector return new IterationSelector(parentSelector, iterationIndices); } + /** + * Parse the supplied string representation of a + * {@link DiscoverySelectorIdentifier}. + * + * @param identifier the string representation of a {@code DiscoverySelectorIdentifier}; + * never {@code null} or blank + * @since 1.11 + * @see DiscoverySelectorIdentifierParser + */ + @API(status = EXPERIMENTAL, since = "1.11") + public static Optional parse(String identifier) { + return DiscoverySelectorIdentifierParsers.parse(identifier); + } + + /** + * Parse the supplied {@link DiscoverySelectorIdentifier}. + * + * @param identifier the {@code DiscoverySelectorIdentifier} to parse; + * never {@code null} + * @since 1.11 + * @see DiscoverySelectorIdentifierParser + */ + @API(status = EXPERIMENTAL, since = "1.11") + public static Optional parse(DiscoverySelectorIdentifier identifier) { + return DiscoverySelectorIdentifierParsers.parse(identifier); + } + + /** + * Parse the supplied string representations of + * {@link DiscoverySelectorIdentifier DiscoverySelectorIdentifiers}. + * + * @param identifiers the string representations of + * {@code DiscoverySelectorIdentifiers} to parse; never {@code null} + * @since 1.11 + * @see DiscoverySelectorIdentifierParser + */ + @API(status = EXPERIMENTAL, since = "1.11") + public static Stream parseAll(String... identifiers) { + return DiscoverySelectorIdentifierParsers.parseAll(identifiers); + } + + /** + * Parse the supplied {@link DiscoverySelectorIdentifier + * DiscoverySelectorIdentifiers}. + * + * @param identifiers the {@code DiscoverySelectorIdentifiers} to parse; + * never {@code null} + * @since 1.11 + * @see DiscoverySelectorIdentifierParser + */ + @API(status = EXPERIMENTAL, since = "1.11") + public static Stream parseAll(Collection identifiers) { + return DiscoverySelectorIdentifierParsers.parseAll(identifiers); + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ExcludeClassNameFilter.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ExcludeClassNameFilter.java index 8a3aa725af21..9ba34a78d35d 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ExcludeClassNameFilter.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ExcludeClassNameFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ExcludePackageNameFilter.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ExcludePackageNameFilter.java index 37fb11ca864d..430234ea38da 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ExcludePackageNameFilter.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ExcludePackageNameFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/FilePosition.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/FilePosition.java index 4245187ae651..41a125472cb4 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/FilePosition.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/FilePosition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -153,6 +153,14 @@ public Optional getColumn() { return Optional.ofNullable(this.column); } + String toQueryPart() { + StringBuilder builder = new StringBuilder("line=").append(this.line); + if (this.column != null) { + builder.append("&column=").append(this.column); + } + return builder.toString(); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/FileSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/FileSelector.java index 78bf501c6d8d..6246fa58e4c3 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/FileSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/FileSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,7 @@ package org.junit.platform.engine.discovery; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import java.io.File; @@ -21,8 +22,10 @@ import java.util.Optional; import org.apiguardian.api.API; +import org.junit.platform.commons.util.StringUtils; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects a file so that @@ -117,4 +120,43 @@ public String toString() { return new ToStringBuilder(this).append("path", this.path).append("position", this.position).toString(); } + @Override + public Optional toIdentifier() { + if (this.position == null) { + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.path)); + } + else { + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, + String.format("%s?%s", this.path, this.position.toQueryPart()))); + } + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for {@link FileSelector + * FileSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + private static final String PREFIX = "file"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + return Optional.of(StringUtils.splitIntoTwo('?', identifier.getValue()).map( // + DiscoverySelectors::selectFile, // + (path, query) -> { + FilePosition position = FilePosition.fromQuery(query).orElse(null); + return DiscoverySelectors.selectFile(path, position); + } // + )); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IncludeClassNameFilter.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IncludeClassNameFilter.java index 5cd49e1413e0..4d69e22a9de3 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IncludeClassNameFilter.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IncludeClassNameFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IncludePackageNameFilter.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IncludePackageNameFilter.java index 2ceb30f4a142..54e554fb3586 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IncludePackageNameFilter.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IncludePackageNameFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IterationSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IterationSelector.java index fe9ff20acfdf..965a86cdb379 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IterationSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IterationSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,18 +11,29 @@ package org.junit.platform.engine.discovery; import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toCollection; import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.INTERNAL; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.SortedSet; import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.IntStream; import org.apiguardian.api.API; +import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.commons.util.StringUtils; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects the iterations of a parent @@ -90,4 +101,89 @@ public String toString() { .toString(); // @formatter:on } + + @Override + public Optional toIdentifier() { + return parentSelector.toIdentifier().map(parentSelectorString -> DiscoverySelectorIdentifier.create( // + IdentifierParser.PREFIX, // + String.format("%s[%s]", parentSelectorString, formatIterationIndicesAsRanges())) // + ); + } + + private String formatIterationIndicesAsRanges() { + class Range { + final int start; + int end; + + Range(int start) { + this.start = start; + this.end = start; + } + } + List ranges = new ArrayList<>(); + Range current = new Range(iterationIndices.first()); + ranges.add(current); + for (int n : iterationIndices.tailSet(current.start + 1)) { + if (n == current.end + 1) { + current.end = n; + } + else { + current = new Range(n); + ranges.add(current); + } + } + return ranges.stream() // + .map(r -> { + if (r.start == r.end) { + return String.valueOf(r.start); + } + if (r.start == r.end - 1) { + return r.start + "," + r.end; + } + return r.start + ".." + r.end; + }) // + .collect(joining(",")); + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for + * {@link IterationSelector IterationSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + public static final String PREFIX = "iteration"; + + private static final Pattern PATTERN = Pattern.compile( + "(?.+)\\[(?(\\d+)(\\.\\.\\d+)?(\\s*,\\s*(\\d+)(\\.\\.\\d+)?)*)]"); + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + Matcher matcher = PATTERN.matcher(identifier.getValue()); + Preconditions.condition(matcher.matches(), "Invalid format: must be IDENTIFIER[INDEX(,INDEX)*]"); + return context.parse(matcher.group("parentIdentifier")) // + .map(parent -> { + int[] iterationIndices = Arrays.stream(matcher.group("indices").split(",")) // + .flatMapToInt(this::parseIndexDefinition) // + .toArray(); + return DiscoverySelectors.selectIteration(parent, iterationIndices); + }); + } + + private IntStream parseIndexDefinition(String value) { + return StringUtils.splitIntoTwo("..", value).map( // + index -> IntStream.of(Integer.parseInt(index)), // + (firstIndex, lastIndex) -> IntStream.rangeClosed(Integer.parseInt(firstIndex), + Integer.parseInt(lastIndex))// + ); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/MethodSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/MethodSelector.java index c1055f68b743..4a58eb805a8f 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/MethodSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/MethodSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,10 +12,12 @@ import static org.apiguardian.api.API.Status.DEPRECATED; import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import java.lang.reflect.Method; import java.util.Objects; +import java.util.Optional; import org.apiguardian.api.API; import org.junit.platform.commons.JUnitException; @@ -25,6 +27,7 @@ import org.junit.platform.commons.util.ReflectionUtils; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects a {@link Method} or a combination of @@ -308,4 +311,34 @@ public String toString() { // @formatter:on } + @Override + public Optional toIdentifier() { + String fullyQualifiedMethodName = ReflectionUtils.getFullyQualifiedMethodName(this.className, this.methodName, + this.parameterTypeNames); + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, fullyQualifiedMethodName)); + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for {@link MethodSelector + * MethodSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + private static final String PREFIX = "method"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + return Optional.of(DiscoverySelectors.selectMethod(identifier.getValue())); + } + + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java index 68825eb0d84b..293654370f8e 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,13 +10,16 @@ package org.junit.platform.engine.discovery; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import java.util.Objects; +import java.util.Optional; import org.apiguardian.api.API; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects a module name so that @@ -73,4 +76,31 @@ public String toString() { return new ToStringBuilder(this).append("moduleName", this.moduleName).toString(); } + @Override + public Optional toIdentifier() { + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.moduleName)); + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for {@link ModuleSelector + * ModuleSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + private static final String PREFIX = "module"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + return Optional.of(DiscoverySelectors.selectModule(identifier.getValue())); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/NestedClassSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/NestedClassSelector.java index 154f74179d1c..cfa1dd6e2500 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/NestedClassSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/NestedClassSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,18 +10,24 @@ package org.junit.platform.engine.discovery; +import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import static org.junit.platform.commons.util.CollectionUtils.toUnmodifiableList; +import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; import org.apiguardian.api.API; import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects a nested {@link Class} @@ -143,4 +149,36 @@ public String toString() { .toString(); } + @Override + public Optional toIdentifier() { + String allClassNames = Stream.concat(enclosingClassSelectors.stream(), Stream.of(nestedClassSelector)) // + .map(ClassSelector::getClassName) // + .collect(joining("/")); + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, allClassNames)); + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for + * {@link NestedClassSelector NestedClassSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + static final String PREFIX = "nested-class"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + List parts = Arrays.asList(identifier.getValue().split("/")); + return Optional.of( + DiscoverySelectors.selectNestedClass(parts.subList(0, parts.size() - 1), parts.get(parts.size() - 1))); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/NestedMethodSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/NestedMethodSelector.java index 22e1ffdef440..ae5d4a69fdc0 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/NestedMethodSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/NestedMethodSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,16 +12,21 @@ import static org.apiguardian.api.API.Status.DEPRECATED; import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.Optional; import org.apiguardian.api.API; import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.commons.util.ReflectionUtils; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects a nested {@link Method} @@ -239,4 +244,45 @@ public String toString() { .toString(); } + @Override + public Optional toIdentifier() { + return nestedClassSelector.toIdentifier() // + .map(parent -> { + String fullyQualifiedMethodName = ReflectionUtils.getFullyQualifiedMethodName(parent.getValue(), + methodSelector.getMethodName(), methodSelector.getParameterTypeNames()); + return DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, fullyQualifiedMethodName); + }); + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for + * {@link NestedMethodSelector NestedMethodSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + private static final String PREFIX = "nested-method"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + List parts = Arrays.asList(identifier.getValue().split("/")); + List enclosingClassNames = parts.subList(0, parts.size() - 1); + + String[] methodParts = ReflectionUtils.parseFullyQualifiedMethodName(parts.get(parts.size() - 1)); + String nestedClassName = methodParts[0]; + String methodName = methodParts[1]; + String parameterTypeNames = methodParts[2]; + + return Optional.of(DiscoverySelectors.selectNestedMethod(enclosingClassNames, nestedClassName, methodName, + parameterTypeNames)); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/PackageNameFilter.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/PackageNameFilter.java index 55a808efa828..082cf82a6d11 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/PackageNameFilter.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/PackageNameFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/PackageSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/PackageSelector.java index 781fc2af0906..fc7da6fb5c6f 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/PackageSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/PackageSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,13 +10,16 @@ package org.junit.platform.engine.discovery; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import java.util.Objects; +import java.util.Optional; import org.apiguardian.api.API; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects a package name so that @@ -73,4 +76,31 @@ public String toString() { return new ToStringBuilder(this).append("packageName", this.packageName).toString(); } + @Override + public Optional toIdentifier() { + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.packageName)); + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for {@link PackageSelector + * PackageSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + private static final String PREFIX = "package"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + return Optional.of(DiscoverySelectors.selectPackage(identifier.getValue())); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/UniqueIdSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/UniqueIdSelector.java index 4fee909e7746..08068b1d9ca6 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/UniqueIdSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/UniqueIdSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,13 +10,16 @@ package org.junit.platform.engine.discovery; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import java.util.Objects; +import java.util.Optional; import org.apiguardian.api.API; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; import org.junit.platform.engine.UniqueId; /** @@ -74,4 +77,31 @@ public String toString() { return new ToStringBuilder(this).append("uniqueId", this.uniqueId).toString(); } + @Override + public Optional toIdentifier() { + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.uniqueId.toString())); + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for + * {@link UniqueIdSelector UniqueIdSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + private static final String PREFIX = "uid"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + return Optional.of(DiscoverySelectors.selectUniqueId(identifier.getValue())); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/UriSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/UriSelector.java index c673851d11e0..9e6ed0c45c8a 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/UriSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/UriSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,14 +10,17 @@ package org.junit.platform.engine.discovery; +import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; import java.net.URI; import java.util.Objects; +import java.util.Optional; import org.apiguardian.api.API; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * A {@link DiscoverySelector} that selects a {@link URI} so that @@ -77,4 +80,31 @@ public String toString() { return new ToStringBuilder(this).append("uri", this.uri).toString(); } + @Override + public Optional toIdentifier() { + return Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.uri.toString())); + } + + /** + * The {@link DiscoverySelectorIdentifierParser} for {@link UriSelector + * UriSelectors}. + */ + @API(status = INTERNAL, since = "1.11") + public static class IdentifierParser implements DiscoverySelectorIdentifierParser { + + private static final String PREFIX = "uri"; + + public IdentifierParser() { + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Optional parse(DiscoverySelectorIdentifier identifier, Context context) { + return Optional.of(DiscoverySelectors.selectUri(identifier.getValue())); + } + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/reporting/ReportEntry.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/reporting/ReportEntry.java index 6e114191d110..740df190eaaa 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/reporting/ReportEntry.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/reporting/ReportEntry.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/config/PrefixedConfigurationParameters.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/config/PrefixedConfigurationParameters.java index f0c8789aace3..53603e8598fe 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/config/PrefixedConfigurationParameters.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/config/PrefixedConfigurationParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptor.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptor.java index dd0a53e8378a..668456e49f32 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptor.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClassSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClassSource.java index 89f33addb0fd..b47cc78cc2a9 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClassSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClassSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSource.java index 0a288d3257b4..595dd33cd307 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/CompositeTestSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/CompositeTestSource.java index 3fa3ccc9e6f3..adfdb8669730 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/CompositeTestSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/CompositeTestSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/DefaultUriSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/DefaultUriSource.java index dd7a602a3d49..91a77ee6afa8 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/DefaultUriSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/DefaultUriSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/DirectorySource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/DirectorySource.java index c64c2dba18a3..5c719d17dea0 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/DirectorySource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/DirectorySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/EngineDescriptor.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/EngineDescriptor.java index fbf57519c458..7fef1aae35b5 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/EngineDescriptor.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/EngineDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FilePosition.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FilePosition.java index a0fb2c943453..ddff0d98e898 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FilePosition.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FilePosition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FileSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FileSource.java index 7e4ab6c9d989..d7b4b74d8bab 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FileSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FileSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FileSystemSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FileSystemSource.java index 2a4c976d04bc..5a338cde508e 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FileSystemSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FileSystemSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/MethodSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/MethodSource.java index 977bd4f8fbf8..79d2574b99f1 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/MethodSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/MethodSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/PackageSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/PackageSource.java index b19dc3b3d9a3..ab78956614af 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/PackageSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/PackageSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ResourceUtils.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ResourceUtils.java index ae9cfdd2f35d..b61598d59b8e 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ResourceUtils.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ResourceUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/UriSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/UriSource.java index fb6829e5c232..bd97bbe609e1 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/UriSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/UriSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ClassContainerSelectorResolver.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ClassContainerSelectorResolver.java index 886743da69ae..c8e9b1843476 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ClassContainerSelectorResolver.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ClassContainerSelectorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolution.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolution.java index 81c618f965c4..c1a6169293e6 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolution.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolution.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolver.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolver.java index 99314c76259d..df5611990a78 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolver.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/SelectorResolver.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/SelectorResolver.java index 3864db3cfe40..4685fb9b48c9 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/SelectorResolver.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/SelectorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/filter/ClasspathScanningSupport.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/filter/ClasspathScanningSupport.java index 6699df7eddb6..e3d2b0392cb2 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/filter/ClasspathScanningSupport.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/filter/ClasspathScanningSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/CompositeLock.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/CompositeLock.java index 41b08d5fa3ae..aff3111df90b 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/CompositeLock.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/CompositeLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,25 +10,41 @@ package org.junit.platform.engine.support.hierarchical; +import static java.util.Collections.unmodifiableList; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.locks.Lock; +import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.commons.util.ToStringBuilder; + /** * @since 1.3 */ class CompositeLock implements ResourceLock { + private final List resources; private final List locks; + private final boolean exclusive; + + CompositeLock(List resources, List locks) { + Preconditions.condition(resources.size() == locks.size(), "Resources and locks must have the same size"); + this.resources = unmodifiableList(resources); + this.locks = Preconditions.notEmpty(locks, "Locks must not be empty"); + this.exclusive = resources.stream().anyMatch( + resource -> resource.getLockMode() == ExclusiveResource.LockMode.READ_WRITE); + } - CompositeLock(List locks) { - this.locks = locks; + @Override + public List getResources() { + return resources; } // for tests only List getLocks() { - return locks; + return this.locks; } @Override @@ -38,9 +54,9 @@ public ResourceLock acquire() throws InterruptedException { } private void acquireAllLocks() throws InterruptedException { - List acquiredLocks = new ArrayList<>(locks.size()); + List acquiredLocks = new ArrayList<>(this.locks.size()); try { - for (Lock lock : locks) { + for (Lock lock : this.locks) { lock.lockInterruptibly(); acquiredLocks.add(lock); } @@ -53,7 +69,7 @@ private void acquireAllLocks() throws InterruptedException { @Override public void release() { - release(locks); + release(this.locks); } private void release(List acquiredLocks) { @@ -62,20 +78,34 @@ private void release(List acquiredLocks) { } } + @Override + public boolean isExclusive() { + return exclusive; + } + + @Override + public String toString() { + return new ToStringBuilder(this) // + .append("resources", resources) // + .toString(); + } + private class CompositeLockManagedBlocker implements ForkJoinPool.ManagedBlocker { - private boolean acquired; + private volatile boolean acquired; @Override public boolean block() throws InterruptedException { - acquireAllLocks(); - acquired = true; + if (!this.acquired) { + acquireAllLocks(); + this.acquired = true; + } return true; } @Override public boolean isReleasable() { - return acquired; + return this.acquired; } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfiguration.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfiguration.java index f00735a7b69d..cb91d2a84639 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfiguration.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfigurationStrategy.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfigurationStrategy.java index a37d9ea35c1c..fc5ac9e257fe 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfigurationStrategy.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfigurationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/EngineExecutionContext.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/EngineExecutionContext.java index 4a3748d96e53..bfc78c472970 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/EngineExecutionContext.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/EngineExecutionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ExclusiveResource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ExclusiveResource.java index 500d69ffb7fb..621fe2ed211b 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ExclusiveResource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ExclusiveResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,8 +10,11 @@ package org.junit.platform.engine.support.hierarchical; +import static java.util.Comparator.comparing; +import static java.util.Comparator.naturalOrder; import static org.apiguardian.api.API.Status.STABLE; +import java.util.Comparator; import java.util.Objects; import java.util.concurrent.locks.ReadWriteLock; @@ -50,6 +53,14 @@ public class ExclusiveResource { static final ExclusiveResource GLOBAL_READ = new ExclusiveResource(GLOBAL_KEY, LockMode.READ); static final ExclusiveResource GLOBAL_READ_WRITE = new ExclusiveResource(GLOBAL_KEY, LockMode.READ_WRITE); + static final Comparator COMPARATOR // + = comparing(ExclusiveResource::getKey, globalKeyFirst().thenComparing(naturalOrder())) // + .thenComparing(ExclusiveResource::getLockMode); + + private static Comparator globalKeyFirst() { + return comparing(key -> !GLOBAL_KEY.equals(key)); + } + private final String key; private final LockMode lockMode; private int hash; diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorService.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorService.java index 7f3fc2214c08..fc0aae08f68c 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorService.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorService.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,10 +12,14 @@ import static java.util.concurrent.CompletableFuture.completedFuture; import static org.apiguardian.api.API.Status.STABLE; +import static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE; import static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.CONCURRENT; +import static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.SAME_THREAD; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Constructor; +import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.Deque; import java.util.LinkedList; import java.util.List; @@ -26,7 +30,6 @@ import java.util.concurrent.ForkJoinTask; import java.util.concurrent.ForkJoinWorkerThread; import java.util.concurrent.Future; -import java.util.concurrent.RecursiveAction; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.Predicate; @@ -50,8 +53,12 @@ @API(status = STABLE, since = "1.10") public class ForkJoinPoolHierarchicalTestExecutorService implements HierarchicalTestExecutorService { - private final ForkJoinPool forkJoinPool; + // package-private for testing + final ForkJoinPool forkJoinPool; + + private final TaskEventListener taskEventListener; private final int parallelism; + private final ThreadLocal threadLocks = ThreadLocal.withInitial(ThreadLock::new); /** * Create a new {@code ForkJoinPoolHierarchicalTestExecutorService} based on @@ -71,7 +78,13 @@ public ForkJoinPoolHierarchicalTestExecutorService(ConfigurationParameters confi */ @API(status = STABLE, since = "1.10") public ForkJoinPoolHierarchicalTestExecutorService(ParallelExecutionConfiguration configuration) { + this(configuration, TaskEventListener.NOOP); + } + + ForkJoinPoolHierarchicalTestExecutorService(ParallelExecutionConfiguration configuration, + TaskEventListener taskEventListener) { forkJoinPool = createForkJoinPool(configuration); + this.taskEventListener = taskEventListener; parallelism = forkJoinPool.getParallelism(); LoggerFactory.getLogger(getClass()).config(() -> "Using ForkJoinPool with parallelism of " + parallelism); } @@ -94,9 +107,9 @@ private ForkJoinPool createForkJoinPool(ParallelExecutionConfiguration configura } private static Optional> sinceJava9Constructor() { - return Try.call(() -> ForkJoinPool.class.getDeclaredConstructor(Integer.TYPE, ForkJoinWorkerThreadFactory.class, - UncaughtExceptionHandler.class, Boolean.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE, Predicate.class, - Long.TYPE, TimeUnit.class)) // + return Try.call(() -> ForkJoinPool.class.getDeclaredConstructor(int.class, ForkJoinWorkerThreadFactory.class, + UncaughtExceptionHandler.class, boolean.class, int.class, int.class, int.class, Predicate.class, long.class, + TimeUnit.class)) // .toOptional(); } @@ -132,7 +145,7 @@ public Future submit(TestTask testTask) { if (testTask.getExecutionMode() == CONCURRENT && ForkJoinTask.getSurplusQueuedTaskCount() < parallelism) { return exclusiveTask.fork(); } - exclusiveTask.compute(); + exclusiveTask.execSync(); return completedFuture(null); } @@ -143,33 +156,42 @@ private boolean isAlreadyRunningInForkJoinPool() { @Override public void invokeAll(List tasks) { if (tasks.size() == 1) { - new ExclusiveTask(tasks.get(0)).compute(); + new ExclusiveTask(tasks.get(0)).execSync(); return; } - Deque nonConcurrentTasks = new LinkedList<>(); + Deque isolatedTasks = new LinkedList<>(); + Deque sameThreadTasks = new LinkedList<>(); Deque concurrentTasksInReverseOrder = new LinkedList<>(); - forkConcurrentTasks(tasks, nonConcurrentTasks, concurrentTasksInReverseOrder); - executeNonConcurrentTasks(nonConcurrentTasks); + forkConcurrentTasks(tasks, isolatedTasks, sameThreadTasks, concurrentTasksInReverseOrder); + executeSync(sameThreadTasks); joinConcurrentTasksInReverseOrderToEnableWorkStealing(concurrentTasksInReverseOrder); + executeSync(isolatedTasks); } - private void forkConcurrentTasks(List tasks, Deque nonConcurrentTasks, - Deque concurrentTasksInReverseOrder) { + private void forkConcurrentTasks(List tasks, Deque isolatedTasks, + Deque sameThreadTasks, Deque concurrentTasksInReverseOrder) { for (TestTask testTask : tasks) { ExclusiveTask exclusiveTask = new ExclusiveTask(testTask); - if (testTask.getExecutionMode() == CONCURRENT) { - exclusiveTask.fork(); - concurrentTasksInReverseOrder.addFirst(exclusiveTask); + if (requiresGlobalReadWriteLock(testTask)) { + isolatedTasks.add(exclusiveTask); + } + else if (testTask.getExecutionMode() == SAME_THREAD) { + sameThreadTasks.add(exclusiveTask); } else { - nonConcurrentTasks.add(exclusiveTask); + exclusiveTask.fork(); + concurrentTasksInReverseOrder.addFirst(exclusiveTask); } } } - private void executeNonConcurrentTasks(Deque nonConcurrentTasks) { - for (ExclusiveTask task : nonConcurrentTasks) { - task.compute(); + private static boolean requiresGlobalReadWriteLock(TestTask testTask) { + return testTask.getResourceLock().getResources().contains(GLOBAL_READ_WRITE); + } + + private void executeSync(Deque tasks) { + for (ExclusiveTask task : tasks) { + task.execSync(); } } @@ -177,7 +199,18 @@ private void joinConcurrentTasksInReverseOrderToEnableWorkStealing( Deque concurrentTasksInReverseOrder) { for (ExclusiveTask forkedTask : concurrentTasksInReverseOrder) { forkedTask.join(); + resubmitDeferredTasks(); + } + } + + private void resubmitDeferredTasks() { + List deferredTasks = threadLocks.get().deferredTasks; + for (ExclusiveTask deferredTask : deferredTasks) { + if (!deferredTask.isDone()) { + deferredTask.fork(); + } } + deferredTasks.clear(); } @Override @@ -186,8 +219,8 @@ public void close() { } // this class cannot not be serialized because TestTask is not Serializable - @SuppressWarnings("serial") - static class ExclusiveTask extends RecursiveAction { + @SuppressWarnings({ "serial", "RedundantSuppression" }) + class ExclusiveTask extends ForkJoinTask { private final TestTask testTask; @@ -195,17 +228,61 @@ static class ExclusiveTask extends RecursiveAction { this.testTask = testTask; } + /** + * Always returns {@code null}. + * + * @return {@code null} always + */ + public final Void getRawResult() { + return null; + } + + /** + * Requires null completion value. + */ + protected final void setRawResult(Void mustBeNull) { + } + + void execSync() { + boolean completed = exec(); + if (!completed) { + throw new IllegalStateException( + "Task was deferred but should have been executed synchronously: " + testTask); + } + } + @SuppressWarnings("try") @Override - public void compute() { - try (ResourceLock lock = testTask.getResourceLock().acquire()) { + public boolean exec() { + // Check if this task is compatible with the current resource lock, if there is any. + // If not, we put this task in the thread local as a deferred task + // and let the worker thread fork it once it is done with the current task. + ResourceLock resourceLock = testTask.getResourceLock(); + ThreadLock threadLock = threadLocks.get(); + if (!threadLock.areAllHeldLocksCompatibleWith(resourceLock)) { + threadLock.addDeferredTask(this); + taskEventListener.deferred(testTask); + // Return false to indicate that this task is not done yet + // this means that .join() will wait. + return false; + } + try ( // + ResourceLock lock = resourceLock.acquire(); // + @SuppressWarnings("unused") + ThreadLock.NestedResourceLock nested = threadLock.withNesting(lock) // + ) { testTask.execute(); + return true; } catch (InterruptedException e) { - ExceptionUtils.throwAsUncheckedException(e); + throw ExceptionUtils.throwAsUncheckedException(e); } } + @Override + public String toString() { + return "ExclusiveTask [" + testTask + "]"; + } } static class WorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { @@ -228,4 +305,35 @@ static class WorkerThread extends ForkJoinWorkerThread { } + static class ThreadLock { + private final Deque locks = new ArrayDeque<>(2); + private final List deferredTasks = new ArrayList<>(); + + void addDeferredTask(ExclusiveTask task) { + deferredTasks.add(task); + } + + NestedResourceLock withNesting(ResourceLock lock) { + locks.push(lock); + return locks::pop; + } + + boolean areAllHeldLocksCompatibleWith(ResourceLock lock) { + return locks.stream().allMatch(l -> l.isCompatible(lock)); + } + + interface NestedResourceLock extends AutoCloseable { + @Override + void close(); + } + } + + interface TaskEventListener { + + TaskEventListener NOOP = __ -> { + }; + + void deferred(TestTask testTask); + } + } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestEngine.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestEngine.java index 2bb665a6983a..c80aa81aeed3 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestEngine.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestEngine.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutor.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutor.java index e1535dc116e7..9f5da42f82a3 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutor.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutorService.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutorService.java index 59fa82d2a21b..e69b5dc30330 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutorService.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutorService.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/LockManager.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/LockManager.java index 1b5b37b024ea..8e055cab7f36 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/LockManager.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/LockManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,16 +10,17 @@ package org.junit.platform.engine.support.hierarchical; -import static java.util.Comparator.comparing; -import static java.util.Comparator.naturalOrder; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toList; import static org.junit.platform.commons.util.CollectionUtils.getOnlyElement; -import static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_KEY; +import static org.junit.platform.commons.util.CollectionUtils.toUnmodifiableList; +import static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ; +import static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE; import static org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode.READ; import java.util.Collection; -import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -33,56 +34,70 @@ */ class LockManager { - private static final Comparator COMPARATOR // - = comparing(ExclusiveResource::getKey, globalKeyFirst().thenComparing(naturalOrder())) // - .thenComparing(ExclusiveResource::getLockMode); + private final Map locksByKey = new ConcurrentHashMap<>(); + private final SingleLock globalReadLock; + private final SingleLock globalReadWriteLock; - private static Comparator globalKeyFirst() { - return comparing(key -> !GLOBAL_KEY.equals(key)); + public LockManager() { + globalReadLock = new SingleLock(GLOBAL_READ, toLock(GLOBAL_READ)); + globalReadWriteLock = new SingleLock(GLOBAL_READ_WRITE, toLock(GLOBAL_READ_WRITE)); } - private final Map locksByKey = new ConcurrentHashMap<>(); - ResourceLock getLockForResources(Collection resources) { - if (resources.size() == 1) { - return getLockForResource(getOnlyElement(resources)); - } - List locks = getDistinctSortedLocks(resources); - return toResourceLock(locks); + return toResourceLock(toDistinctSortedResources(resources)); } ResourceLock getLockForResource(ExclusiveResource resource) { - return new SingleLock(toLock(resource)); + return toResourceLock(singletonList(resource)); } - private List getDistinctSortedLocks(Collection resources) { + private List toDistinctSortedResources(Collection resources) { + if (resources.isEmpty()) { + return emptyList(); + } + if (resources.size() == 1) { + return singletonList(getOnlyElement(resources)); + } // @formatter:off Map> resourcesByKey = resources.stream() - .sorted(COMPARATOR) + .sorted(ExclusiveResource.COMPARATOR) .distinct() .collect(groupingBy(ExclusiveResource::getKey, LinkedHashMap::new, toList())); return resourcesByKey.values().stream() .map(resourcesWithSameKey -> resourcesWithSameKey.get(0)) - .map(this::toLock) - .collect(toList()); + .collect(toUnmodifiableList()); // @formatter:on } - private Lock toLock(ExclusiveResource resource) { - ReadWriteLock lock = this.locksByKey.computeIfAbsent(resource.getKey(), key -> new ReentrantReadWriteLock()); - return resource.getLockMode() == READ ? lock.readLock() : lock.writeLock(); - } - - private ResourceLock toResourceLock(List locks) { - switch (locks.size()) { + private ResourceLock toResourceLock(List resources) { + switch (resources.size()) { case 0: return NopLock.INSTANCE; case 1: - return new SingleLock(locks.get(0)); + return toSingleLock(getOnlyElement(resources)); default: - return new CompositeLock(locks); + return new CompositeLock(resources, toLocks(resources)); + } + } + + private SingleLock toSingleLock(ExclusiveResource resource) { + if (GLOBAL_READ.equals(resource)) { + return globalReadLock; + } + if (GLOBAL_READ_WRITE.equals(resource)) { + return globalReadWriteLock; } + return new SingleLock(resource, toLock(resource)); + } + + private List toLocks(List resources) { + return resources.stream().map(this::toLock).collect(toUnmodifiableList()); + } + + private Lock toLock(ExclusiveResource resource) { + ReadWriteLock lock = this.locksByKey.computeIfAbsent(resource.getKey(), key -> new ReentrantReadWriteLock()); + return resource.getLockMode() == READ ? lock.readLock() : lock.writeLock(); } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/Node.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/Node.java index d826340a582f..1726ae2e1965 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/Node.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/Node.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeExecutionAdvisor.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeExecutionAdvisor.java index 0a86036135f3..eb95daab0237 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeExecutionAdvisor.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeExecutionAdvisor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -33,6 +33,10 @@ void useResourceLock(TestDescriptor testDescriptor, ResourceLock resourceLock) { resourceLocksByTestDescriptor.put(testDescriptor, resourceLock); } + void removeResourceLock(TestDescriptor testDescriptor) { + resourceLocksByTestDescriptor.remove(testDescriptor); + } + Optional getForcedExecutionMode(TestDescriptor testDescriptor) { return testDescriptor.getParent().flatMap(this::lookupExecutionModeForcedByAncestor); } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTestTask.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTestTask.java index 34df1e0c0acc..8e602b990ba9 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTestTask.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTestTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -79,6 +79,11 @@ public ExecutionMode getExecutionMode() { return taskContext.getExecutionAdvisor().getForcedExecutionMode(testDescriptor).orElse(node.getExecutionMode()); } + @Override + public String toString() { + return "NodeTestTask [" + testDescriptor + "]"; + } + void setParentContext(C parentContext) { this.parentContext = parentContext; } @@ -239,7 +244,7 @@ public void awaitFinished() throws InterruptedException { // Futures returned by execute() may have been cancelled } catch (ExecutionException e) { - ExceptionUtils.throwAsUncheckedException(e.getCause()); + throw ExceptionUtils.throwAsUncheckedException(e.getCause()); } } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTestTaskContext.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTestTaskContext.java index 35a34af81313..6b1e6dfd8852 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTestTaskContext.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTestTaskContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTreeWalker.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTreeWalker.java index 2bb59d5099f9..ada030923b76 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTreeWalker.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTreeWalker.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -50,9 +50,17 @@ NodeExecutionAdvisor walk(TestDescriptor rootDescriptor) { private void walk(TestDescriptor globalLockDescriptor, TestDescriptor testDescriptor, NodeExecutionAdvisor advisor) { + + if (advisor.getResourceLock(globalLockDescriptor) == globalReadWriteLock) { + // Global read-write lock is already being enforced, so no additional locks are needed + return; + } + Set exclusiveResources = getExclusiveResources(testDescriptor); if (exclusiveResources.isEmpty()) { - advisor.useResourceLock(testDescriptor, globalReadLock); + if (globalLockDescriptor.equals(testDescriptor)) { + advisor.useResourceLock(globalLockDescriptor, globalReadLock); + } testDescriptor.getChildren().forEach(child -> walk(globalLockDescriptor, child, advisor)); } else { @@ -70,14 +78,24 @@ private void walk(TestDescriptor globalLockDescriptor, TestDescriptor testDescri advisor.forceDescendantExecutionMode(child, SAME_THREAD); }); } - if (!globalLockDescriptor.equals(testDescriptor) && allResources.contains(GLOBAL_READ_WRITE)) { - forceDescendantExecutionModeRecursively(advisor, globalLockDescriptor); + if (allResources.contains(GLOBAL_READ_WRITE)) { + advisor.forceDescendantExecutionMode(globalLockDescriptor, SAME_THREAD); + doForChildrenRecursively(globalLockDescriptor, child -> { + advisor.forceDescendantExecutionMode(child, SAME_THREAD); + // Remove any locks that may have been set for siblings or their descendants + advisor.removeResourceLock(child); + }); advisor.useResourceLock(globalLockDescriptor, globalReadWriteLock); } - if (globalLockDescriptor.equals(testDescriptor) && !allResources.contains(GLOBAL_READ_WRITE)) { - allResources.add(GLOBAL_READ); + else { + if (globalLockDescriptor.equals(testDescriptor)) { + allResources.add(GLOBAL_READ); + } + else { + allResources.remove(GLOBAL_READ); + } + advisor.useResourceLock(testDescriptor, lockManager.getLockForResources(allResources)); } - advisor.useResourceLock(testDescriptor, lockManager.getLockForResources(allResources)); } } @@ -87,8 +105,7 @@ private void forceDescendantExecutionModeRecursively(NodeExecutionAdvisor adviso } private boolean isReadOnly(Set exclusiveResources) { - return exclusiveResources.stream().allMatch( - exclusiveResource -> exclusiveResource.getLockMode() == ExclusiveResource.LockMode.READ); + return exclusiveResources.stream().allMatch(it -> it.getLockMode() == ExclusiveResource.LockMode.READ); } private Set getExclusiveResources(TestDescriptor testDescriptor) { diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeUtils.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeUtils.java index b76ba240ddb5..b2807378a05d 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeUtils.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NopLock.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NopLock.java index df6f64fc3707..7cdd79592d53 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NopLock.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NopLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,12 @@ package org.junit.platform.engine.support.hierarchical; +import static java.util.Collections.emptyList; + +import java.util.List; + +import org.junit.platform.commons.util.ToStringBuilder; + /** * No-op {@link ResourceLock} implementation. * @@ -22,6 +28,11 @@ class NopLock implements ResourceLock { private NopLock() { } + @Override + public List getResources() { + return emptyList(); + } + @Override public ResourceLock acquire() { return this; @@ -32,4 +43,13 @@ public void release() { // nothing to do } + @Override + public boolean isExclusive() { + return false; + } + + @Override + public String toString() { + return new ToStringBuilder(this).toString(); + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/OpenTest4JAwareThrowableCollector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/OpenTest4JAwareThrowableCollector.java index 53b9e2168d70..52f8e2f0ea1e 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/OpenTest4JAwareThrowableCollector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/OpenTest4JAwareThrowableCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfiguration.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfiguration.java index 444d544014d8..adde417cbdd3 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfiguration.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,6 @@ package org.junit.platform.engine.support.hierarchical; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; import java.util.concurrent.ForkJoinPool; @@ -69,7 +68,7 @@ public interface ParallelExecutionConfiguration { * @see ForkJoinPool#ForkJoinPool(int, ForkJoinPool.ForkJoinWorkerThreadFactory, Thread.UncaughtExceptionHandler, * boolean, int, int, int, Predicate, long, TimeUnit) */ - @API(status = EXPERIMENTAL, since = "1.9") + @API(status = STABLE, since = "1.11") default Predicate getSaturatePredicate() { return null; } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfigurationStrategy.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfigurationStrategy.java index dd2b9be17ccd..549ce71c694b 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfigurationStrategy.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfigurationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ResourceLock.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ResourceLock.java index 0024478e6afa..7e10c3ba7941 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ResourceLock.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ResourceLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,6 +12,9 @@ import static org.apiguardian.api.API.Status.STABLE; +import java.util.List; +import java.util.Optional; + import org.apiguardian.api.API; /** @@ -43,4 +46,50 @@ default void close() { release(); } + /** + * {@return the exclusive resources this lock represents} + */ + List getResources(); + + /** + * {@return whether this lock requires exclusiveness} + */ + boolean isExclusive(); + + /** + * {@return whether the given lock is compatible with this lock} + * @param other the other lock to check for compatibility + */ + default boolean isCompatible(ResourceLock other) { + + List ownResources = this.getResources(); + List otherResources = other.getResources(); + + if (ownResources.isEmpty() || otherResources.isEmpty()) { + return true; + } + + // Whenever there's a READ_WRITE lock, it's incompatible with any other lock + // because we guarantee that all children will have exclusive access to the + // resource in question. In practice, whenever a READ_WRITE lock is present, + // NodeTreeWalker will force all children to run in the same thread so that + // it should never attempt to steal work from another thread, and we shouldn't + // actually reach this point. + // The global read lock (which is always on direct children of the engine node) + // needs special treatment so that it is compatible with the first write lock + // (which may be on a test method). + boolean isGlobalReadLock = ownResources.size() == 1 + && ExclusiveResource.GLOBAL_READ.equals(ownResources.get(0)); + if ((!isGlobalReadLock && other.isExclusive()) || this.isExclusive()) { + return false; + } + + Optional potentiallyDeadlockCausingAdditionalResource = otherResources.stream() // + .filter(resource -> !ownResources.contains(resource)) // + .findFirst() // + .filter(resource -> ExclusiveResource.COMPARATOR.compare(resource, + ownResources.get(ownResources.size() - 1)) < 0); + + return !(potentiallyDeadlockCausingAdditionalResource.isPresent()); + } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SameThreadHierarchicalTestExecutorService.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SameThreadHierarchicalTestExecutorService.java index bb2d4280ce3b..f18652d5d4bc 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SameThreadHierarchicalTestExecutorService.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SameThreadHierarchicalTestExecutorService.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleLock.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleLock.java index 5d104e8d9284..cfa9d2f3acf0 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleLock.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,23 +10,36 @@ package org.junit.platform.engine.support.hierarchical; +import static java.util.Collections.singletonList; +import static org.junit.platform.commons.util.CollectionUtils.getOnlyElement; + +import java.util.List; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.locks.Lock; +import org.junit.platform.commons.util.ToStringBuilder; + /** * @since 1.3 */ class SingleLock implements ResourceLock { + private final List resources; private final Lock lock; - SingleLock(Lock lock) { + SingleLock(ExclusiveResource resource, Lock lock) { + this.resources = singletonList(resource); this.lock = lock; } + @Override + public List getResources() { + return resources; + } + // for tests only Lock getLock() { - return lock; + return this.lock; } @Override @@ -37,25 +50,38 @@ public ResourceLock acquire() throws InterruptedException { @Override public void release() { - lock.unlock(); + this.lock.unlock(); + } + + @Override + public boolean isExclusive() { + return resources.get(0).getLockMode() == ExclusiveResource.LockMode.READ_WRITE; + } + + @Override + public String toString() { + return new ToStringBuilder(this) // + .append("resource", getOnlyElement(resources)) // + .toString(); } private class SingleLockManagedBlocker implements ForkJoinPool.ManagedBlocker { - private boolean acquired; + private volatile boolean acquired; @Override public boolean block() throws InterruptedException { - lock.lockInterruptibly(); - acquired = true; + if (!this.acquired) { + SingleLock.this.lock.lockInterruptibly(); + this.acquired = true; + } return true; } @Override public boolean isReleasable() { - return acquired || lock.tryLock(); + return this.acquired || (this.acquired = SingleLock.this.lock.tryLock()); } } - } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleTestExecutor.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleTestExecutor.java index 0496a61e4ded..7765d4b5ed92 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleTestExecutor.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleTestExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ThrowableCollector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ThrowableCollector.java index 5a1e9d56d470..e8187134671a 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ThrowableCollector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ThrowableCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -158,7 +158,7 @@ public boolean isNotEmpty() { */ public void assertEmpty() { if (!isEmpty()) { - ExceptionUtils.throwAsUncheckedException(this.throwable); + throw ExceptionUtils.throwAsUncheckedException(this.throwable); } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.java index 536c029b10a0..79836ac6f501 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -37,19 +37,25 @@ * {@link #NamespacedHierarchicalStore(NamespacedHierarchicalStore, CloseAction)} * constructor. * - *

This class is thread-safe. + *

This class is thread-safe. Please note, however, that thread safety is + * not guaranteed while the {@link #close()} method is being invoked. * * @param Namespace type - * @since 5.10 + * @since 1.10 */ -@API(status = EXPERIMENTAL, since = "5.10") +@API(status = EXPERIMENTAL, since = "1.10") public final class NamespacedHierarchicalStore implements AutoCloseable { private final AtomicInteger insertOrderSequence = new AtomicInteger(); + private final ConcurrentMap, StoredValue> storedValues = new ConcurrentHashMap<>(4); + private final NamespacedHierarchicalStore parentStore; + private final CloseAction closeAction; + private volatile boolean closed = false; + /** * Create a new store with the supplied parent. * @@ -72,7 +78,7 @@ public NamespacedHierarchicalStore(NamespacedHierarchicalStore parentStore, C } /** - * Create a child store with this store as its parent using the same close + * Create a child store with this store as its parent and this store's close * action. */ public NamespacedHierarchicalStore newChild() { @@ -80,23 +86,46 @@ public NamespacedHierarchicalStore newChild() { } /** - * If a close action is configured, it will be called with all successfully + * Determine if this store has been {@linkplain #close() closed}. + * + * @return {@code true} if this store has been closed + * @since 1.11 + * @see #close() + */ + @API(status = EXPERIMENTAL, since = "1.11") + public boolean isClosed() { + return this.closed; + } + + /** + * If a {@link CloseAction} is configured, it will be called with all successfully * stored values in reverse insertion order. * *

Closing a store does not close its parent or any of its children. + * + *

Invocations of this method after the store has already been closed will + * be ignored. + * + * @see #isClosed() */ @Override public void close() { - if (this.closeAction == null) { - return; + if (!this.closed) { + try { + if (this.closeAction != null) { + ThrowableCollector throwableCollector = new ThrowableCollector(__ -> false); + this.storedValues.entrySet().stream() // + .map(e -> e.getValue().evaluateSafely(e.getKey())) // + .filter(it -> it != null && it.value != null) // + .sorted(EvaluatedValue.REVERSE_INSERT_ORDER) // + .forEach(it -> throwableCollector.execute(() -> it.close(this.closeAction))); + throwableCollector.assertEmpty(); + } + } + finally { + this.closed = true; + } } - ThrowableCollector throwableCollector = new ThrowableCollector(__ -> false); - this.storedValues.entrySet().stream() // - .map(e -> e.getValue().evaluateSafely(e.getKey())) // - .filter(it -> it != null && it.value != null) // - .sorted(EvaluatedValue.REVERSE_INSERT_ORDER) // - .forEach(it -> throwableCollector.execute(() -> it.close(this.closeAction))); - throwableCollector.assertEmpty(); } /** @@ -106,6 +135,8 @@ public void close() { * @param namespace the namespace; never {@code null} * @param key the key; never {@code null} * @return the stored value; may be {@code null} + * @throws NamespacedHierarchicalStoreException if this store has already been + * closed */ public Object get(N namespace, Object key) { StoredValue storedValue = getStoredValue(new CompositeKey<>(namespace, key)); @@ -121,7 +152,7 @@ public Object get(N namespace, Object key) { * @param requiredType the required type of the value; never {@code null} * @return the stored value; may be {@code null} * @throws NamespacedHierarchicalStoreException if the stored value cannot - * be cast to the required type + * be cast to the required type, or if this store has already been closed */ public T get(N namespace, Object key, Class requiredType) throws NamespacedHierarchicalStoreException { Object value = get(namespace, key); @@ -137,6 +168,8 @@ public T get(N namespace, Object key, Class requiredType) throws Namespac * @param defaultCreator the function called with the supplied {@code key} * to create a new value; never {@code null} but may return {@code null} * @return the stored value; may be {@code null} + * @throws NamespacedHierarchicalStoreException if this store has already been + * closed */ public Object getOrComputeIfAbsent(N namespace, K key, Function defaultCreator) { Preconditions.notNull(defaultCreator, "defaultCreator must not be null"); @@ -144,7 +177,10 @@ public Object getOrComputeIfAbsent(N namespace, K key, Function def StoredValue storedValue = getStoredValue(compositeKey); if (storedValue == null) { storedValue = this.storedValues.computeIfAbsent(compositeKey, - __ -> storedValue(new MemoizingSupplier(() -> defaultCreator.apply(key)))); + __ -> storedValue(new MemoizingSupplier(() -> { + rejectIfClosed(); + return defaultCreator.apply(key); + }))); } return storedValue.evaluate(); } @@ -161,10 +197,11 @@ public Object getOrComputeIfAbsent(N namespace, K key, Function def * @param requiredType the required type of the value; never {@code null} * @return the stored value; may be {@code null} * @throws NamespacedHierarchicalStoreException if the stored value cannot - * be cast to the required type + * be cast to the required type, or if this store has already been closed */ public V getOrComputeIfAbsent(N namespace, K key, Function defaultCreator, Class requiredType) throws NamespacedHierarchicalStoreException { + Object value = getOrComputeIfAbsent(namespace, key, defaultCreator); return castToRequiredType(key, value, requiredType); } @@ -180,10 +217,11 @@ public V getOrComputeIfAbsent(N namespace, K key, Function defaultC * @param key the key; never {@code null} * @param value the value to store; may be {@code null} * @return the previously stored value; may be {@code null} - * @throws NamespacedHierarchicalStoreException if the stored value cannot - * be cast to the required type + * @throws NamespacedHierarchicalStoreException if an error occurs while + * storing the value, or if this store has already been closed */ public Object put(N namespace, Object key, Object value) throws NamespacedHierarchicalStoreException { + rejectIfClosed(); StoredValue oldValue = this.storedValues.put(new CompositeKey<>(namespace, key), storedValue(() -> value)); return StoredValue.evaluateIfNotNull(oldValue); } @@ -198,8 +236,11 @@ public Object put(N namespace, Object key, Object value) throws NamespacedHierar * @param namespace the namespace; never {@code null} * @param key the key; never {@code null} * @return the previously stored value; may be {@code null} + * @throws NamespacedHierarchicalStoreException if this store has already been + * closed */ public Object remove(N namespace, Object key) { + rejectIfClosed(); StoredValue previous = this.storedValues.remove(new CompositeKey<>(namespace, key)); return StoredValue.evaluateIfNotNull(previous); } @@ -216,9 +257,10 @@ public Object remove(N namespace, Object key) { * @param requiredType the required type of the value; never {@code null} * @return the previously stored value; may be {@code null} * @throws NamespacedHierarchicalStoreException if the stored value cannot - * be cast to the required type + * be cast to the required type, or if this store has already been closed */ public T remove(N namespace, Object key, Class requiredType) throws NamespacedHierarchicalStoreException { + rejectIfClosed(); Object value = remove(namespace, key); return castToRequiredType(key, value, requiredType); } @@ -252,7 +294,15 @@ private T castToRequiredType(Object key, Object value, Class requiredType } // else throw new NamespacedHierarchicalStoreException( - String.format("Object stored under key [%s] is not of required type [%s]", key, requiredType.getName())); + String.format("Object stored under key [%s] is not of required type [%s], but was [%s]: %s", key, + requiredType.getName(), value.getClass().getName(), value)); + } + + private void rejectIfClosed() { + if (this.closed) { + throw new NamespacedHierarchicalStoreException( + "A NamespacedHierarchicalStore cannot be modified or queried after it has been closed"); + } } private static class CompositeKey { @@ -296,7 +346,7 @@ private static class StoredValue { private EvaluatedValue evaluateSafely(CompositeKey compositeKey) { try { - return new EvaluatedValue<>(compositeKey, order, evaluate()); + return new EvaluatedValue<>(compositeKey, this.order, evaluate()); } catch (Throwable t) { UnrecoverableExceptions.rethrowIfUnrecoverable(t); @@ -305,7 +355,7 @@ private EvaluatedValue evaluateSafely(CompositeKey compositeKey) { } private Object evaluate() { - return supplier.get(); + return this.supplier.get(); } static Object evaluateIfNotNull(StoredValue value) { @@ -361,7 +411,7 @@ public Object get() { computeValue(); } if (this.value instanceof Failure) { - ExceptionUtils.throwAsUncheckedException(((Failure) this.value).throwable); + throw ExceptionUtils.throwAsUncheckedException(((Failure) this.value).throwable); } return this.value; } @@ -392,7 +442,7 @@ public Failure(Throwable throwable) { /** * Called for each successfully stored non-null value in the store when a * {@link NamespacedHierarchicalStore} is - * {@link NamespacedHierarchicalStore#close() closed}. + * {@linkplain NamespacedHierarchicalStore#close() closed}. */ @FunctionalInterface public interface CloseAction { diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStoreException.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStoreException.java index 276c890e7c9b..bf9dfefa1d16 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStoreException.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStoreException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -18,9 +18,9 @@ /** * Exception thrown by failed {@link NamespacedHierarchicalStore} operations. * - * @since 5.10 + * @since 1.10 */ -@API(status = EXPERIMENTAL, since = "5.10") +@API(status = EXPERIMENTAL, since = "1.10") public class NamespacedHierarchicalStoreException extends JUnitException { private static final long serialVersionUID = 1L; @@ -29,8 +29,8 @@ public NamespacedHierarchicalStoreException(String message) { super(message); } - @SuppressWarnings("unused") public NamespacedHierarchicalStoreException(String message, Throwable cause) { super(message, cause); } + } diff --git a/junit-platform-engine/src/main/resources/META-INF/services/org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser b/junit-platform-engine/src/main/resources/META-INF/services/org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser new file mode 100644 index 000000000000..f8aa801d77fd --- /dev/null +++ b/junit-platform-engine/src/main/resources/META-INF/services/org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser @@ -0,0 +1,13 @@ +org.junit.platform.engine.discovery.ClasspathResourceSelector$IdentifierParser +org.junit.platform.engine.discovery.ClasspathRootSelector$IdentifierParser +org.junit.platform.engine.discovery.ClassSelector$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 diff --git a/junit-platform-engine/src/module/org.junit.platform.engine/module-info.java b/junit-platform-engine/src/module/org.junit.platform.engine/module-info.java index 46c2069448c2..baabf4794d85 100644 --- a/junit-platform-engine/src/module/org.junit.platform.engine/module-info.java +++ b/junit-platform-engine/src/module/org.junit.platform.engine/module-info.java @@ -31,4 +31,22 @@ exports org.junit.platform.engine.support.filter; exports org.junit.platform.engine.support.hierarchical; exports org.junit.platform.engine.support.store; + + uses org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser; + + provides 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; + } diff --git a/junit-platform-engine/src/test/README.md b/junit-platform-engine/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-engine/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoEngineExecutionContext.java b/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoEngineExecutionContext.java index 8d3c74a71b8b..e6cf952390a2 100644 --- a/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoEngineExecutionContext.java +++ b/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoEngineExecutionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalContainerDescriptor.java b/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalContainerDescriptor.java index 09c0b622c601..0361e8c9dcb9 100644 --- a/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalContainerDescriptor.java +++ b/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalContainerDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalEngineDescriptor.java b/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalEngineDescriptor.java index 1d14b7710182..8da648cc3249 100644 --- a/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalEngineDescriptor.java +++ b/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalEngineDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalTestDescriptor.java b/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalTestDescriptor.java index 130aba4ad691..aaaf64b540de 100644 --- a/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalTestDescriptor.java +++ b/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalTestEngine.java b/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalTestEngine.java index 97f23ce1ed8d..61656c6b476e 100644 --- a/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalTestEngine.java +++ b/junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalTestEngine.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestDescriptorStub.java b/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestDescriptorStub.java index 49907eece1e0..1336811dfac7 100644 --- a/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestDescriptorStub.java +++ b/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestDescriptorStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestEngineSpy.java b/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestEngineSpy.java index cdf6c136345a..0c1dc37aa026 100644 --- a/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestEngineSpy.java +++ b/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestEngineSpy.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestEngineStub.java b/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestEngineStub.java index 3ef7186545df..d97cb8307fd2 100644 --- a/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestEngineStub.java +++ b/junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestEngineStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-jfr/junit-platform-jfr.gradle.kts b/junit-platform-jfr/junit-platform-jfr.gradle.kts index f2555cd59166..0f03e2b99f86 100644 --- a/junit-platform-jfr/junit-platform-jfr.gradle.kts +++ b/junit-platform-jfr/junit-platform-jfr.gradle.kts @@ -10,6 +10,12 @@ dependencies { compileOnlyApi(libs.apiguardian) + if (java.toolchain.implementation.orNull == JvmImplementation.J9) { + compileOnly(libs.jfrPolyfill) { + because("OpenJ9 does not include JFR") + } + } + osgiVerification(projects.junitJupiterEngine) osgiVerification(projects.junitPlatformLauncher) } diff --git a/junit-platform-jfr/src/main/java/org/junit/platform/jfr/FlightRecordingDiscoveryListener.java b/junit-platform-jfr/src/main/java/org/junit/platform/jfr/FlightRecordingDiscoveryListener.java index d506a562f320..100942573680 100644 --- a/junit-platform-jfr/src/main/java/org/junit/platform/jfr/FlightRecordingDiscoveryListener.java +++ b/junit-platform-jfr/src/main/java/org/junit/platform/jfr/FlightRecordingDiscoveryListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,7 @@ package org.junit.platform.jfr; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.STABLE; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -36,7 +36,7 @@ * @since 1.8 * @see JEP 328: Flight Recorder */ -@API(status = EXPERIMENTAL, since = "1.8") +@API(status = STABLE, since = "1.11") public class FlightRecordingDiscoveryListener implements LauncherDiscoveryListener { private final AtomicReference launcherDiscoveryEvent = new AtomicReference<>(); diff --git a/junit-platform-jfr/src/main/java/org/junit/platform/jfr/FlightRecordingExecutionListener.java b/junit-platform-jfr/src/main/java/org/junit/platform/jfr/FlightRecordingExecutionListener.java index be66660299cc..cef4753f01b2 100644 --- a/junit-platform-jfr/src/main/java/org/junit/platform/jfr/FlightRecordingExecutionListener.java +++ b/junit-platform-jfr/src/main/java/org/junit/platform/jfr/FlightRecordingExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,7 @@ package org.junit.platform.jfr; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.STABLE; import java.util.Map; import java.util.Optional; @@ -38,7 +38,7 @@ * @since 1.8 * @see JEP 328: Flight Recorder */ -@API(status = EXPERIMENTAL, since = "1.8") +@API(status = STABLE, since = "1.11") public class FlightRecordingExecutionListener implements TestExecutionListener { private final AtomicReference testPlanExecutionEvent = new AtomicReference<>(); diff --git a/junit-platform-jfr/src/main/java/org/junit/platform/jfr/UniqueId.java b/junit-platform-jfr/src/main/java/org/junit/platform/jfr/UniqueId.java index 653226e69ede..9083cef2e7b2 100644 --- a/junit-platform-jfr/src/main/java/org/junit/platform/jfr/UniqueId.java +++ b/junit-platform-jfr/src/main/java/org/junit/platform/jfr/UniqueId.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-jfr/src/test/README.md b/junit-platform-jfr/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-jfr/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/EngineDiscoveryResult.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/EngineDiscoveryResult.java index 5dad3bc37982..d12ed056f1b3 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/EngineDiscoveryResult.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/EngineDiscoveryResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/EngineFilter.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/EngineFilter.java index 6013abb28732..a65a68f265db 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/EngineFilter.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/EngineFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/Launcher.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/Launcher.java index 1abb128b32dc..41e5f0960576 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/Launcher.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/Launcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java index 7e85b35d1c53..5cb30bf8e69d 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -92,8 +92,9 @@ public class LauncherConstants { public static final String STDERR_REPORT_ENTRY_KEY = "stderr"; /** - * Property name used to provide patterns for deactivating listeners registered - * via the {@link java.util.ServiceLoader ServiceLoader} mechanism: {@value} + * Property name used to provide patterns for deactivating + * {@linkplain TestExecutionListener listeners} registered via the + * {@link java.util.ServiceLoader ServiceLoader} mechanism: {@value} * *

Pattern Matching Syntax

* @@ -121,6 +122,17 @@ public class LauncherConstants { * {@code org.example.TheirListener}. * * + *

Only listeners registered via the {@code ServiceLoader} mechanism can + * be deactivated. In other words, any listener registered explicitly via the + * {@link LauncherDiscoveryRequest} cannot be deactivated via this + * configuration parameter. + * + *

In addition, since execution listeners are registered before the test + * run starts, this configuration parameter can only be supplied as a JVM + * system property or via the JUnit Platform configuration file but cannot + * be supplied in the {@link LauncherDiscoveryRequest}} that is passed to + * the {@link Launcher}. + * * @see #DEACTIVATE_ALL_LISTENERS_PATTERN * @see org.junit.platform.launcher.TestExecutionListener */ @@ -143,6 +155,11 @@ public class LauncherConstants { * *

By default, interceptor registration is disabled. * + *

Since interceptors are registered before the test run starts, this + * configuration parameter can only be supplied as a JVM system property or + * via the JUnit Platform configuration file but cannot be supplied in the + * {@link LauncherDiscoveryRequest}} that is passed to the {@link Launcher}. + * * @see LauncherInterceptor */ @API(status = EXPERIMENTAL, since = "1.10") diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherDiscoveryListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherDiscoveryListener.java index 9b97f4b1ce3a..25c8751b6ef2 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherDiscoveryListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherDiscoveryListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,6 @@ package org.junit.platform.launcher; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; import org.apiguardian.api.API; @@ -39,7 +38,7 @@ * @see LauncherDiscoveryRequest#getDiscoveryListener() * @see org.junit.platform.launcher.core.LauncherConfig.Builder#addLauncherDiscoveryListeners */ -@API(status = EXPERIMENTAL, since = "1.6") +@API(status = STABLE, since = "1.11") public interface LauncherDiscoveryListener extends EngineDiscoveryListener { /** diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherDiscoveryRequest.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherDiscoveryRequest.java index 0eadbbfe6a9f..f6043705a82c 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherDiscoveryRequest.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherDiscoveryRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherInterceptor.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherInterceptor.java index 262a1d539809..8375db13beed 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherInterceptor.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherSession.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherSession.java index 0d93fb15d116..92fb5e9f37ae 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherSession.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherSessionListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherSessionListener.java index 9a89def0f531..1024b695c188 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherSessionListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherSessionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/PostDiscoveryFilter.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/PostDiscoveryFilter.java index 7c31d55c5a00..c6a6911f478a 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/PostDiscoveryFilter.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/PostDiscoveryFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TagFilter.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TagFilter.java index 7389aa53584d..7b3579af26a3 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TagFilter.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TagFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestExecutionListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestExecutionListener.java index 548395c43cfa..9179705f3661 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestExecutionListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,6 +13,7 @@ import static org.apiguardian.api.API.Status.STABLE; import org.apiguardian.api.API; +import org.junit.platform.commons.util.UnrecoverableExceptions; import org.junit.platform.engine.TestExecutionResult; import org.junit.platform.engine.TestExecutionResult.Status; import org.junit.platform.engine.reporting.ReportEntry; @@ -30,6 +31,12 @@ * events are called in reverse order. Test case execution won't start before * all {@link #executionStarted(TestIdentifier)} calls have returned. * + *

If an exception is thrown by an implementation of a method of this + * interface, the exception will be caught and logged unless it is deemed + * {@linkplain UnrecoverableExceptions unrecoverable}. In consequence, a + * {@code TestExecutionListener} cannot cause test execution to fail or abort it + * early by throwing an exception. + * *

JUnit provides two example implementations. * *

    diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestIdentifier.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestIdentifier.java index 20eaf35624d1..ae3cd416469e 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestIdentifier.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestIdentifier.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -284,7 +284,8 @@ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOEx source = serializedForm.source; tags = serializedForm.tags; type = serializedForm.type; - parentId = UniqueId.parse(serializedForm.parentId); + String parentId = serializedForm.parentId; + this.parentId = parentId == null ? null : UniqueId.parse(parentId); legacyReportingName = serializedForm.legacyReportingName; } @@ -307,7 +308,8 @@ private static class SerializedForm implements Serializable { SerializedForm(TestIdentifier testIdentifier) { this.uniqueId = testIdentifier.uniqueId.toString(); - this.parentId = testIdentifier.parentId.toString(); + UniqueId parentId = testIdentifier.parentId; + this.parentId = parentId == null ? null : parentId.toString(); this.displayName = testIdentifier.displayName; this.legacyReportingName = testIdentifier.legacyReportingName; this.source = testIdentifier.source; diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestPlan.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestPlan.java index a31ab12ecf04..c83f11124913 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestPlan.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestPlan.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/CompositeEngineExecutionListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/CompositeEngineExecutionListener.java index 29311c9b0773..b68e59bee23c 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/CompositeEngineExecutionListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/CompositeEngineExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/CompositeTestExecutionListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/CompositeTestExecutionListener.java index 977bc4a30739..9107865d8872 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/CompositeTestExecutionListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/CompositeTestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultDiscoveryRequest.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultDiscoveryRequest.java index 422f9592374e..3ea9c295384a 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultDiscoveryRequest.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultDiscoveryRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncher.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncher.java index 9f21595d0d64..b6473d86a86a 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncher.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -39,7 +39,7 @@ class DefaultLauncher implements Launcher { private final LauncherListenerRegistry listenerRegistry = new LauncherListenerRegistry(); private final EngineExecutionOrchestrator executionOrchestrator = new EngineExecutionOrchestrator( - listenerRegistry.testExecutionListeners);; + listenerRegistry.testExecutionListeners); private final EngineDiscoveryOrchestrator discoveryOrchestrator; /** diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherConfig.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherConfig.java index 500cdde224ff..6a88425416b0 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherConfig.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherSession.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherSession.java index b3743ac253a7..10965c631eb9 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherSession.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingEngineExecutionListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingEngineExecutionListener.java index e2f410533602..18a95fabdb3b 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingEngineExecutionListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingEngineExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingLauncher.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingLauncher.java index d4332b52fe35..fa0706b5dbef 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingLauncher.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryErrorDescriptor.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryErrorDescriptor.java index 50a4df10b05c..8a67aaae4888 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryErrorDescriptor.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryErrorDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryOrchestrator.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryOrchestrator.java index 4bb7f3193149..09cb6231b9a7 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryOrchestrator.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryOrchestrator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryResultValidator.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryResultValidator.java index 797656f719af..460e8d271825 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryResultValidator.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryResultValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineExecutionOrchestrator.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineExecutionOrchestrator.java index bf0f78a7bde0..c000c09ce6cc 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineExecutionOrchestrator.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineExecutionOrchestrator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineFilterer.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineFilterer.java index 04f0d24fcabe..830855532d87 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineFilterer.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineFilterer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineIdValidator.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineIdValidator.java index abeeb298acaa..f6305283b1b9 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineIdValidator.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineIdValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -16,6 +16,7 @@ import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.logging.Logger; import org.junit.platform.commons.logging.LoggerFactory; +import org.junit.platform.commons.util.Preconditions; import org.junit.platform.engine.TestEngine; /** @@ -29,7 +30,7 @@ private EngineIdValidator() { static Iterable validate(Iterable testEngines) { Set ids = new HashSet<>(); for (TestEngine testEngine : testEngines) { - // check usage of reserved id prefix + // check usage of reserved ID prefix if (!validateReservedIds(testEngine)) { getLogger().warn(() -> String.format( "Third-party TestEngine implementations are forbidden to use the reserved 'junit-' prefix for their ID: '%s'", @@ -52,23 +53,27 @@ private static Logger getLogger() { // https://github.com/junit-team/junit5/issues/1557 private static boolean validateReservedIds(TestEngine testEngine) { - String engineId = testEngine.getId(); + String engineId = Preconditions.notBlank(testEngine.getId(), + () -> String.format("ID for TestEngine [%s] must not be null or blank", testEngine.getClass().getName())); if (!engineId.startsWith("junit-")) { return true; } - if (engineId.equals("junit-jupiter")) { - validateWellKnownClassName(testEngine, "org.junit.jupiter.engine.JupiterTestEngine"); - return true; - } - if (engineId.equals("junit-vintage")) { - validateWellKnownClassName(testEngine, "org.junit.vintage.engine.VintageTestEngine"); - return true; - } - if (engineId.equals("junit-platform-suite")) { - validateWellKnownClassName(testEngine, "org.junit.platform.suite.engine.SuiteTestEngine"); - return true; + switch (engineId) { + case "junit-jupiter": { + validateWellKnownClassName(testEngine, "org.junit.jupiter.engine.JupiterTestEngine"); + return true; + } + case "junit-vintage": { + validateWellKnownClassName(testEngine, "org.junit.vintage.engine.VintageTestEngine"); + return true; + } + case "junit-platform-suite": { + validateWellKnownClassName(testEngine, "org.junit.platform.suite.engine.SuiteTestEngine"); + return true; + } + default: + return false; } - return false; } private static void validateWellKnownClassName(TestEngine testEngine, String expectedClassName) { @@ -80,4 +85,5 @@ private static void validateWellKnownClassName(TestEngine testEngine, String exp String.format("Third-party TestEngine '%s' is forbidden to use the reserved '%s' TestEngine ID.", actualClassName, testEngine.getId())); } + } diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ExecutionListenerAdapter.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ExecutionListenerAdapter.java index f2d87e4bfea8..54e3b7c2ae38 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ExecutionListenerAdapter.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ExecutionListenerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/InterceptingLauncher.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/InterceptingLauncher.java index 043d4e9688f5..56e9f427071a 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/InterceptingLauncher.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/InterceptingLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/InternalTestPlan.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/InternalTestPlan.java index 20a1557195fe..9195eaef2e97 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/InternalTestPlan.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/InternalTestPlan.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/IterationOrder.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/IterationOrder.java index 40308c574c22..3d1dbfcea19e 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/IterationOrder.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/IterationOrder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherConfig.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherConfig.java index b9dcd3775a7c..8efe4de2323b 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherConfig.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherConfigurationParameters.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherConfigurationParameters.java index 5d6b95edd9ee..1fa5f9a462bc 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherConfigurationParameters.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherConfigurationParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,8 @@ package org.junit.platform.launcher.core; +import static java.util.stream.Collectors.joining; + import java.io.InputStream; import java.net.URL; import java.net.URLConnection; @@ -25,10 +27,12 @@ import java.util.Properties; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.platform.commons.logging.Logger; import org.junit.platform.commons.logging.LoggerFactory; import org.junit.platform.commons.util.ClassLoaderUtils; +import org.junit.platform.commons.util.CollectionUtils; import org.junit.platform.commons.util.Preconditions; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.ConfigurationParameters; @@ -93,6 +97,7 @@ public String toString() { static final class Builder { private final Map explicitParameters = new HashMap<>(); + private final List configResources = new ArrayList<>(); private boolean implicitProvidersEnabled = true; private String configFileName = ConfigurationParameters.CONFIG_FILE_NAME; private ConfigurationParameters parentConfigurationParameters; @@ -106,6 +111,12 @@ Builder explicitParameters(Map parameters) { return this; } + Builder configurationResources(List configResources) { + Preconditions.notNull(configResources, "configResources must not be null"); + this.configResources.addAll(configResources); + return this; + } + Builder enableImplicitProviders(boolean enabled) { this.implicitProvidersEnabled = enabled; return this; @@ -129,6 +140,9 @@ LauncherConfigurationParameters build() { parameterProviders.add(ParameterProvider.explicit(explicitParameters)); } + CollectionUtils.forEachInReverseOrder(configResources, + configResource -> parameterProviders.add(ParameterProvider.propertiesFile(configResource))); + if (parentConfigurationParameters != null) { parameterProviders.add(ParameterProvider.inherited(parentConfigurationParameters)); } @@ -261,13 +275,21 @@ private static Properties loadClasspathResource(String configFileName) { Set resources = new LinkedHashSet<>(Collections.list(classLoader.getResources(configFileName))); if (!resources.isEmpty()) { + + URL configFileUrl = CollectionUtils.getFirstElement(resources).get(); + if (resources.size() > 1) { - logger.warn(() -> String.format( - "Discovered %d '%s' configuration files in the classpath; only the first will be used.", - resources.size(), configFileName)); + logger.warn(() -> { + String formattedResourceList = Stream.concat( // + Stream.of(configFileUrl + " (*)"), // + resources.stream().skip(1).map(URL::toString) // + ).collect(joining("\n- ", "\n- ", "")); + return String.format( + "Discovered %d '%s' configuration files on the classpath (see below); only the first (*) will be used.%s", + resources.size(), configFileName, formattedResourceList); + }); } - URL configFileUrl = resources.iterator().next(); // same as List#get(0) logger.config(() -> String.format( "Loading JUnit Platform configuration parameters from classpath resource [%s].", configFileUrl)); URLConnection urlConnection = configFileUrl.openConnection(); diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.java index 1d2f22d7a559..cb480d2533e3 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -58,7 +59,7 @@ * selectMethod("org.example.order.OrderTests", "test4"), * selectMethod(OrderTests.class, "test5"), * selectMethod(OrderTests.class, testMethod), - * selectClasspathRoots(Collections.singleton(new File("/my/local/path1"))), + * selectClasspathRoots(Collections.singleton(Paths.get("/my/local/path1"))), * selectUniqueId("unique-id-1"), * selectUniqueId("unique-id-2") * ) @@ -102,6 +103,7 @@ public final class LauncherDiscoveryRequestBuilder { private final List> discoveryFilters = new ArrayList<>(); private final List postDiscoveryFilters = new ArrayList<>(); private final Map configurationParameters = new HashMap<>(); + private final List configurationParametersResources = new ArrayList<>(); private final List discoveryListeners = new ArrayList<>(); private boolean implicitConfigurationParametersEnabled = true; private ConfigurationParameters parentConfigurationParameters; @@ -124,7 +126,7 @@ public LauncherDiscoveryRequestBuilder() { } /** - * Add all of the supplied {@code selectors} to the request. + * Add all supplied {@code selectors} to the request. * * @param selectors the {@code DiscoverySelectors} to add; never {@code null} * @return this builder for method chaining @@ -136,7 +138,7 @@ public LauncherDiscoveryRequestBuilder selectors(DiscoverySelector... selectors) } /** - * Add all of the supplied {@code selectors} to the request. + * Add all supplied {@code selectors} to the request. * * @param selectors the {@code DiscoverySelectors} to add; never {@code null} * @return this builder for method chaining @@ -149,7 +151,7 @@ public LauncherDiscoveryRequestBuilder selectors(ListThe {@code filters} are combined using AND semantics, i.e. all of them * have to include a resource for it to end up in the test plan. @@ -185,7 +187,7 @@ public LauncherDiscoveryRequestBuilder configurationParameter(String key, String } /** - * Add all of the supplied configuration parameters to the request. + * Add all supplied configuration parameters to the request. * * @param configurationParameters the map of configuration parameters to add; * never {@code null} @@ -198,6 +200,18 @@ public LauncherDiscoveryRequestBuilder configurationParameters(MapBy default, in addition to those parameters that are passed explicitly + * to this builder, configuration parameters are read from system properties + * and from the {@code junit-platform.properties} classpath resource. + * Passing {@code false} to this method, disables the latter two sources so + * that only explicit configuration parameters are taken into account. + * + * @param enabled {@code true} if implicit configuration parameters should be + * considered + * @return this builder for method chaining + * @since 1.7 + * @see #configurationParameter(String, String) + * @see #configurationParameters(Map) + */ + @API(status = STABLE, since = "1.10") + public LauncherDiscoveryRequestBuilder enableImplicitConfigurationParameters(boolean enabled) { + this.implicitConfigurationParametersEnabled = enabled; return this; } /** - * Add all of the supplied discovery listeners to the request. + * Add all supplied discovery listeners to the request. * *

    In addition to the {@linkplain LauncherDiscoveryListener listeners} * registered using this method, this builder will add a default listener @@ -246,25 +283,6 @@ public LauncherDiscoveryRequestBuilder listeners(LauncherDiscoveryListener... li return this; } - /** - * Configure whether implicit configuration parameters should be considered. - * - *

    By default, in addition to those parameters that are passed explicitly - * to this builder, configuration parameters are read from system properties - * and from the {@code junit-platform.properties} classpath resource. - * Passing {@code false} to this method, disables the latter two sources so - * that only explicit configuration parameters are taken into account. - * - * @since 1.7 - * @see #configurationParameter(String, String) - * @see #configurationParameters(Map) - */ - @API(status = STABLE, since = "1.10") - public LauncherDiscoveryRequestBuilder enableImplicitConfigurationParameters(boolean enabled) { - this.implicitConfigurationParametersEnabled = enabled; - return this; - } - private void storeFilter(Filter filter) { if (filter instanceof EngineFilter) { this.engineFilters.add((EngineFilter) filter); @@ -296,9 +314,10 @@ public LauncherDiscoveryRequest build() { private LauncherConfigurationParameters buildLauncherConfigurationParameters() { Builder builder = LauncherConfigurationParameters.builder() // .explicitParameters(this.configurationParameters) // + .configurationResources(this.configurationParametersResources) // .enableImplicitProviders(this.implicitConfigurationParametersEnabled); - if (parentConfigurationParameters != null) { + if (this.parentConfigurationParameters != null) { builder.parentConfigurationParameters(this.parentConfigurationParameters); } @@ -308,14 +327,14 @@ private LauncherConfigurationParameters buildLauncherConfigurationParameters() { private LauncherDiscoveryListener getLauncherDiscoveryListener(ConfigurationParameters configurationParameters) { LauncherDiscoveryListener defaultDiscoveryListener = getDefaultLauncherDiscoveryListener( configurationParameters); - if (discoveryListeners.isEmpty()) { + if (this.discoveryListeners.isEmpty()) { return defaultDiscoveryListener; } - if (discoveryListeners.contains(defaultDiscoveryListener)) { - return LauncherDiscoveryListeners.composite(discoveryListeners); + if (this.discoveryListeners.contains(defaultDiscoveryListener)) { + return LauncherDiscoveryListeners.composite(this.discoveryListeners); } - List allDiscoveryListeners = new ArrayList<>(discoveryListeners.size() + 1); - allDiscoveryListeners.addAll(discoveryListeners); + List allDiscoveryListeners = new ArrayList<>(this.discoveryListeners.size() + 1); + allDiscoveryListeners.addAll(this.discoveryListeners); allDiscoveryListeners.add(defaultDiscoveryListener); return LauncherDiscoveryListeners.composite(allDiscoveryListeners); } diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryResult.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryResult.java index 9e6d3e7a3bdc..d65b4c3fe4d1 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryResult.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherFactory.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherFactory.java index 8a214a546946..a84296db623b 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherFactory.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherListenerRegistry.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherListenerRegistry.java index 17b21b141f07..9e1850e41e50 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherListenerRegistry.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherListenerRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ListenerRegistry.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ListenerRegistry.java index 1fab26cd1cdf..c5dd3c6fc884 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ListenerRegistry.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ListenerRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/OutcomeDelayingEngineExecutionListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/OutcomeDelayingEngineExecutionListener.java index 9b55df04d521..9e140c655bf3 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/OutcomeDelayingEngineExecutionListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/OutcomeDelayingEngineExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ServiceLoaderRegistry.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ServiceLoaderRegistry.java index 54b446cce41a..83866088dcb6 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ServiceLoaderRegistry.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ServiceLoaderRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ServiceLoaderTestEngineRegistry.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ServiceLoaderTestEngineRegistry.java index 9959e6e3a2e7..ada5a07778c3 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ServiceLoaderTestEngineRegistry.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ServiceLoaderTestEngineRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/SessionPerRequestLauncher.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/SessionPerRequestLauncher.java index 167efb6443ec..c0edfa8e80b0 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/SessionPerRequestLauncher.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/SessionPerRequestLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StackTracePruningEngineExecutionListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StackTracePruningEngineExecutionListener.java index c183fdb683b8..3063fd9b8073 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StackTracePruningEngineExecutionListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StackTracePruningEngineExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StreamInterceptingTestExecutionListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StreamInterceptingTestExecutionListener.java index be0e6a41e1ae..32a135896554 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StreamInterceptingTestExecutionListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StreamInterceptingTestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StreamInterceptor.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StreamInterceptor.java index aa72890f359f..b34fd72c1a15 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StreamInterceptor.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StreamInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/TestEngineFormatter.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/TestEngineFormatter.java index 0c5beba2b06e..ec4fe8418ca7 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/TestEngineFormatter.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/TestEngineFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/LegacyReportingUtils.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/LegacyReportingUtils.java index 4ab5097a9d33..08ef0e55b7c2 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/LegacyReportingUtils.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/LegacyReportingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/LoggingListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/LoggingListener.java index 949d36572f22..6dd62495353a 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/LoggingListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/LoggingListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/MutableTestExecutionSummary.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/MutableTestExecutionSummary.java index 96f341443ce4..da39a9a2b6d4 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/MutableTestExecutionSummary.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/MutableTestExecutionSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/OutputDir.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/OutputDir.java index 14080b1f7e27..31de20e55175 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/OutputDir.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/OutputDir.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/SummaryGeneratingListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/SummaryGeneratingListener.java index a161fc503162..c01c09af2bf9 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/SummaryGeneratingListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/SummaryGeneratingListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/TestExecutionSummary.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/TestExecutionSummary.java index 3a996d0163c7..800338127886 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/TestExecutionSummary.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/TestExecutionSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.java index 379dccdb6f73..a7a02b662731 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,7 @@ package org.junit.platform.launcher.listeners; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.apiguardian.api.API.Status.STABLE; import java.io.IOException; import java.io.PrintWriter; @@ -87,7 +87,7 @@ * * @since 1.8 */ -@API(status = EXPERIMENTAL, since = "1.8") +@API(status = STABLE, since = "1.11") public class UniqueIdTrackingListener implements TestExecutionListener { /** @@ -130,6 +130,7 @@ public class UniqueIdTrackingListener implements TestExecutionListener { private final List uniqueIds = new ArrayList<>(); private boolean enabled; + private TestPlan testPlan; public UniqueIdTrackingListener() { // to avoid missing-explicit-ctor warning @@ -138,22 +139,38 @@ public UniqueIdTrackingListener() { @Override public void testPlanExecutionStarted(TestPlan testPlan) { this.enabled = testPlan.getConfigurationParameters().getBoolean(LISTENER_ENABLED_PROPERTY_NAME).orElse(false); + this.testPlan = testPlan; } @Override public void executionSkipped(TestIdentifier testIdentifier, String reason) { - trackTestUid(testIdentifier); + if (this.enabled) { + // When a container is skipped, there are no events for its children. + // Therefore, in order to track them, we need to traverse the subtree. + trackTestUidRecursively(testIdentifier); + } } @Override public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { - trackTestUid(testIdentifier); + if (this.enabled) { + trackTestUid(testIdentifier); + } + } + + private void trackTestUidRecursively(TestIdentifier testIdentifier) { + boolean tracked = trackTestUid(testIdentifier); + if (!tracked) { + this.testPlan.getChildren(testIdentifier).forEach(this::trackTestUidRecursively); + } } - private void trackTestUid(TestIdentifier testIdentifier) { - if (this.enabled && testIdentifier.isTest()) { + private boolean trackTestUid(TestIdentifier testIdentifier) { + if (testIdentifier.isTest()) { this.uniqueIds.add(testIdentifier.getUniqueId()); + return true; } + return false; } @Override @@ -178,6 +195,7 @@ public void testPlanExecutionFinished(TestPlan testPlan) { logger.error(ex, () -> "Failed to write unique IDs to output file " + outputFile.toAbsolutePath()); } } + this.testPlan = null; } private Path createOutputFile(ConfigurationParameters configurationParameters) { diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/AbortOnFailureLauncherDiscoveryListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/AbortOnFailureLauncherDiscoveryListener.java index b41816c4cc03..e0dd78eda8fc 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/AbortOnFailureLauncherDiscoveryListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/AbortOnFailureLauncherDiscoveryListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/CompositeLauncherDiscoveryListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/CompositeLauncherDiscoveryListener.java index f2931001de2a..f94d256cbe16 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/CompositeLauncherDiscoveryListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/CompositeLauncherDiscoveryListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/LauncherDiscoveryListeners.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/LauncherDiscoveryListeners.java index c8efa369abee..70a183621a41 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/LauncherDiscoveryListeners.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/LauncherDiscoveryListeners.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/LoggingLauncherDiscoveryListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/LoggingLauncherDiscoveryListener.java index b3a674d51802..40a53a7d34cb 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/LoggingLauncherDiscoveryListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/LoggingLauncherDiscoveryListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/session/CompositeLauncherSessionListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/session/CompositeLauncherSessionListener.java index ac538accdf7f..b3d452bd8b42 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/session/CompositeLauncherSessionListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/session/CompositeLauncherSessionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/session/LauncherSessionListeners.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/session/LauncherSessionListeners.java index f200e01adc54..ccd7cc0deaab 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/session/LauncherSessionListeners.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/session/LauncherSessionListeners.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/DequeStack.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/DequeStack.java index c49df5f640b3..be9fda237e56 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/DequeStack.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/DequeStack.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Operator.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Operator.java index 936b53c57203..a8b1be00cf76 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Operator.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Operator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Operators.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Operators.java index a8685088cb10..475fda07d49f 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Operators.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Operators.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseResult.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseResult.java index 1c0a13469bac..50086134e9f8 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseResult.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseResults.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseResults.java index ceb19b32b8d1..c2220484337a 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseResults.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseResults.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseStatus.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseStatus.java index 52f8fd154368..a36c11a06cfb 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseStatus.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Parser.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Parser.java index c3b3000a4c93..2a10848dc14b 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Parser.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Parser.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ShuntingYard.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ShuntingYard.java index 063574ff6bd7..6c92b7846de7 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ShuntingYard.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ShuntingYard.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Stack.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Stack.java index ab7d8b738fd2..50e9c930ace7 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Stack.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Stack.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TagExpression.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TagExpression.java index 823191bcf9ab..51bf079075ca 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TagExpression.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TagExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TagExpressions.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TagExpressions.java index a9f8e04aef9f..4301d9277f4b 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TagExpressions.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TagExpressions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -71,7 +71,7 @@ public boolean evaluate(Collection tags) { @Override public String toString() { - return "!" + toNegate + ""; + return "!" + toNegate; } }; } diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Token.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Token.java index f06a60ca97de..f8451100ae0d 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Token.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Token.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TokenWith.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TokenWith.java index a580df8769f2..511745d8fac6 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TokenWith.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TokenWith.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Tokenizer.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Tokenizer.java index 65d13c11811b..a1b3661b0e42 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Tokenizer.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Tokenizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/module/org.junit.platform.launcher/module-info.java b/junit-platform-launcher/src/module/org.junit.platform.launcher/module-info.java index 9501caff5a6a..9d79f3f4166a 100644 --- a/junit-platform-launcher/src/module/org.junit.platform.launcher/module-info.java +++ b/junit-platform-launcher/src/module/org.junit.platform.launcher/module-info.java @@ -16,6 +16,8 @@ * @since 1.0 * @uses org.junit.platform.engine.TestEngine * @uses org.junit.platform.launcher.LauncherDiscoveryListener + * @uses org.junit.platform.launcher.LauncherInterceptor + * @uses org.junit.platform.launcher.LauncherSessionListener * @uses org.junit.platform.launcher.PostDiscoveryFilter * @uses org.junit.platform.launcher.TestExecutionListener */ @@ -32,6 +34,7 @@ uses org.junit.platform.engine.TestEngine; uses org.junit.platform.launcher.LauncherDiscoveryListener; + uses org.junit.platform.launcher.LauncherInterceptor; uses org.junit.platform.launcher.LauncherSessionListener; uses org.junit.platform.launcher.PostDiscoveryFilter; uses org.junit.platform.launcher.TestExecutionListener; diff --git a/junit-platform-launcher/src/test/README.md b/junit-platform-launcher/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-launcher/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/ConfigurationParametersFactoryForTests.java b/junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/ConfigurationParametersFactoryForTests.java index 4444022d2df5..102634bdfbf7 100644 --- a/junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/ConfigurationParametersFactoryForTests.java +++ b/junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/ConfigurationParametersFactoryForTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/LauncherFactoryForTestingPurposesOnly.java b/junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/LauncherFactoryForTestingPurposesOnly.java index 93002b81fe5f..7036c0609b60 100644 --- a/junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/LauncherFactoryForTestingPurposesOnly.java +++ b/junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/LauncherFactoryForTestingPurposesOnly.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/LegacyReportingUtils.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/LegacyReportingUtils.java index fd8968f4f829..68ac58467101 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/LegacyReportingUtils.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/LegacyReportingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListener.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListener.java index 10d902884eb2..29ef317d3767 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListener.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportData.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportData.java index 1f7993531af7..b99315243944 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportData.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportData.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java index fd9dd98ded2f..e81d3abbd10c 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/JUnitFactory.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/JUnitFactory.java index 382404bdd7a1..8064da6f20c4 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/JUnitFactory.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/JUnitFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/LegacyReportingName.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/LegacyReportingName.java index a81214a70c4e..95ce6cac8d9a 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/LegacyReportingName.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/LegacyReportingName.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListener.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListener.java index 05c452880a90..dbb5541e9ce3 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListener.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -236,10 +236,9 @@ else if (source instanceof UriSource) { public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) { String id = inProgressIds.get(testIdentifier.getUniqueIdObject()); eventsFileWriter.append(reported(id, Instant.now()), // - reported -> reported.append(attachments(), attachments -> attachments.append(data(), data -> { - data.withTime(entry.getTimestamp()); - entry.getKeyValuePairs().forEach(data::addEntry); - }))); + reported -> reported.append(attachments(), // + attachments -> attachments.append(data(entry.getTimestamp()), // + data -> entry.getKeyValuePairs().forEach(data::addEntry)))); } @Override diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/Type.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/Type.java index 49f8d32cbb92..ca08eef6fad8 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/Type.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/Type.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/UniqueId.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/UniqueId.java index 4d3ef7b61e81..c1ac3bbce988 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/UniqueId.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/UniqueId.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-reporting/src/test/README.md b/junit-platform-reporting/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-reporting/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatform.java b/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatform.java index bbc87847f7c1..8b8c26f098b1 100644 --- a/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatform.java +++ b/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatform.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -30,6 +30,7 @@ import org.junit.platform.launcher.TestPlan; import org.junit.platform.launcher.core.LauncherFactory; import org.junit.platform.suite.api.ConfigurationParameter; +import org.junit.platform.suite.api.ConfigurationParametersResource; import org.junit.platform.suite.api.ExcludeClassNamePatterns; import org.junit.platform.suite.api.ExcludeEngines; import org.junit.platform.suite.api.ExcludePackages; @@ -38,6 +39,7 @@ import org.junit.platform.suite.api.IncludeEngines; import org.junit.platform.suite.api.IncludePackages; import org.junit.platform.suite.api.IncludeTags; +import org.junit.platform.suite.api.Select; import org.junit.platform.suite.api.SelectClasses; import org.junit.platform.suite.api.SelectClasspathResource; import org.junit.platform.suite.api.SelectDirectories; @@ -87,6 +89,7 @@ * ClassNameFilter#STANDARD_INCLUDE_PATTERN}). * * @since 1.0 + * @see Select * @see SelectClasses * @see SelectClasspathResource * @see SelectDirectories @@ -105,6 +108,7 @@ * @see SuiteDisplayName * @see org.junit.platform.suite.api.UseTechnicalNames UseTechnicalNames * @see ConfigurationParameter + * @see ConfigurationParametersResource * @deprecated since 1.8, in favor of the {@link Suite @Suite} support provided by * the {@code junit-platform-suite-engine} module; to be removed in JUnit Platform 2.0 */ diff --git a/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformRunnerListener.java b/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformRunnerListener.java index 039117187dd4..e91045ee8422 100644 --- a/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformRunnerListener.java +++ b/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformRunnerListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformTestTree.java b/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformTestTree.java index cb32dfebc477..96f7239f9227 100644 --- a/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformTestTree.java +++ b/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformTestTree.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-runner/src/test/README.md b/junit-platform-runner/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-runner/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/AfterSuite.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/AfterSuite.java new file mode 100644 index 000000000000..c2b5e33a3467 --- /dev/null +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/AfterSuite.java @@ -0,0 +1,75 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.api; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @AfterSuite} is used to signal that the annotated method should be + * executed after all tests in the current test suite. + * + *

    Method Signatures

    + * + *

    {@code @AfterSuite} methods must have a {@code void} return type, must + * be {@code static} and must not be {@code private}. + * + *

    Inheritance and Execution Order

    + * + *

    {@code @AfterSuite} methods are inherited from superclasses as long as they + * are not overridden according to the visibility rules of the Java + * language. Furthermore, {@code @AfterSuite} methods from superclasses will be + * executed after {@code @AfterSuite} methods in subclasses. + * + *

    The JUnit Platform Suite Engine does not guarantee the execution order of + * multiple {@code @AfterSuite} methods that are declared within a single test + * class or test interface. While it may at times appear that these methods are + * invoked in alphabetical order, they are in fact sorted using an algorithm + * that is deterministic but intentionally non-obvious. + * + *

    In addition, {@code @AfterSuite} methods are in no way linked to + * {@code @BeforeSuite} methods. Consequently, there are no guarantees with regard + * to their wrapping behavior. For example, given two + * {@code @BeforeSuite} methods {@code createA()} and {@code createB()} as well as + * two {@code @AfterSuite} methods {@code destroyA()} and {@code destroyB()}, the + * order in which the {@code @BeforeSuite} methods are executed (e.g. + * {@code createA()} before {@code createB()}) does not imply any order for the + * seemingly corresponding {@code @AfterSuite} methods. In other words, + * {@code destroyA()} might be called before or after + * {@code destroyB()}. The JUnit Team therefore recommends that developers + * declare at most one {@code @BeforeSuite} method and at most one + * {@code @AfterSuite} method per test class or test interface unless there are no + * dependencies between the {@code @BeforeSuite} methods or between the + * {@code @AfterSuite} methods. + * + *

    Composition

    + * + *

    {@code @AfterSuite} may be used as a meta-annotation in order to create + * a custom composed annotation that inherits the semantics of + * {@code @AfterSuite}. + * + * @since 1.11 + * @see BeforeSuite + * @see Suite + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@API(status = EXPERIMENTAL, since = "1.11") +public @interface AfterSuite { +} diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/BeforeSuite.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/BeforeSuite.java new file mode 100644 index 000000000000..5b4c0d3c93f0 --- /dev/null +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/BeforeSuite.java @@ -0,0 +1,75 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.api; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @BeforeSuite} is used to signal that the annotated method should be + * executed before all tests in the current test suite. + * + *

    Method Signatures

    + * + *

    {@code @BeforeSuite} methods must have a {@code void} return type, must + * be {@code static} and must not be {@code private}. + * + *

    Inheritance and Execution Order

    + * + *

    {@code @BeforeSuite} methods are inherited from superclasses as long as they + * are not overridden according to the visibility rules of the Java + * language. Furthermore, {@code @BeforeSuite} methods from superclasses will be + * executed before {@code @BeforeSuite} methods in subclasses. + * + *

    The JUnit Platform Suite Engine does not guarantee the execution order of + * multiple {@code @BeforeSuite} methods that are declared within a single test + * class or test interface. While it may at times appear that these methods are + * invoked in alphabetical order, they are in fact sorted using an algorithm + * that is deterministic but intentionally non-obvious. + * + *

    In addition, {@code @BeforeSuite} methods are in no way linked to + * {@code @AfterSuite} methods. Consequently, there are no guarantees with regard + * to their wrapping behavior. For example, given two + * {@code @BeforeSuite} methods {@code createA()} and {@code createB()} as well as + * two {@code @AfterSuite} methods {@code destroyA()} and {@code destroyB()}, the + * order in which the {@code @BeforeSuite} methods are executed (e.g. + * {@code createA()} before {@code createB()}) does not imply any order for the + * seemingly corresponding {@code @AfterSuite} methods. In other words, + * {@code destroyA()} might be called before or after + * {@code destroyB()}. The JUnit Team therefore recommends that developers + * declare at most one {@code @BeforeSuite} method and at most one + * {@code @AfterSuite} method per test class or test interface unless there are no + * dependencies between the {@code @BeforeSuite} methods or between the + * {@code @AfterSuite} methods. + * + *

    Composition

    + * + *

    {@code @BeforeSuite} may be used as a meta-annotation in order to create + * a custom composed annotation that inherits the semantics of + * {@code @BeforeSuite}. + * + * @since 1.11 + * @see AfterSuite + * @see Suite + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@API(status = EXPERIMENTAL, since = "1.11") +public @interface BeforeSuite { +} diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParameter.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParameter.java index 3b844b3ad340..268f7029cc12 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParameter.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -29,6 +29,7 @@ * a test suite on the JUnit Platform. * * @since 1.8 + * @see ConfigurationParametersResource * @see DisableParentConfigurationParameters * @see Suite * @see org.junit.platform.runner.JUnitPlatform diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParameters.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParameters.java index ecad947396cb..9c7514a1315b 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParameters.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParametersResource.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParametersResource.java new file mode 100644 index 000000000000..86fdfb38a71f --- /dev/null +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParametersResource.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.api; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @ConfigurationParametersResource} is a + * {@linkplain Repeatable repeatable} annotation that specifies a configuration + * file in Java's properties format on the classpath to be added to the + * discovery request when running a test suite on the JUnit Platform. + * + * @since 1.11 + * @see ConfigurationParameter + * @see DisableParentConfigurationParameters + * @see Suite + * @see org.junit.platform.runner.JUnitPlatform + * @see org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder#configurationParametersResources(String...) + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +@Documented +@API(status = EXPERIMENTAL, since = "1.11") +@Repeatable(ConfigurationParametersResources.class) +public @interface ConfigurationParametersResource { + + /** + * The classpath location for the desired properties file; never {@code null} or blank. + */ + String value(); + +} diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParametersResources.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParametersResources.java new file mode 100644 index 000000000000..6b6fd1f6790e --- /dev/null +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParametersResources.java @@ -0,0 +1,48 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.api; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @ConfigurationParametersResources} is a container for one or more + * {@link ConfigurationParametersResource @ConfigurationParametersResource} declarations. + * + *

    Note, however, that use of the {@code @ConfigurationParametersResources} container + * is completely optional since {@code @ConfigurationParametersResource} is a + * {@linkplain java.lang.annotation.Repeatable repeatable} annotation. + * + * @since 1.11 + * @see ConfigurationParametersResource + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +@Documented +@API(status = EXPERIMENTAL, since = "1.11") +public @interface ConfigurationParametersResources { + + /** + * An array of one or more {@link ConfigurationParametersResource @ConfigurationParameterResource} + * declarations. + */ + ConfigurationParametersResource[] value(); + +} diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/DisableParentConfigurationParameters.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/DisableParentConfigurationParameters.java index 088fc037a9df..84905807c45c 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/DisableParentConfigurationParameters.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/DisableParentConfigurationParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -24,16 +24,18 @@ /** * Disable parent configuration parameters. * - *

    By default a suite discovers tests using the configuration parameters + *

    By default, a suite discovers tests using the configuration parameters * explicitly configured via {@link ConfigurationParameter @ConfigurationParameter} - * and the configuration parameters from the discovery request that was used to - * discover the suite. + * and {@link ConfigurationParametersResource} as well as the configuration + * parameters from the discovery request that was used to discover the suite. * *

    Annotating a suite with this annotation disables the latter source so - * that only explicit configuration parameters are taken into account. + * that only explicit configuration parameters and resources are taken into + * account. * * @since 1.8 * @see ConfigurationParameter + * @see ConfigurationParametersResource * @see Suite */ @Retention(RetentionPolicy.RUNTIME) diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeClassNamePatterns.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeClassNamePatterns.java index 3aa18b8bdc63..a68525a4a09b 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeClassNamePatterns.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeClassNamePatterns.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeEngines.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeEngines.java index e65acb5c7a5c..1925e2b3d612 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeEngines.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeEngines.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludePackages.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludePackages.java index 22b7736c2a0b..196f6ab156c7 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludePackages.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludePackages.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeTags.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeTags.java index 9d55090efc67..695e68cdb9e1 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeTags.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeTags.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeClassNamePatterns.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeClassNamePatterns.java index 5d75fedbb6d9..d3d962f1ffb1 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeClassNamePatterns.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeClassNamePatterns.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeEngines.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeEngines.java index 4117947fbf99..b7014e0d39fa 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeEngines.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeEngines.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludePackages.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludePackages.java index 7635d1dc3bc6..d3191f255e07 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludePackages.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludePackages.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeTags.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeTags.java index 744dc75a30ed..98cf52e50a38 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeTags.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeTags.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Select.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Select.java new file mode 100644 index 000000000000..79c991743f5e --- /dev/null +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Select.java @@ -0,0 +1,49 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.api; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @Select} is a {@linkplain Repeatable repeatable} annotation that + * specifies which tests to select based on prefixed + * {@linkplain org.junit.platform.engine.DiscoverySelectorIdentifier selector identifiers}. + * + * @since 1.11 + * @see Suite + * @see org.junit.platform.engine.discovery.DiscoverySelectors#parse(String) + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +@Documented +@API(status = EXPERIMENTAL, since = "1.11") +@Repeatable(Selects.class) +public @interface Select { + + /** + * One or more prefixed + * {@linkplain org.junit.platform.engine.DiscoverySelectorIdentifier selector identifiers} + * to select. + */ + String[] value(); + +} diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasses.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasses.java index 024772788141..13aa76cf0cf9 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasses.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasses.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasspathResource.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasspathResource.java index a6fda29df8ba..1705163ad9aa 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasspathResource.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasspathResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasspathResources.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasspathResources.java index c1be1af94e45..a643fbfc0415 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasspathResources.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasspathResources.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectDirectories.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectDirectories.java index 43bcf16ccec6..c08747e5a9fa 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectDirectories.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectDirectories.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectFile.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectFile.java index bddf3cfc3d03..0df68a8ca0b4 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectFile.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectFile.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectFiles.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectFiles.java index e5a989e87ff9..53875f94a666 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectFiles.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectFiles.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectMethod.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectMethod.java index a1313a343398..7be2264c8a0d 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectMethod.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectMethods.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectMethods.java index 542c647aa108..6f487107856d 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectMethods.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectMethods.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectModules.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectModules.java index a4cbe0111391..5b003444629a 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectModules.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectModules.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectPackages.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectPackages.java index c2d5eafff60b..592fe107af56 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectPackages.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectPackages.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectUris.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectUris.java index 5783114bdacd..f3f0bb8e7398 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectUris.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectUris.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Selects.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Selects.java new file mode 100644 index 000000000000..44403f81aa21 --- /dev/null +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Selects.java @@ -0,0 +1,47 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.api; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +/** + * {@code @Selects} is a container for one or more + * {@link Select @Select} declarations. + * + *

    Note, however, that use of the {@code @Selects} container is + * completely optional since {@code @Select} is a + * {@linkplain java.lang.annotation.Repeatable repeatable} annotation. + * + * @since 1.11 + * @see Select + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +@Documented +@API(status = EXPERIMENTAL, since = "1.11") +public @interface Selects { + + /** + * An array of one or more {@link Select @Select} declarations. + */ + Select[] value(); + +} diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Suite.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Suite.java index d1d3f85fd64e..35e0d2f2140f 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Suite.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Suite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -20,7 +20,6 @@ import java.lang.annotation.Target; import org.apiguardian.api.API; -import org.apiguardian.api.API.Status; import org.junit.platform.commons.annotation.Testable; /** @@ -46,6 +45,7 @@ * configuration parameters are taken into account. * * @since 1.8 + * @see Select * @see SelectClasses * @see SelectClasspathResource * @see SelectDirectories @@ -63,6 +63,7 @@ * @see ExcludeTags * @see SuiteDisplayName * @see ConfigurationParameter + * @see ConfigurationParametersResource * @see DisableParentConfigurationParameters * @see org.junit.platform.launcher.LauncherDiscoveryRequest * @see org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder @@ -81,7 +82,7 @@ * * @since 1.9 */ - @API(status = Status.EXPERIMENTAL, since = "1.9") + @API(status = STABLE, since = "1.11") boolean failIfNoTests() default true; } diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SuiteDisplayName.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SuiteDisplayName.java index 3cd11cb9ef4e..0db93a88eedf 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SuiteDisplayName.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SuiteDisplayName.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/UseTechnicalNames.java b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/UseTechnicalNames.java index 12b1180841a2..d9621689172e 100644 --- a/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/UseTechnicalNames.java +++ b/junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/UseTechnicalNames.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-api/src/test/README.md b/junit-platform-suite-api/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-suite-api/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-platform-suite-commons/src/main/java/org/junit/platform/suite/commons/AdditionalDiscoverySelectors.java b/junit-platform-suite-commons/src/main/java/org/junit/platform/suite/commons/AdditionalDiscoverySelectors.java index 65e6c3504190..a66a5545de54 100644 --- a/junit-platform-suite-commons/src/main/java/org/junit/platform/suite/commons/AdditionalDiscoverySelectors.java +++ b/junit-platform-suite-commons/src/main/java/org/junit/platform/suite/commons/AdditionalDiscoverySelectors.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -17,6 +17,7 @@ import org.junit.platform.commons.util.Preconditions; import org.junit.platform.commons.util.StringUtils; +import org.junit.platform.engine.DiscoverySelector; import org.junit.platform.engine.discovery.ClassSelector; import org.junit.platform.engine.discovery.ClasspathResourceSelector; import org.junit.platform.engine.discovery.DirectorySelector; @@ -112,6 +113,11 @@ static ClasspathResourceSelector selectClasspathResource(String classpathResourc return DiscoverySelectors.selectClasspathResource(classpathResourceName, FilePosition.from(line, column)); } + static List parseIdentifiers(String[] identifiers) { + return DiscoverySelectors.parseAll(identifiers) // + .collect(Collectors.toList()); + } + private static Stream uniqueStreamOf(T[] elements) { return Arrays.stream(elements).distinct(); } diff --git a/junit-platform-suite-commons/src/main/java/org/junit/platform/suite/commons/SuiteLauncherDiscoveryRequestBuilder.java b/junit-platform-suite-commons/src/main/java/org/junit/platform/suite/commons/SuiteLauncherDiscoveryRequestBuilder.java index c94b8009aaf8..23eecf29ce62 100644 --- a/junit-platform-suite-commons/src/main/java/org/junit/platform/suite/commons/SuiteLauncherDiscoveryRequestBuilder.java +++ b/junit-platform-suite-commons/src/main/java/org/junit/platform/suite/commons/SuiteLauncherDiscoveryRequestBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -46,6 +46,7 @@ import org.junit.platform.launcher.TagFilter; import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; import org.junit.platform.suite.api.ConfigurationParameter; +import org.junit.platform.suite.api.ConfigurationParametersResource; import org.junit.platform.suite.api.DisableParentConfigurationParameters; import org.junit.platform.suite.api.ExcludeClassNamePatterns; import org.junit.platform.suite.api.ExcludeEngines; @@ -55,6 +56,7 @@ import org.junit.platform.suite.api.IncludeEngines; import org.junit.platform.suite.api.IncludePackages; import org.junit.platform.suite.api.IncludeTags; +import org.junit.platform.suite.api.Select; import org.junit.platform.suite.api.SelectClasses; import org.junit.platform.suite.api.SelectClasspathResource; import org.junit.platform.suite.api.SelectDirectories; @@ -65,7 +67,48 @@ import org.junit.platform.suite.api.SelectUris; /** + * The {@code SuiteLauncherDiscoveryRequestBuilder} provides a light-weight DSL + * for generating a {@link LauncherDiscoveryRequest} specifically tailored for + * suite execution. + * + *

    Example

    + * + *
    {@code
    + * SuiteLauncherDiscoveryRequestBuilder.request()
    + *   .selectors(
    + *        selectPackage("org.example.user"),
    + *        selectClass("org.example.payment.PaymentTests"),
    + *        selectClass(ShippingTests.class),
    + *        selectMethod("org.example.order.OrderTests#test1"),
    + *        selectMethod("org.example.order.OrderTests#test2()"),
    + *        selectMethod("org.example.order.OrderTests#test3(java.lang.String)"),
    + *        selectMethod("org.example.order.OrderTests", "test4"),
    + *        selectMethod(OrderTests.class, "test5"),
    + *        selectMethod(OrderTests.class, testMethod),
    + *        selectClasspathRoots(Collections.singleton(Paths.get("/my/local/path1"))),
    + *        selectUniqueId("unique-id-1"),
    + *        selectUniqueId("unique-id-2")
    + *   )
    + *   .filters(
    + *        includeEngines("junit-jupiter", "spek"),
    + *        // excludeEngines("junit-vintage"),
    + *        includeTags("fast"),
    + *        // excludeTags("slow"),
    + *        includeClassNamePatterns(".*Test[s]?")
    + *        // includeClassNamePatterns("org\.example\.tests.*")
    + *   )
    + *   .configurationParameter("key", "value")
    + *   .enableImplicitConfigurationParameters(true)
    + *   .applyConfigurationParametersFromSuite(MySuite.class)
    + *   .applySelectorsAndFiltersFromSuite(MySuite.class)
    + *   .build();
    + * }
    + * * @since 1.8 + * @see org.junit.platform.engine.discovery.DiscoverySelectors + * @see org.junit.platform.engine.discovery.ClassNameFilter + * @see org.junit.platform.launcher.EngineFilter + * @see org.junit.platform.launcher.TagFilter */ @API(status = Status.INTERNAL, since = "1.8", consumers = { "org.junit.platform.suite.engine", "org.junit.platform.runner" }) @@ -81,61 +124,226 @@ public final class SuiteLauncherDiscoveryRequestBuilder { private SuiteLauncherDiscoveryRequestBuilder() { } + /** + * Create a new {@code SuiteLauncherDiscoveryRequestBuilder}. + * + * @return a new builder + */ public static SuiteLauncherDiscoveryRequestBuilder request() { return new SuiteLauncherDiscoveryRequestBuilder(); } - public SuiteLauncherDiscoveryRequestBuilder filterStandardClassNamePatterns( - boolean filterStandardClassNamePatterns) { - this.filterStandardClassNamePatterns = filterStandardClassNamePatterns; - return this; - } - + /** + * Add all supplied {@code selectors} to the request. + * + * @param selectors the {@code DiscoverySelectors} to add; never {@code null} + * @return this builder for method chaining + */ public SuiteLauncherDiscoveryRequestBuilder selectors(DiscoverySelector... selectors) { - delegate.selectors(selectors); + this.delegate.selectors(selectors); return this; } + /** + * Add all supplied {@code selectors} to the request. + * + * @param selectors the {@code DiscoverySelectors} to add; never {@code null} + * @return this builder for method chaining + */ public SuiteLauncherDiscoveryRequestBuilder selectors(List selectors) { - delegate.selectors(selectors); + this.delegate.selectors(selectors); return this; } + /** + * Add all supplied {@code filters} to the request. + * + *

    The {@code filters} are combined using AND semantics, i.e. all of them + * have to include a resource for it to end up in the test plan. + * + *

    Warning: be cautious when registering multiple competing + * {@link EngineFilter#includeEngines include} {@code EngineFilters} or multiple + * competing {@link EngineFilter#excludeEngines exclude} {@code EngineFilters} + * for the same discovery request since doing so will likely lead to + * undesirable results (i.e., zero engines being active). + * + * @param filters the {@code Filter}s to add; never {@code null} + * @return this builder for method chaining + */ public SuiteLauncherDiscoveryRequestBuilder filters(Filter... filters) { - delegate.filters(filters); + this.delegate.filters(filters); + return this; + } + + /** + * Specify whether to filter standard class name patterns. + *

    If set to {@code true}, standard class name patterns are filtered. + * + * @param filterStandardClassNamePatterns {@code true} to filter standard class + * name patterns, {@code false} otherwise + * @return this builder for method chaining + */ + public SuiteLauncherDiscoveryRequestBuilder filterStandardClassNamePatterns( + boolean filterStandardClassNamePatterns) { + this.filterStandardClassNamePatterns = filterStandardClassNamePatterns; return this; } + /** + * Add the supplied configuration parameter to the request. + * + * @param key the configuration parameter key under which to store the + * value; never {@code null} or blank + * @param value the value to store + * @return this builder for method chaining + */ public SuiteLauncherDiscoveryRequestBuilder configurationParameter(String key, String value) { - delegate.configurationParameter(key, value); + this.delegate.configurationParameter(key, value); return this; } + /** + * Add all supplied configuration parameters to the request. + * + * @param configurationParameters the map of configuration parameters to add; + * never {@code null} + * @return this builder for method chaining + * @see #configurationParameter(String, String) + */ public SuiteLauncherDiscoveryRequestBuilder configurationParameters(Map configurationParameters) { - delegate.configurationParameters(configurationParameters); + this.delegate.configurationParameters(configurationParameters); + return this; + } + + public SuiteLauncherDiscoveryRequestBuilder configurationParametersResource(String resourceFile) { + this.delegate.configurationParametersResources(resourceFile); return this; } + /** + * Set the parent configuration parameters to use for the request. + * + *

    Any explicit configuration parameters configured via + * {@link #configurationParameter(String, String)} or + * {@link #configurationParameters(Map)} takes precedence over the supplied + * configuration parameters. + * + * @param parentConfigurationParameters the parent instance to use for looking + * up configuration parameters that have not been explicitly configured; + * never {@code null} + * @return this builder for method chaining + * @see #configurationParameter(String, String) + * @see #configurationParameters(Map) + */ public SuiteLauncherDiscoveryRequestBuilder parentConfigurationParameters( ConfigurationParameters parentConfigurationParameters) { this.parentConfigurationParameters = parentConfigurationParameters; return this; } + /** + * Configure whether implicit configuration parameters should be considered. + * + *

    By default, in addition to those parameters that are passed explicitly + * to this builder, configuration parameters are read from system properties + * and from the {@code junit-platform.properties} classpath resource. + * Passing {@code false} to this method, disables the latter two sources so + * that only explicit configuration parameters are taken into account. + * + * @param enabled {@code true} if implicit configuration parameters should be + * considered + * @return this builder for method chaining + * @see #configurationParameter(String, String) + * @see #configurationParameters(Map) + */ public SuiteLauncherDiscoveryRequestBuilder enableImplicitConfigurationParameters(boolean enabled) { - delegate.enableImplicitConfigurationParameters(enabled); + this.delegate.enableImplicitConfigurationParameters(enabled); return this; } + /** + * Apply a suite's annotation-based configuration, selectors, and filters to + * this builder. + * + * @param suiteClass the class to apply the annotations from; never {@code null} + * @return this builder for method chaining + * @see org.junit.platform.suite.api.Suite + * @deprecated as of JUnit Platform 1.11 in favor of + * {@link #applyConfigurationParametersFromSuite} and + * {@link #applySelectorsAndFiltersFromSuite} + */ + @Deprecated public SuiteLauncherDiscoveryRequestBuilder suite(Class suiteClass) { Preconditions.notNull(suiteClass, "Suite class must not be null"); + applyConfigurationParametersFromSuite(suiteClass); + applySelectorsAndFiltersFromSuite(suiteClass); + return this; + } + + /** + * Apply a suite's annotation-based configuration to this builder. + * + *

    This will apply the configuration from the following annotations. + *

      + *
    • {@link ConfigurationParameter}
    • + *
    • {@link DisableParentConfigurationParameters}
    • + *
    + * + * @param suiteClass the class to apply the configuration annotations from; + * never {@code null} + * @return this builder for method chaining + * @since 1.11 + * @see org.junit.platform.suite.api.Suite + */ + public SuiteLauncherDiscoveryRequestBuilder applyConfigurationParametersFromSuite(Class suiteClass) { + Preconditions.notNull(suiteClass, "Suite class must not be null"); - // Annotations in alphabetical order (except @SelectClasses) // @formatter:off findRepeatableAnnotations(suiteClass, ConfigurationParameter.class) .forEach(configuration -> configurationParameter(configuration.key(), configuration.value())); + findRepeatableAnnotations(suiteClass, ConfigurationParametersResource.class) + .forEach(configResource -> configurationParametersResource(configResource.value())); findAnnotation(suiteClass, DisableParentConfigurationParameters.class) - .ifPresent(__ -> enableParentConfigurationParameters = false); + .ifPresent(__ -> this.enableParentConfigurationParameters = false); + // @formatter:on + return this; + } + + /** + * Apply a suite's annotation-based discovery selectors and filters to this + * builder. + * + *

    This will apply the configuration from the following annotations. + *

      + *
    • {@link ExcludeClassNamePatterns}
    • + *
    • {@link ExcludeEngines}
    • + *
    • {@link ExcludePackages}
    • + *
    • {@link ExcludeTags}
    • + *
    • {@link IncludeClassNamePatterns}
    • + *
    • {@link IncludeEngines}
    • + *
    • {@link IncludePackages}
    • + *
    • {@link IncludeTags}
    • + *
    • {@link SelectClasses}
    • + *
    • {@link SelectClasspathResource}
    • + *
    • {@link SelectDirectories}
    • + *
    • {@link SelectFile}
    • + *
    • {@link SelectMethod}
    • + *
    • {@link SelectModules}
    • + *
    • {@link SelectUris}
    • + *
    • {@link SelectPackages}
    • + *
    + * + * @param suiteClass the class to apply the discovery selectors and filter + * annotations from; never {@code null} + * @return this builder for method chaining + * @since 1.11 + * @see org.junit.platform.suite.api.Suite + */ + public SuiteLauncherDiscoveryRequestBuilder applySelectorsAndFiltersFromSuite(Class suiteClass) { + Preconditions.notNull(suiteClass, "Suite class must not be null"); + + // Annotations in alphabetical order (except @SelectClasses) + // @formatter:off findAnnotationValues(suiteClass, ExcludeClassNamePatterns.class, ExcludeClassNamePatterns::value) .flatMap(SuiteLauncherDiscoveryRequestBuilder::trimmed) .map(ClassNameFilter::excludeClassNamePatterns) @@ -162,7 +370,7 @@ public SuiteLauncherDiscoveryRequestBuilder suite(Class suiteClass) { .flatMap(SuiteLauncherDiscoveryRequestBuilder::trimmed) .map(this::createIncludeClassNameFilter) .ifPresent(filters -> { - includeClassNamePatternsUsed = true; + this.includeClassNamePatternsUsed = true; filters(filters); }); findAnnotationValues(suiteClass, IncludeEngines.class, IncludeEngines::value) @@ -194,20 +402,27 @@ public SuiteLauncherDiscoveryRequestBuilder suite(Class suiteClass) { findAnnotationValues(suiteClass, SelectPackages.class, SelectPackages::value) .map(AdditionalDiscoverySelectors::selectPackages) .ifPresent(this::selectors); + findAnnotationValues(suiteClass, Select.class, Select::value) + .map(AdditionalDiscoverySelectors::parseIdentifiers) + .ifPresent(this::selectors); // @formatter:on return this; } + /** + * Build the {@link LauncherDiscoveryRequest} that has been configured via + * this builder. + */ public LauncherDiscoveryRequest build() { - if (filterStandardClassNamePatterns && !includeClassNamePatternsUsed) { - delegate.filters(createIncludeClassNameFilter(STANDARD_INCLUDE_PATTERN)); + if (this.filterStandardClassNamePatterns && !this.includeClassNamePatternsUsed) { + this.delegate.filters(createIncludeClassNameFilter(STANDARD_INCLUDE_PATTERN)); } - if (enableParentConfigurationParameters && parentConfigurationParameters != null) { - delegate.parentConfigurationParameters(parentConfigurationParameters); + if (this.enableParentConfigurationParameters && this.parentConfigurationParameters != null) { + this.delegate.parentConfigurationParameters(this.parentConfigurationParameters); } - return delegate.build(); + return this.delegate.build(); } private List selectClasses(Class suiteClass, SelectClasses annotation) { @@ -229,7 +444,7 @@ private static Stream toClassSelectors(Class suiteClass, Selec private MethodSelector selectMethod(Class suiteClass, SelectMethod annotation) { MethodSelector methodSelector = toMethodSelector(suiteClass, annotation); - selectedClassNames.add(methodSelector.getClassName()); + this.selectedClassNames.add(methodSelector.getClassName()); return methodSelector; } diff --git a/junit-platform-suite-commons/src/test/README.md b/junit-platform-suite-commons/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-suite-commons/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/ClassSelectorResolver.java b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/ClassSelectorResolver.java index 441dc3f836c9..153a671b317d 100644 --- a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/ClassSelectorResolver.java +++ b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/ClassSelectorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/DiscoverySelectorResolver.java b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/DiscoverySelectorResolver.java index f9c20e5acca9..15d31d65ccc2 100644 --- a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/DiscoverySelectorResolver.java +++ b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/DiscoverySelectorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/IsPotentialTestContainer.java b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/IsPotentialTestContainer.java index 9e6816af646a..f079cf8162bf 100644 --- a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/IsPotentialTestContainer.java +++ b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/IsPotentialTestContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/IsSuiteClass.java b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/IsSuiteClass.java index f336c03fa746..6a162075a719 100644 --- a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/IsSuiteClass.java +++ b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/IsSuiteClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/LifecycleMethodUtils.java b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/LifecycleMethodUtils.java new file mode 100644 index 000000000000..b00bca64a131 --- /dev/null +++ b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/LifecycleMethodUtils.java @@ -0,0 +1,90 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.engine; + +import static org.junit.platform.commons.util.AnnotationUtils.findAnnotatedMethods; +import static org.junit.platform.commons.util.ReflectionUtils.returnsPrimitiveVoid; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.List; + +import org.junit.platform.commons.JUnitException; +import org.junit.platform.commons.util.ReflectionUtils; +import org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode; +import org.junit.platform.engine.support.hierarchical.ThrowableCollector; +import org.junit.platform.suite.api.AfterSuite; +import org.junit.platform.suite.api.BeforeSuite; + +/** + * Collection of utilities for working with test lifecycle methods. + * + * @since 1.11 + */ +final class LifecycleMethodUtils { + + private LifecycleMethodUtils() { + /* no-op */ + } + + static List findBeforeSuiteMethods(Class testClass, ThrowableCollector throwableCollector) { + return findMethodsAndAssertStaticAndNonPrivate(testClass, BeforeSuite.class, HierarchyTraversalMode.TOP_DOWN, + throwableCollector); + } + + static List findAfterSuiteMethods(Class testClass, ThrowableCollector throwableCollector) { + return findMethodsAndAssertStaticAndNonPrivate(testClass, AfterSuite.class, HierarchyTraversalMode.BOTTOM_UP, + throwableCollector); + } + + private static List findMethodsAndAssertStaticAndNonPrivate(Class testClass, + Class annotationType, HierarchyTraversalMode traversalMode, + ThrowableCollector throwableCollector) { + + List methods = findAnnotatedMethods(testClass, annotationType, traversalMode); + throwableCollector.execute(() -> methods.forEach(method -> { + assertVoid(annotationType, method); + assertStatic(annotationType, method); + assertNonPrivate(annotationType, method); + assertNoParameters(annotationType, method); + })); + return methods; + } + + private static void assertStatic(Class annotationType, Method method) { + if (ReflectionUtils.isNotStatic(method)) { + throw new JUnitException(String.format("@%s method '%s' must be static.", annotationType.getSimpleName(), + method.toGenericString())); + } + } + + private static void assertNonPrivate(Class annotationType, Method method) { + if (ReflectionUtils.isPrivate(method)) { + throw new JUnitException(String.format("@%s method '%s' must not be private.", + annotationType.getSimpleName(), method.toGenericString())); + } + } + + private static void assertVoid(Class annotationType, Method method) { + if (!returnsPrimitiveVoid(method)) { + throw new JUnitException(String.format("@%s method '%s' must not return a value.", + annotationType.getSimpleName(), method.toGenericString())); + } + } + + private static void assertNoParameters(Class annotationType, Method method) { + if (method.getParameterCount() > 0) { + throw new JUnitException(String.format("@%s method '%s' must not accept parameters.", + annotationType.getSimpleName(), method.toGenericString())); + } + } + +} diff --git a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/NoTestsDiscoveredException.java b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/NoTestsDiscoveredException.java index 966cfcbe025e..2c4db7af1bb3 100644 --- a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/NoTestsDiscoveredException.java +++ b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/NoTestsDiscoveredException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteEngineDescriptor.java b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteEngineDescriptor.java index 3c351b2eba46..b70aae700fd7 100644 --- a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteEngineDescriptor.java +++ b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteEngineDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteLauncher.java b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteLauncher.java index af776ef84f46..e2932f3bd4b1 100644 --- a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteLauncher.java +++ b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteTestDescriptor.java b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteTestDescriptor.java index 52cb4e1166fe..ccf40405f7bd 100644 --- a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteTestDescriptor.java +++ b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,8 +13,12 @@ import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation; import static org.junit.platform.suite.commons.SuiteLauncherDiscoveryRequestBuilder.request; +import java.lang.reflect.Method; +import java.util.List; + import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.commons.util.ReflectionUtils; import org.junit.platform.commons.util.StringUtils; import org.junit.platform.engine.ConfigurationParameters; import org.junit.platform.engine.EngineExecutionListener; @@ -24,6 +28,8 @@ import org.junit.platform.engine.discovery.DiscoverySelectors; import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor; import org.junit.platform.engine.support.descriptor.ClassSource; +import org.junit.platform.engine.support.hierarchical.OpenTest4JAwareThrowableCollector; +import org.junit.platform.engine.support.hierarchical.ThrowableCollector; import org.junit.platform.launcher.LauncherDiscoveryRequest; import org.junit.platform.launcher.core.LauncherDiscoveryResult; import org.junit.platform.launcher.listeners.TestExecutionSummary; @@ -70,14 +76,14 @@ private static Boolean getFailIfNoTests(Class suiteClass) { SuiteTestDescriptor addDiscoveryRequestFrom(Class suiteClass) { Preconditions.condition(launcherDiscoveryResult == null, - "discovery request can not be modified after discovery"); - discoveryRequestBuilder.suite(suiteClass); + "discovery request cannot be modified after discovery"); + discoveryRequestBuilder.applySelectorsAndFiltersFromSuite(suiteClass); return this; } SuiteTestDescriptor addDiscoveryRequestFrom(UniqueId uniqueId) { Preconditions.condition(launcherDiscoveryResult == null, - "discovery request can not be modified after discovery"); + "discovery request cannot be modified after discovery"); discoveryRequestBuilder.selectors(DiscoverySelectors.selectUniqueId(uniqueId)); return this; } @@ -92,6 +98,7 @@ void discover() { .filterStandardClassNamePatterns(true) .enableImplicitConfigurationParameters(false) .parentConfigurationParameters(configurationParameters) + .applyConfigurationParametersFromSuite(suiteClass) .build(); // @formatter:on this.launcher = SuiteLauncher.create(); @@ -120,16 +127,58 @@ private static String getSuiteDisplayName(Class testClass) { void execute(EngineExecutionListener parentEngineExecutionListener) { parentEngineExecutionListener.executionStarted(this); + ThrowableCollector throwableCollector = new OpenTest4JAwareThrowableCollector(); + + List beforeSuiteMethods = LifecycleMethodUtils.findBeforeSuiteMethods(suiteClass, throwableCollector); + List afterSuiteMethods = LifecycleMethodUtils.findAfterSuiteMethods(suiteClass, throwableCollector); + + executeBeforeSuiteMethods(beforeSuiteMethods, throwableCollector); + + TestExecutionSummary summary = executeTests(parentEngineExecutionListener, throwableCollector); + + executeAfterSuiteMethods(afterSuiteMethods, throwableCollector); + + TestExecutionResult testExecutionResult = computeTestExecutionResult(summary, throwableCollector); + parentEngineExecutionListener.executionFinished(this, testExecutionResult); + } + + private void executeBeforeSuiteMethods(List beforeSuiteMethods, ThrowableCollector throwableCollector) { + if (throwableCollector.isNotEmpty()) { + return; + } + for (Method beforeSuiteMethod : beforeSuiteMethods) { + throwableCollector.execute(() -> ReflectionUtils.invokeMethod(beforeSuiteMethod, null)); + if (throwableCollector.isNotEmpty()) { + return; + } + } + } + + private TestExecutionSummary executeTests(EngineExecutionListener parentEngineExecutionListener, + ThrowableCollector throwableCollector) { + if (throwableCollector.isNotEmpty()) { + return null; + } + // #2838: The discovery result from a suite may have been filtered by // post discovery filters from the launcher. The discovery result should - // be pruned accordingly + // be pruned accordingly. LauncherDiscoveryResult discoveryResult = this.launcherDiscoveryResult.withRetainedEngines( getChildren()::contains); - TestExecutionSummary summary = launcher.execute(discoveryResult, parentEngineExecutionListener); - parentEngineExecutionListener.executionFinished(this, computeTestExecutionResult(summary)); + return launcher.execute(discoveryResult, parentEngineExecutionListener); } - private TestExecutionResult computeTestExecutionResult(TestExecutionSummary summary) { + private void executeAfterSuiteMethods(List afterSuiteMethods, ThrowableCollector throwableCollector) { + for (Method afterSuiteMethod : afterSuiteMethods) { + throwableCollector.execute(() -> ReflectionUtils.invokeMethod(afterSuiteMethod, null)); + } + } + + private TestExecutionResult computeTestExecutionResult(TestExecutionSummary summary, + ThrowableCollector throwableCollector) { + if (throwableCollector.isNotEmpty()) { + return TestExecutionResult.failed(throwableCollector.getThrowable()); + } if (failIfNoTests && summary.getTestsFoundCount() == 0) { return TestExecutionResult.failed(new NoTestsDiscoveredException(suiteClass)); } @@ -139,8 +188,8 @@ private TestExecutionResult computeTestExecutionResult(TestExecutionSummary summ @Override public boolean mayRegisterTests() { // While a suite will not register new tests after discovery, we pretend - // it does. This allows the suite to fail if not tests were discovered. - // If not, the empty suite would be pruned. + // it does. This allows the suite to fail if no tests were discovered. + // Otherwise, the empty suite would be pruned. return true; } diff --git a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteTestEngine.java b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteTestEngine.java index 96b353392b27..b489870ba488 100644 --- a/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteTestEngine.java +++ b/junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteTestEngine.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-suite-engine/src/test/README.md b/junit-platform-suite-engine/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-suite-engine/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-platform-suite/src/test/README.md b/junit-platform-suite/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-suite/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Assertions.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Assertions.java index 74015e8818eb..30fbc6ba577a 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Assertions.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Assertions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EngineExecutionResults.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EngineExecutionResults.java index bce1db1c4a88..8d884d649300 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EngineExecutionResults.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EngineExecutionResults.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EngineTestKit.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EngineTestKit.java index ee4ab403e27f..23884df7577b 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EngineTestKit.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EngineTestKit.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Event.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Event.java index 63c69409b381..190a7aaf5510 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Event.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Event.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventConditions.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventConditions.java index 7f354b75b7f7..3431252cbef2 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventConditions.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventConditions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -141,7 +141,7 @@ public static Condition test() { * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor} is * a {@linkplain TestDescriptor#isContainer() container} and its * {@linkplain TestDescriptor#getUniqueId() unique id} contains the - * fully-qualified name of the supplied {@link Class}. + * fully qualified name of the supplied {@link Class}. */ public static Condition container(Class clazz) { Preconditions.notNull(clazz, "Class must not be null"); diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventStatistics.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventStatistics.java index fda7d1d8b2ef..bd1cf3e06262 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventStatistics.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventStatistics.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventType.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventType.java index 1fed5b9b67ba..09a6bdb59833 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventType.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventType.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -60,6 +60,6 @@ public enum EventType { * * @see org.junit.platform.engine.EngineExecutionListener#reportingEntryPublished(TestDescriptor, ReportEntry) */ - REPORTING_ENTRY_PUBLISHED; + REPORTING_ENTRY_PUBLISHED } diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Events.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Events.java index 01481ac30fce..256ea9f2d2d3 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Events.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Events.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Execution.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Execution.java index be14554e79b7..9b5a70846d83 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Execution.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Execution.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/ExecutionRecorder.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/ExecutionRecorder.java index 15db74b2b7d6..c7f42f5693af 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/ExecutionRecorder.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/ExecutionRecorder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Executions.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Executions.java index 6ef7a89e29d3..bbf1d2df1a3f 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Executions.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Executions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/TerminationInfo.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/TerminationInfo.java index e3875c8ce0d3..99035a3bdc16 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/TerminationInfo.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/TerminationInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/TestExecutionResultConditions.java b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/TestExecutionResultConditions.java index 21ff73e7e4b5..1ebbeabb875c 100644 --- a/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/TestExecutionResultConditions.java +++ b/junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/TestExecutionResultConditions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,9 +12,11 @@ import static java.util.function.Predicate.isEqual; import static java.util.stream.Collectors.toList; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.MAINTAINED; import static org.junit.platform.commons.util.FunctionUtils.where; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Predicate; @@ -22,6 +24,7 @@ import org.apiguardian.api.API; import org.assertj.core.api.Assertions; import org.assertj.core.api.Condition; +import org.junit.platform.commons.util.Preconditions; import org.junit.platform.engine.TestExecutionResult; import org.junit.platform.engine.TestExecutionResult.Status; @@ -69,6 +72,9 @@ public static Condition throwable(Condition... c * Create a new {@link Condition} that matches if and only if a * {@link Throwable}'s {@linkplain Throwable#getCause() cause} matches all * supplied conditions. + * + * @see #rootCause(Condition...) + * @see #suppressed(int, Condition...) */ @SafeVarargs @SuppressWarnings("varargs") @@ -80,10 +86,33 @@ public static Condition cause(Condition... conditions) { return Assertions.allOf(list); } + /** + * Create a new {@link Condition} that matches if and only if a + * {@link Throwable}'s root {@linkplain Throwable#getCause() cause} matches + * all supplied conditions. + * + * @since 1.11 + * @see #cause(Condition...) + * @see #suppressed(int, Condition...) + */ + @API(status = EXPERIMENTAL, since = "1.11") + @SafeVarargs + @SuppressWarnings("varargs") + public static Condition rootCause(Condition... conditions) { + List> list = Arrays.stream(conditions)// + .map(TestExecutionResultConditions::rootCause)// + .collect(toList()); + + return Assertions.allOf(list); + } + /** * Create a new {@link Condition} that matches if and only if a * {@link Throwable}'s {@linkplain Throwable#getSuppressed() suppressed * throwable} at the supplied index matches all supplied conditions. + * + * @see #cause(Condition...) + * @see #rootCause(Condition...) */ @SafeVarargs @SuppressWarnings("varargs") @@ -135,6 +164,36 @@ private static Condition cause(Condition condition) { condition); } + private static Condition rootCause(Condition condition) { + Predicate predicate = throwable -> { + Preconditions.notNull(throwable, "Throwable must not be null"); + Preconditions.notNull(throwable.getCause(), "Throwable does not have a cause"); + Throwable rootCause = getRootCause(throwable, new ArrayList<>()); + return condition.matches(rootCause); + }; + return new Condition<>(predicate, "throwable root cause matches %s", condition); + } + + /** + * Get the root cause of the supplied {@link Throwable}, or the supplied + * {@link Throwable} if it has no cause. + */ + private static Throwable getRootCause(Throwable throwable, List causeChain) { + // If we have already seen the current Throwable, that means we have + // encountered recursion in the cause chain and therefore return the last + // Throwable in the cause chain, which was the root cause before the recursion. + if (causeChain.contains(throwable)) { + return causeChain.get(causeChain.size() - 1); + } + Throwable cause = throwable.getCause(); + if (cause == null) { + return throwable; + } + // Track current Throwable before recursing. + causeChain.add(throwable); + return getRootCause(cause, causeChain); + } + private static Condition suppressed(int index, Condition condition) { return new Condition<>( throwable -> throwable.getSuppressed().length > index diff --git a/junit-platform-testkit/src/test/README.md b/junit-platform-testkit/src/test/README.md new file mode 100644 index 000000000000..6e2fd0b363f0 --- /dev/null +++ b/junit-platform-testkit/src/test/README.md @@ -0,0 +1 @@ +For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project. diff --git a/junit-vintage-engine/junit-vintage-engine.gradle.kts b/junit-vintage-engine/junit-vintage-engine.gradle.kts index 288b3b184075..950aed698a14 100644 --- a/junit-vintage-engine/junit-vintage-engine.gradle.kts +++ b/junit-vintage-engine/junit-vintage-engine.gradle.kts @@ -74,7 +74,7 @@ tasks { !it.name.startsWith("junit-4") } } - withType().matching { it.name != testWithoutJUnit4.name }.configureEach { + withType().named { it != testWithoutJUnit4.name }.configureEach { (options as JUnitPlatformOptions).apply { excludeTags("missing-junit4") } diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/JUnit4VersionCheck.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/JUnit4VersionCheck.java index 8c5f1f276015..bcc7d128b935 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/JUnit4VersionCheck.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/JUnit4VersionCheck.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/VintageTestEngine.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/VintageTestEngine.java index fb52f79bf06e..be13d89580a7 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/VintageTestEngine.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/VintageTestEngine.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/DescriptionUtils.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/DescriptionUtils.java index 3cc89c7e33e8..85e9c0b51a5d 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/DescriptionUtils.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/DescriptionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/OrFilter.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/OrFilter.java index 5bf9bf713ee5..4826c67faa78 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/OrFilter.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/OrFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerDecorator.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerDecorator.java index d81b64f1f41c..01f890fd50ae 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerDecorator.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerRequest.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerRequest.java index bb9037c2b7e0..414ddc2b4955 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerRequest.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerTestDescriptor.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerTestDescriptor.java index 0ad3c5f9b6a0..bd20da74a3d2 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerTestDescriptor.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -43,12 +43,14 @@ public class RunnerTestDescriptor extends VintageTestDescriptor { private final Set rejectedExclusions = new HashSet<>(); private Runner runner; + private final boolean ignored; private boolean wasFiltered; private List filters = new ArrayList<>(); - public RunnerTestDescriptor(UniqueId uniqueId, Class testClass, Runner runner) { + public RunnerTestDescriptor(UniqueId uniqueId, Class testClass, Runner runner, boolean ignored) { super(uniqueId, runner.getDescription(), testClass.getSimpleName(), ClassSource.from(testClass)); this.runner = runner; + this.ignored = ignored; } @Override @@ -155,6 +157,10 @@ private Runner getRunnerToReport() { return (runner instanceof RunnerDecorator) ? ((RunnerDecorator) runner).getDecoratedRunner() : runner; } + public boolean isIgnored() { + return ignored; + } + private static class ExcludeDescriptionFilter extends Filter { private final Description description; diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/TestSourceProvider.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/TestSourceProvider.java index e5b327a2d1dc..3797f1014209 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/TestSourceProvider.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/TestSourceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/VintageEngineDescriptor.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/VintageEngineDescriptor.java index 9e0450fa9a01..26356fdfe9f7 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/VintageEngineDescriptor.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/VintageEngineDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/VintageTestDescriptor.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/VintageTestDescriptor.java index d8a64ba7c293..89435681207c 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/VintageTestDescriptor.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/VintageTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/ClassSelectorResolver.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/ClassSelectorResolver.java index a2d1e7d4c80c..6db805e7968b 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/ClassSelectorResolver.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/ClassSelectorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -26,7 +26,6 @@ import org.junit.platform.engine.discovery.UniqueIdSelector; import org.junit.platform.engine.support.discovery.SelectorResolver; import org.junit.runner.Runner; -import org.junit.runners.model.RunnerBuilder; import org.junit.vintage.engine.descriptor.RunnerTestDescriptor; /** @@ -34,7 +33,7 @@ */ class ClassSelectorResolver implements SelectorResolver { - private static final RunnerBuilder RUNNER_BUILDER = new DefensiveAllDefaultPossibilitiesBuilder(); + private static final DefensiveAllDefaultPossibilitiesBuilder RUNNER_BUILDER = new DefensiveAllDefaultPossibilitiesBuilder(); private final ClassFilter classFilter; @@ -76,7 +75,7 @@ private Resolution resolveTestClass(Class testClass, Context context) { private RunnerTestDescriptor createRunnerTestDescriptor(TestDescriptor parent, Class testClass, Runner runner) { UniqueId uniqueId = parent.getUniqueId().append(SEGMENT_TYPE_RUNNER, testClass.getName()); - return new RunnerTestDescriptor(uniqueId, testClass, runner); + return new RunnerTestDescriptor(uniqueId, testClass, runner, RUNNER_BUILDER.isIgnored(runner)); } } diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/DefensiveAllDefaultPossibilitiesBuilder.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/DefensiveAllDefaultPossibilitiesBuilder.java index 0da94f689fde..5d03e0a64133 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/DefensiveAllDefaultPossibilitiesBuilder.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/DefensiveAllDefaultPossibilitiesBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -64,6 +64,10 @@ public Runner runnerForClass(Class testClass) throws Throwable { return runner; } + boolean isIgnored(Runner runner) { + return runner instanceof IgnoredClassRunner || runner instanceof IgnoringRunnerDecorator; + } + /** * Instead of checking for the {@link Ignore} annotation and returning an * {@link IgnoredClassRunner} from {@link IgnoredBuilder}, we've let the @@ -72,7 +76,7 @@ public Runner runnerForClass(Class testClass) throws Throwable { * override its runtime behavior (i.e. skip execution) but return its * regular {@link org.junit.runner.Description}. */ - private Runner decorateIgnoredTestClass(Runner runner) { + private IgnoringRunnerDecorator decorateIgnoredTestClass(Runner runner) { if (runner instanceof Filterable) { return new FilterableIgnoringRunnerDecorator(runner); } diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/FilterableIgnoringRunnerDecorator.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/FilterableIgnoringRunnerDecorator.java index 636ef2554802..636c9a28ee27 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/FilterableIgnoringRunnerDecorator.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/FilterableIgnoringRunnerDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IgnoringRunnerDecorator.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IgnoringRunnerDecorator.java index 3b5597e51169..d95c1d68e57b 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IgnoringRunnerDecorator.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IgnoringRunnerDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestClass.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestClass.java index 91b9b1837adb..0d1f04831598 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestClass.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestMethod.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestMethod.java index d382a606496d..75352330d1c2 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestMethod.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/MethodSelectorResolver.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/MethodSelectorResolver.java index 724e9a0231f9..f3227d857526 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/MethodSelectorResolver.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/MethodSelectorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/RunnerTestDescriptorPostProcessor.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/RunnerTestDescriptorPostProcessor.java index b9948537521a..e124236e1ac2 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/RunnerTestDescriptorPostProcessor.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/RunnerTestDescriptorPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/UniqueIdFilter.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/UniqueIdFilter.java index 24685c55aed6..400d64b52f0b 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/UniqueIdFilter.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/UniqueIdFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/VintageDiscoverer.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/VintageDiscoverer.java index 427526bfeb14..a8868df3abd4 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/VintageDiscoverer.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/VintageDiscoverer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/EventType.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/EventType.java index 949cc4363330..75177f5527b4 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/EventType.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/EventType.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/RunListenerAdapter.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/RunListenerAdapter.java index 9cefb8faee30..63d415bd1c11 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/RunListenerAdapter.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/RunListenerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -20,6 +20,7 @@ import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.TestExecutionResult; import org.junit.platform.engine.UniqueId; +import org.junit.platform.engine.support.descriptor.ClassSource; import org.junit.runner.Description; import org.junit.runner.Result; import org.junit.runner.notification.Failure; @@ -49,7 +50,7 @@ class RunListenerAdapter extends RunListener { @Override public void testRunStarted(Description description) { - if (description.isSuite() && description.getAnnotation(Ignore.class) == null) { + if (description.isSuite() && !testRun.getRunnerTestDescriptor().isIgnored()) { fireExecutionStarted(testRun.getRunnerTestDescriptor(), EventType.REPORTED); } } @@ -65,7 +66,9 @@ public void testSuiteStarted(Description description) { @Override public void testIgnored(Description description) { - testIgnored(lookupOrRegisterNextTestDescriptor(description), determineReasonForIgnoredTest(description)); + TestDescriptor testDescriptor = lookupOrRegisterNextTestDescriptor(description); + String reason = determineReasonForIgnoredTest(testDescriptor, description).orElse(""); + testIgnored(testDescriptor, reason); } @Override @@ -176,9 +179,21 @@ private void testIgnored(TestDescriptor testDescriptor, String reason) { fireExecutionSkipped(testDescriptor, reason); } - private String determineReasonForIgnoredTest(Description description) { - Ignore ignoreAnnotation = description.getAnnotation(Ignore.class); - return Optional.ofNullable(ignoreAnnotation).map(Ignore::value).orElse(""); + private Optional determineReasonForIgnoredTest(TestDescriptor testDescriptor, Description description) { + Optional reason = getReason(description.getAnnotation(Ignore.class)); + if (reason.isPresent()) { + return reason; + } + // Workaround for some runners (e.g. JUnit38ClassRunner) don't include the @Ignore annotation + // in the description, so we read it from the test class directly + return testDescriptor.getSource() // + .filter(ClassSource.class::isInstance) // + .map(source -> ((ClassSource) source).getJavaClass()) // + .flatMap(testClass -> getReason(testClass.getAnnotation(Ignore.class))); + } + + private static Optional getReason(Ignore annotation) { + return Optional.ofNullable(annotation).map(Ignore::value); } private void dynamicTestRegistered(TestDescriptor testDescriptor) { diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/RunnerExecutor.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/RunnerExecutor.java index 23fad4dea5c5..cb0a1d5c35ee 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/RunnerExecutor.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/RunnerExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/TestRun.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/TestRun.java index 1e46e0e4a9e3..bf0b160b3a83 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/TestRun.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/TestRun.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/support/UniqueIdReader.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/support/UniqueIdReader.java index 35a90736a831..7e396d74bcaf 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/support/UniqueIdReader.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/support/UniqueIdReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/support/UniqueIdStringifier.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/support/UniqueIdStringifier.java index 6c2846e1ac47..c0fc50aadb2c 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/support/UniqueIdStringifier.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/support/UniqueIdStringifier.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/JUnit4ParameterizedTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/JUnit4ParameterizedTests.java index bb533dbc1fc5..7ef1faa72686 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/JUnit4ParameterizedTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/JUnit4ParameterizedTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/JUnit4VersionCheckTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/JUnit4VersionCheckTests.java index 4df0e86a7ba5..af9002c615c6 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/JUnit4VersionCheckTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/JUnit4VersionCheckTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageLauncherIntegrationTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageLauncherIntegrationTests.java index cb12669aef13..40b0db53a478 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageLauncherIntegrationTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageLauncherIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -151,9 +151,8 @@ void removesCompleteClassIfItHasExcludedTag() { .containsExactly("JUnit Vintage"); } - @TrackLogRecords @Test - void executesAllTestsForNotFilterableRunner(LogRecordListener logRecordListener) { + void executesAllTestsForNotFilterableRunner(@TrackLogRecords LogRecordListener logRecordListener) { Class testClass = JUnit4TestCaseWithNotFilterableRunner.class; var request = request() // .selectors(selectClass(testClass)) // @@ -172,9 +171,8 @@ void executesAllTestsForNotFilterableRunner(LogRecordListener logRecordListener) + " does not support filtering and will therefore be run completely."); } - @TrackLogRecords @Test - void executesAllTestsForNotFilterableChildRunnerOfSuite(LogRecordListener logRecordListener) { + void executesAllTestsForNotFilterableChildRunnerOfSuite(@TrackLogRecords LogRecordListener logRecordListener) { Class suiteClass = JUnit4SuiteOfSuiteWithFilterableChildRunner.class; Class testClass = JUnit4TestCaseWithNotFilterableRunner.class; var request = request() // @@ -194,9 +192,9 @@ void executesAllTestsForNotFilterableChildRunnerOfSuite(LogRecordListener logRec + " was not able to satisfy all filter requests."); } - @TrackLogRecords @Test - void executesAllTestsWhenFilterDidNotExcludeTestForJUnit3Suite(LogRecordListener logRecordListener) { + void executesAllTestsWhenFilterDidNotExcludeTestForJUnit3Suite( + @TrackLogRecords LogRecordListener logRecordListener) { Class suiteClass = JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails.class; Class testClass = PlainJUnit3TestCaseWithSingleTestWhichFails.class; var request = request() // diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineBasicTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineBasicTests.java index e78df198bc8c..c51b0875fd88 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineBasicTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineBasicTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineDiscoveryTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineDiscoveryTests.java index b89470cf92bc..b59d68144595 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineDiscoveryTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineDiscoveryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineExecutionTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineExecutionTests.java index 66af815aff99..dee38a4fec62 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineExecutionTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineExecutionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,7 @@ package org.junit.vintage.engine; +import static java.util.function.Predicate.isEqual; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; @@ -56,8 +57,10 @@ import org.junit.runner.notification.RunNotifier; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; +import org.junit.vintage.engine.samples.junit3.IgnoredJUnit3TestCase; import org.junit.vintage.engine.samples.junit3.JUnit3ParallelSuiteWithSubsuites; import org.junit.vintage.engine.samples.junit3.JUnit3SuiteWithSubsuites; +import org.junit.vintage.engine.samples.junit3.JUnit4SuiteWithIgnoredJUnit3TestCase; import org.junit.vintage.engine.samples.junit3.PlainJUnit3TestCaseWithSingleTestWhichFails; import org.junit.vintage.engine.samples.junit4.CompletelyDynamicTestCase; import org.junit.vintage.engine.samples.junit4.EmptyIgnoredTestCase; @@ -746,7 +749,7 @@ void executesJUnit4TestCaseWithErrorCollectorStoringMultipleFailures() { instanceOf(MultipleFailuresError.class), // new Condition<>(throwable -> ((MultipleFailuresError) throwable).getFailures().size() == 3, "MultipleFailuresError must contain 3 failures"), // - new Condition<>(throwable -> ((MultipleFailuresError) throwable).getSuppressed().length == 3, + new Condition<>(throwable -> throwable.getSuppressed().length == 3, "MultipleFailuresError must contain 3 suppressed exceptions")// )), // event(container(testClass), finishedSuccessfully()), // @@ -896,6 +899,27 @@ void executesRegularSpockFeatureMethod() { event(engine(), finishedSuccessfully())); } + @Test + void executesIgnoredJUnit3TestCase() { + var suiteClass = IgnoredJUnit3TestCase.class; + execute(suiteClass).allEvents().assertEventsMatchExactly( // + event(engine(), started()), // + event(container(suiteClass), skippedWithReason(isEqual("testing"))), // + event(engine(), finishedSuccessfully())); + } + + @Test + void executesJUnit4SuiteWithIgnoredJUnit3TestCase() { + var suiteClass = JUnit4SuiteWithIgnoredJUnit3TestCase.class; + var testClass = IgnoredJUnit3TestCase.class; + execute(suiteClass).allEvents().assertEventsMatchExactly( // + event(engine(), started()), // + event(container(suiteClass), started()), // + event(container(testClass), skippedWithReason(isEqual("testing"))), // + event(container(suiteClass), finishedSuccessfully()), // + event(engine(), finishedSuccessfully())); + } + private static EngineExecutionResults execute(Class testClass) { return execute(request(testClass)); } diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineTestSuite.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineTestSuite.java index 62f9f6c80897..1de375649de0 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineTestSuite.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageUniqueIdBuilder.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageUniqueIdBuilder.java index 88fdd91502a9..92a9e1bc0544 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageUniqueIdBuilder.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageUniqueIdBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/DescriptionUtilsTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/DescriptionUtilsTests.java index 10247cb75e30..4401da092743 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/DescriptionUtilsTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/DescriptionUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -49,8 +49,8 @@ private Stream toDynamicTests(Class testClass) { Stream toDynamicTests(Stream children) { return children.map(description -> description.isTest() // - ? toDynamicTest(description, "child: " + description.toString()) // - : dynamicContainer("class: " + description.toString(), Stream.concat( // + ? toDynamicTest(description, "child: " + description) // + : dynamicContainer("class: " + description, Stream.concat( // Stream.of(toDynamicTest(description, "self")), // toDynamicTests(description.getChildren().stream())))); } diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/OrFilterTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/OrFilterTests.java index 572b4e7f5ba9..4cc9c5c856fe 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/OrFilterTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/OrFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/TestSourceProviderTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/TestSourceProviderTests.java index 8211f75983e2..b182da828175 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/TestSourceProviderTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/TestSourceProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/VintageTestDescriptorTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/VintageTestDescriptorTests.java index 58be5a839128..349cdc8fb409 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/VintageTestDescriptorTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/VintageTestDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestClassTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestClassTests.java index 442daa53d3d8..f19516a527c6 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestClassTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestClassTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/RunnerTestDescriptorPostProcessorTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/RunnerTestDescriptorPostProcessorTests.java index 6b64d1afa10f..01733137bab1 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/RunnerTestDescriptorPostProcessorTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/RunnerTestDescriptorPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/VintageDiscovererTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/VintageDiscovererTests.java index 754c34b03a3d..806e69769556 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/VintageDiscovererTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/VintageDiscovererTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/execution/TestRunTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/execution/TestRunTests.java index 69272fd511db..cea6d3c6ee86 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/execution/TestRunTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/execution/TestRunTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -32,7 +32,8 @@ class TestRunTests { void returnsEmptyOptionalForUnknownDescriptions() throws Exception { Class testClass = PlainJUnit4TestCaseWithSingleTestWhichFails.class; var runnerId = engineId().append(SEGMENT_TYPE_RUNNER, testClass.getName()); - var runnerTestDescriptor = new RunnerTestDescriptor(runnerId, testClass, new BlockJUnit4ClassRunner(testClass)); + var runnerTestDescriptor = new RunnerTestDescriptor(runnerId, testClass, new BlockJUnit4ClassRunner(testClass), + false); var unknownDescription = createTestDescription(testClass, "dynamicTest"); var testRun = new TestRun(runnerTestDescriptor); @@ -45,7 +46,8 @@ void returnsEmptyOptionalForUnknownDescriptions() throws Exception { void registersDynamicTestDescriptors() throws Exception { Class testClass = PlainJUnit4TestCaseWithSingleTestWhichFails.class; var runnerId = engineId().append(SEGMENT_TYPE_RUNNER, testClass.getName()); - var runnerTestDescriptor = new RunnerTestDescriptor(runnerId, testClass, new BlockJUnit4ClassRunner(testClass)); + var runnerTestDescriptor = new RunnerTestDescriptor(runnerId, testClass, new BlockJUnit4ClassRunner(testClass), + false); var dynamicTestId = runnerId.append(SEGMENT_TYPE_DYNAMIC, "dynamicTest"); var dynamicDescription = createTestDescription(testClass, "dynamicTest"); var dynamicTestDescriptor = new VintageTestDescriptor(dynamicTestId, dynamicDescription, null); diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/support/UniqueIdReaderTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/support/UniqueIdReaderTests.java index 3ea9cca2abc8..b2eff398baa9 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/support/UniqueIdReaderTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/support/UniqueIdReaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/support/UniqueIdStringifierTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/support/UniqueIdStringifierTests.java index 6ec8e41a0b36..3d8cc0af798c 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/support/UniqueIdStringifierTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/support/UniqueIdStringifierTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/PlainOldJavaClassWithoutAnyTestsTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/PlainOldJavaClassWithoutAnyTestsTestCase.java index cad6044ab55e..fff58931677f 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/PlainOldJavaClassWithoutAnyTestsTestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/PlainOldJavaClassWithoutAnyTestsTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/AbstractJUnit3TestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/AbstractJUnit3TestCase.java index ec7f01e9322c..46bbea61fcb4 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/AbstractJUnit3TestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/AbstractJUnit3TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/IgnoredJUnit3TestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/IgnoredJUnit3TestCase.java new file mode 100644 index 000000000000..e29562595ead --- /dev/null +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/IgnoredJUnit3TestCase.java @@ -0,0 +1,28 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.vintage.engine.samples.junit3; + +import junit.framework.TestCase; + +import org.junit.Assert; +import org.junit.Ignore; + +/** + * @since 4.12 + */ +@Ignore("testing") +public class IgnoredJUnit3TestCase extends TestCase { + + public void test() { + Assert.fail("this test should be ignored"); + } + +} diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3ParallelSuiteWithSubsuites.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3ParallelSuiteWithSubsuites.java index 0171e86ed846..5157c622dcce 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3ParallelSuiteWithSubsuites.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3ParallelSuiteWithSubsuites.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails.java index a253e2f4d571..bf0274e2edd2 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3SuiteWithSubsuites.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3SuiteWithSubsuites.java index cb070d7738f7..3c11ed125bbb 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3SuiteWithSubsuites.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3SuiteWithSubsuites.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit4SuiteWithIgnoredJUnit3TestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit4SuiteWithIgnoredJUnit3TestCase.java new file mode 100644 index 000000000000..288c26e92085 --- /dev/null +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit4SuiteWithIgnoredJUnit3TestCase.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.vintage.engine.samples.junit3; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({ IgnoredJUnit3TestCase.class }) +public class JUnit4SuiteWithIgnoredJUnit3TestCase { +} diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/PlainJUnit3TestCaseWithSingleTestWhichFails.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/PlainJUnit3TestCaseWithSingleTestWhichFails.java index 17918c65bb5a..932aac5156f7 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/PlainJUnit3TestCaseWithSingleTestWhichFails.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/PlainJUnit3TestCaseWithSingleTestWhichFails.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/AbstractJUnit4TestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/AbstractJUnit4TestCase.java index 6c18d8fbee90..6a88673df784 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/AbstractJUnit4TestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/AbstractJUnit4TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/AbstractJunit4TestCaseWithConstructorParameter.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/AbstractJunit4TestCaseWithConstructorParameter.java index b25e11801918..8e5e40eb21eb 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/AbstractJunit4TestCaseWithConstructorParameter.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/AbstractJunit4TestCaseWithConstructorParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/Categories.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/Categories.java index 9cc5794815a8..ba49bbe39914 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/Categories.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/Categories.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/CompletelyDynamicTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/CompletelyDynamicTestCase.java index 2331a56b6037..e435cd0e682f 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/CompletelyDynamicTestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/CompletelyDynamicTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ConcreteJUnit4TestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ConcreteJUnit4TestCase.java index e659b5953a20..a717a96cf578 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ConcreteJUnit4TestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ConcreteJUnit4TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ConfigurableRunner.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ConfigurableRunner.java index 47400e9465da..c10e61b28a94 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ConfigurableRunner.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ConfigurableRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/DynamicRunner.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/DynamicRunner.java index 20f3238abe0b..6ebfb7e46625 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/DynamicRunner.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/DynamicRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EmptyIgnoredTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EmptyIgnoredTestCase.java index 69f53e2d9956..ade1340dae5c 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EmptyIgnoredTestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EmptyIgnoredTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EnclosedJUnit4TestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EnclosedJUnit4TestCase.java index dec70cc9c8a3..b315b15b798f 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EnclosedJUnit4TestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EnclosedJUnit4TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EnclosedWithParameterizedChildrenJUnit4TestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EnclosedWithParameterizedChildrenJUnit4TestCase.java index dce4f50c723a..3305da289100 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EnclosedWithParameterizedChildrenJUnit4TestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EnclosedWithParameterizedChildrenJUnit4TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ExceptionThrowingRunner.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ExceptionThrowingRunner.java index 146d659ca116..00ee38f1eb7f 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ExceptionThrowingRunner.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ExceptionThrowingRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredJUnit4TestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredJUnit4TestCase.java index 19d53de56896..9a9f860cfe07 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredJUnit4TestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredJUnit4TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredJUnit4TestCaseWithNotFilterableRunner.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredJUnit4TestCaseWithNotFilterableRunner.java index dd19ad0ad46b..4a491c253c24 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredJUnit4TestCaseWithNotFilterableRunner.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredJUnit4TestCaseWithNotFilterableRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredParameterizedTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredParameterizedTestCase.java index 0c127b5bc363..d2625c1e5bc3 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredParameterizedTestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredParameterizedTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4ParameterizedTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4ParameterizedTestCase.java index 705f2b891915..fe05d745fc61 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4ParameterizedTestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4ParameterizedTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithFilterableChildRunner.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithFilterableChildRunner.java index 5b8a1284a004..9955d8e10806 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithFilterableChildRunner.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithFilterableChildRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithIgnoredJUnit4TestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithIgnoredJUnit4TestCase.java index 04c4aed2ed34..dbfd835d67ee 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithIgnoredJUnit4TestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithIgnoredJUnit4TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.java index 271cda1804c0..d81e679e8ee7 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithJUnit4TestCaseWithErrorInBeforeClass.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithJUnit4TestCaseWithErrorInBeforeClass.java index 66b0273f44a6..cab393ed7b4c 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithJUnit4TestCaseWithErrorInBeforeClass.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithJUnit4TestCaseWithErrorInBeforeClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithExceptionThrowingRunner.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithExceptionThrowingRunner.java index 01dd3aebddd5..f6fc59535ee8 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithExceptionThrowingRunner.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithExceptionThrowingRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithIgnoredJUnit4TestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithIgnoredJUnit4TestCase.java index c1f1279a5053..c0b7351e9c3c 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithIgnoredJUnit4TestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithIgnoredJUnit4TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit3SuiteWithSingleTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit3SuiteWithSingleTestCase.java index aa4c409480a8..8d055eb75720 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit3SuiteWithSingleTestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit3SuiteWithSingleTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.java index 6f2e3673091c..2bffda246870 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithErrorInBeforeClass.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithErrorInBeforeClass.java index 56bc1bd23452..2d2a0eff61a9 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithErrorInBeforeClass.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithErrorInBeforeClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.java index 7e4dc1b0d22a..3477bc366a72 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.java index b784ffde17fd..6f526104e679 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored.java index a6e652024510..3b8fd42c965a 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithTwoTestCases.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithTwoTestCases.java index 4cf0eed133d1..498b80d20d5b 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithTwoTestCases.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithTwoTestCases.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithAssumptionFailureInBeforeClass.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithAssumptionFailureInBeforeClass.java index f06d3fe81998..f628dbd5f5ed 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithAssumptionFailureInBeforeClass.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithAssumptionFailureInBeforeClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithDistinguishableOverloadedMethod.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithDistinguishableOverloadedMethod.java index 10f77db2df12..553ab1031f7b 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithDistinguishableOverloadedMethod.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithDistinguishableOverloadedMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorCollectorStoringMultipleFailures.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorCollectorStoringMultipleFailures.java index 090e04011b10..b47efc989451 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorCollectorStoringMultipleFailures.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorCollectorStoringMultipleFailures.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorInAfterClass.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorInAfterClass.java index 2cb44e601759..00903b8b1999 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorInAfterClass.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorInAfterClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorInBeforeClass.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorInBeforeClass.java index dd3afe5eb616..53bd47ed80d9 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorInBeforeClass.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorInBeforeClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithExceptionThrowingRunner.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithExceptionThrowingRunner.java index 443296756b6b..3fdde38963bc 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithExceptionThrowingRunner.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithExceptionThrowingRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.java index b1816d2ad44a..f81696ad4fe0 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithIndistinguishableOverloadedMethod.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithIndistinguishableOverloadedMethod.java index 6d4099db0615..e590208ccf11 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithIndistinguishableOverloadedMethod.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithIndistinguishableOverloadedMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithNotFilterableRunner.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithNotFilterableRunner.java index 9ece2415312f..a36112ba5c26 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithNotFilterableRunner.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithNotFilterableRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.java index 8906ce6864a3..1fccc00f7819 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithRunnerWithDuplicateChangingChildDescriptions.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithRunnerWithDuplicateChangingChildDescriptions.java index aa039daf1a65..9b82ffc916c0 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithRunnerWithDuplicateChangingChildDescriptions.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithRunnerWithDuplicateChangingChildDescriptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/MalformedJUnit4TestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/MalformedJUnit4TestCase.java index 87480a21960b..d3c318003317 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/MalformedJUnit4TestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/MalformedJUnit4TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/NotFilterableRunner.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/NotFilterableRunner.java index 4eb6bea4069e..5fcdfca51ef8 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/NotFilterableRunner.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/NotFilterableRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedTestCase.java index ec09442767f3..eae9c787c323 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedTestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedTimingTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedTimingTestCase.java index a9905cbe96fd..9330453cd681 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedTimingTestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedTimingTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedWithAfterParamFailureTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedWithAfterParamFailureTestCase.java index a6852f992030..73c6219695eb 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedWithAfterParamFailureTestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedWithAfterParamFailureTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedWithBeforeParamFailureTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedWithBeforeParamFailureTestCase.java index 515f8d4ea633..685e231dff94 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedWithBeforeParamFailureTestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedWithBeforeParamFailureTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithFiveTestMethods.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithFiveTestMethods.java index 08fe68db3f08..fb23515ea145 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithFiveTestMethods.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithFiveTestMethods.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithLifecycleMethods.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithLifecycleMethods.java index c629a21707a5..12c24a541271 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithLifecycleMethods.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithLifecycleMethods.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleInheritedTestWhichFails.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleInheritedTestWhichFails.java index 6ac3601bf9d6..2d7805ec5d71 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleInheritedTestWhichFails.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleInheritedTestWhichFails.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleTestWhichFails.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleTestWhichFails.java index de6ac2b1ac0e..c9b165c261b6 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleTestWhichFails.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleTestWhichFails.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleTestWhichIsIgnored.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleTestWhichIsIgnored.java index fb80eb65be49..697a8ac1842d 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleTestWhichIsIgnored.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleTestWhichIsIgnored.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithTwoTestMethods.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithTwoTestMethods.java index ffd2cdc0c0cc..8055a7cee501 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithTwoTestMethods.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithTwoTestMethods.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/RunnerThatOnlyReportsFailures.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/RunnerThatOnlyReportsFailures.java index dd94df329162..29d379200443 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/RunnerThatOnlyReportsFailures.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/RunnerThatOnlyReportsFailures.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/RunnerWithCustomUniqueIdsAndDisplayNames.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/RunnerWithCustomUniqueIdsAndDisplayNames.java index 1cf2140d57cd..bdf04f25ff2f 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/RunnerWithCustomUniqueIdsAndDisplayNames.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/RunnerWithCustomUniqueIdsAndDisplayNames.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -66,8 +66,7 @@ public CustomUniqueId(String testName) { @Override public boolean equals(Object obj) { - if (obj instanceof CustomUniqueId) { - var that = (CustomUniqueId) obj; + if (obj instanceof CustomUniqueId that) { return Objects.equals(this.testName, that.testName); } return false; diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/SingleFailingTheoryTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/SingleFailingTheoryTestCase.java index 1718b50acbcf..37f7671e7169 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/SingleFailingTheoryTestCase.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/SingleFailingTheoryTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/TestCaseRunWithJUnitPlatformRunner.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/TestCaseRunWithJUnitPlatformRunner.java index e4074c2f4fa7..2a724e128f12 100644 --- a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/TestCaseRunWithJUnitPlatformRunner.java +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/TestCaseRunWithJUnitPlatformRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/jupiter-tests/jupiter-tests.gradle.kts b/jupiter-tests/jupiter-tests.gradle.kts new file mode 100644 index 000000000000..3e55acc2371d --- /dev/null +++ b/jupiter-tests/jupiter-tests.gradle.kts @@ -0,0 +1,38 @@ +import org.gradle.api.tasks.PathSensitivity.RELATIVE + +plugins { + id("junitbuild.code-generator") + id("junitbuild.kotlin-library-conventions") + id("junitbuild.junit4-compatibility") + id("junitbuild.testing-conventions") + groovy +} + +dependencies { + testImplementation(projects.junitJupiter) + testImplementation(projects.junitJupiterMigrationsupport) + testImplementation(projects.junitPlatformLauncher) + testImplementation(projects.junitPlatformSuiteEngine) + testImplementation(projects.junitPlatformTestkit) + testImplementation(testFixtures(projects.junitPlatformCommons)) + testImplementation(kotlin("stdlib")) + testImplementation(libs.jimfs) + testImplementation(libs.junit4) + testImplementation(libs.kotlinx.coroutines) + testImplementation(libs.groovy4) + testImplementation(libs.memoryfilesystem) + testImplementation(testFixtures(projects.junitJupiterApi)) + testImplementation(testFixtures(projects.junitJupiterEngine)) +} + +tasks { + test { + inputs.dir("src/test/resources").withPathSensitivity(RELATIVE) + systemProperty("developmentVersion", version) + } + test_4_12 { + filter { + includeTestsMatching("org.junit.jupiter.migrationsupport.*") + } + } +} diff --git a/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/DisabledOnJreConditionTests.java.jte b/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/DisabledOnJreConditionTests.java.jte new file mode 100644 index 000000000000..fc4d8884be75 --- /dev/null +++ b/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/DisabledOnJreConditionTests.java.jte @@ -0,0 +1,99 @@ +@import java.util.List +@import junitbuild.generator.model.JRE + +@param List jres +@param List jresSortedByStringValue +@param String licenseHeader +${licenseHeader} +package org.junit.jupiter.api.condition; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +@for(var jre : jresSortedByStringValue)<%-- +--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava${jre.getVersion()}; +@endfor<%-- +--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.platform.commons.PreconditionViolationException; + +/** + * Unit tests for {@link DisabledOnJreCondition}. + * + *

    Note that test method names MUST match the test method names in + * {@link DisabledOnJreIntegrationTests}. + * + * @since 5.1 + */ +class DisabledOnJreConditionTests extends AbstractExecutionConditionTests { + + @Override + protected ExecutionCondition getExecutionCondition() { + return new DisabledOnJreCondition(); + } + + @Override + protected Class getTestClass() { + return DisabledOnJreIntegrationTests.class; + } + + /** + * @see DisabledOnJreIntegrationTests#enabledBecauseAnnotationIsNotPresent() + */ + @Test + void enabledBecauseAnnotationIsNotPresent() { + evaluateCondition(); + assertEnabled(); + assertReasonContains("@DisabledOnJre is not present"); + } + + /** + * @see DisabledOnJreIntegrationTests#missingJreDeclaration() + */ + @Test + void missingJreDeclaration() { + Exception exception = assertThrows(PreconditionViolationException.class, this::evaluateCondition); + assertThat(exception).hasMessageContaining("You must declare at least one JRE"); + } + + /** + * @see DisabledOnJreIntegrationTests#disabledOnAllJavaVersions() + */ + @Test + void disabledOnAllJavaVersions() { + evaluateCondition(); + assertDisabledOnCurrentJreIf(true); + assertCustomDisabledReasonIs("Disabled on every JRE"); + } +@for(var jre : jres) + /** + * @see DisabledOnJreIntegrationTests#java${jre.getVersion()}() + */ + @Test + void java${jre.getVersion()}() { + evaluateCondition(); + assertDisabledOnCurrentJreIf(onJava${jre.getVersion()}()); + } +@endfor + /** + * @see DisabledOnJreIntegrationTests#other() + */ + @Test + void other() { + evaluateCondition(); + assertDisabledOnCurrentJreIf(!onKnownVersion()); + } + + private void assertDisabledOnCurrentJreIf(boolean condition) { + if (condition) { + assertDisabled(); + assertReasonContains("Disabled on JRE version: " + System.getProperty("java.version")); + } + else { + assertEnabled(); + assertReasonContains("Enabled on JRE version: " + System.getProperty("java.version")); + } + } + +} diff --git a/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/DisabledOnJreIntegrationTests.java.jte b/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/DisabledOnJreIntegrationTests.java.jte new file mode 100644 index 000000000000..6d86cd5c670b --- /dev/null +++ b/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/DisabledOnJreIntegrationTests.java.jte @@ -0,0 +1,66 @@ +@import java.util.List +@import junitbuild.generator.model.JRE + +@param List jres +@param List jresSortedByStringValue +@param String licenseHeader +${licenseHeader} +package org.junit.jupiter.api.condition; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +@for(var jre : jresSortedByStringValue)<%-- +--%>import static org.junit.jupiter.api.condition.JRE.JAVA_${jre.getVersion()}; +@endfor<%-- +--%>import static org.junit.jupiter.api.condition.JRE.OTHER; +@for(var jre : jresSortedByStringValue)<%-- +--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava${jre.getVersion()}; +@endfor<%-- +--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Integration tests for {@link DisabledOnJre}. + * + * @since 5.1 + */ +class DisabledOnJreIntegrationTests { + + @Test + @Disabled("Only used in a unit test via reflection") + void enabledBecauseAnnotationIsNotPresent() { + } + + @Test + @Disabled("Only used in a unit test via reflection") + @DisabledOnJre({}) + void missingJreDeclaration() { + } + + @Test + @DisabledOnJre(disabledReason = "Disabled on every JRE", value = { // +@for(var jre : jres)<%-- +--%> JAVA_${jre.getVersion()}, // +@endfor<%-- +--%> OTHER // + }) + void disabledOnAllJavaVersions() { + fail("should be disabled"); + } +@for(var jre : jres) + @Test + @DisabledOnJre(JAVA_${jre.getVersion()}) + void java${jre.getVersion()}() { + assertFalse(onJava${jre.getVersion()}()); + } +@endfor + @Test + @DisabledOnJre(OTHER) + void other() { + assertTrue(onKnownVersion()); + } + +} diff --git a/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/EnabledOnJreConditionTests.java.jte b/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/EnabledOnJreConditionTests.java.jte new file mode 100644 index 000000000000..81c9e1cc4dfd --- /dev/null +++ b/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/EnabledOnJreConditionTests.java.jte @@ -0,0 +1,99 @@ +@import java.util.List +@import junitbuild.generator.model.JRE + +@param List jres +@param List jresSortedByStringValue +@param String licenseHeader +${licenseHeader} +package org.junit.jupiter.api.condition; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +@for(var jre : jresSortedByStringValue)<%-- +--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava${jre.getVersion()}; +@endfor<%-- +--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.platform.commons.PreconditionViolationException; + +/** + * Unit tests for {@link EnabledOnJreCondition}. + * + *

    Note that test method names MUST match the test method names in + * {@link EnabledOnJreIntegrationTests}. + * + * @since 5.1 + */ +class EnabledOnJreConditionTests extends AbstractExecutionConditionTests { + + @Override + protected ExecutionCondition getExecutionCondition() { + return new EnabledOnJreCondition(); + } + + @Override + protected Class getTestClass() { + return EnabledOnJreIntegrationTests.class; + } + + /** + * @see EnabledOnJreIntegrationTests#enabledBecauseAnnotationIsNotPresent() + */ + @Test + void enabledBecauseAnnotationIsNotPresent() { + evaluateCondition(); + assertEnabled(); + assertReasonContains("@EnabledOnJre is not present"); + } + + /** + * @see EnabledOnJreIntegrationTests#missingJreDeclaration() + */ + @Test + void missingJreDeclaration() { + Exception exception = assertThrows(PreconditionViolationException.class, this::evaluateCondition); + assertThat(exception).hasMessageContaining("You must declare at least one JRE"); + } + + /** + * @see EnabledOnJreIntegrationTests#enabledOnAllJavaVersions() + */ + @Test + void enabledOnAllJavaVersions() { + evaluateCondition(); + assertEnabledOnCurrentJreIf(true); + } +@for(var jre : jres) + /** + * @see EnabledOnJreIntegrationTests#java${jre.getVersion()}() + */ + @Test + void java${jre.getVersion()}() { + evaluateCondition(); + assertEnabledOnCurrentJreIf(onJava${jre.getVersion()}()); + } +@endfor + /** + * @see EnabledOnJreIntegrationTests#other() + */ + @Test + void other() { + evaluateCondition(); + assertEnabledOnCurrentJreIf(!onKnownVersion()); + assertCustomDisabledReasonIs("Disabled on almost every JRE"); + } + + private void assertEnabledOnCurrentJreIf(boolean condition) { + if (condition) { + assertEnabled(); + assertReasonContains("Enabled on JRE version: " + System.getProperty("java.version")); + } + else { + assertDisabled(); + assertReasonContains("Disabled on JRE version: " + System.getProperty("java.version")); + } + } + +} diff --git a/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/EnabledOnJreIntegrationTests.java.jte b/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/EnabledOnJreIntegrationTests.java.jte new file mode 100644 index 000000000000..7aee1f374c73 --- /dev/null +++ b/jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/EnabledOnJreIntegrationTests.java.jte @@ -0,0 +1,64 @@ +@import java.util.List +@import junitbuild.generator.model.JRE + +@param List jres +@param List jresSortedByStringValue +@param String licenseHeader +${licenseHeader} +package org.junit.jupiter.api.condition; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +@for(var jre : jresSortedByStringValue)<%-- +--%>import static org.junit.jupiter.api.condition.JRE.JAVA_${jre.getVersion()}; +@endfor<%-- +--%>import static org.junit.jupiter.api.condition.JRE.OTHER; +@for(var jre : jresSortedByStringValue)<%-- +--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava${jre.getVersion()}; +@endfor<%-- +--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Integration tests for {@link EnabledOnJre}. + * + * @since 5.1 + */ +class EnabledOnJreIntegrationTests { + + @Test + @Disabled("Only used in a unit test via reflection") + void enabledBecauseAnnotationIsNotPresent() { + } + + @Test + @Disabled("Only used in a unit test via reflection") + @EnabledOnJre({}) + void missingJreDeclaration() { + } + + @Test + @EnabledOnJre({ // +@for(var jre : jres)<%-- +--%> JAVA_${jre.getVersion()}, // +@endfor<%-- +--%> OTHER // + }) + void enabledOnAllJavaVersions() { + } +@for(var jre : jres) + @Test + @EnabledOnJre(JAVA_${jre.getVersion()}) + void java${jre.getVersion()}() { + assertTrue(onJava${jre.getVersion()}()); + } +@endfor + @Test + @EnabledOnJre(value = OTHER, disabledReason = "Disabled on almost every JRE") + void other() { + assertFalse(onKnownVersion()); + } + +} diff --git a/junit-jupiter-engine/src/test/groovy/org/junit/jupiter/api/GroovyAssertEqualsTests.groovy b/jupiter-tests/src/test/groovy/org/junit/jupiter/api/GroovyAssertEqualsTests.groovy similarity index 100% rename from junit-jupiter-engine/src/test/groovy/org/junit/jupiter/api/GroovyAssertEqualsTests.groovy rename to jupiter-tests/src/test/groovy/org/junit/jupiter/api/GroovyAssertEqualsTests.groovy diff --git a/junit-jupiter-engine/src/test/groovy/org/junit/jupiter/api/GroovyAssertNotEqualsTests.groovy b/jupiter-tests/src/test/groovy/org/junit/jupiter/api/GroovyAssertNotEqualsTests.groovy similarity index 100% rename from junit-jupiter-engine/src/test/groovy/org/junit/jupiter/api/GroovyAssertNotEqualsTests.groovy rename to jupiter-tests/src/test/groovy/org/junit/jupiter/api/GroovyAssertNotEqualsTests.groovy diff --git a/junit-jupiter-engine/src/test/groovy/org/junit/jupiter/api/PrimitiveAndWrapperTypeHelpers.groovy b/jupiter-tests/src/test/groovy/org/junit/jupiter/api/PrimitiveAndWrapperTypeHelpers.groovy similarity index 100% rename from junit-jupiter-engine/src/test/groovy/org/junit/jupiter/api/PrimitiveAndWrapperTypeHelpers.groovy rename to jupiter-tests/src/test/groovy/org/junit/jupiter/api/PrimitiveAndWrapperTypeHelpers.groovy diff --git a/junit-jupiter-engine/src/test/java/DefaultPackageTestCase.java b/jupiter-tests/src/test/java/DefaultPackageTestCase.java similarity index 91% rename from junit-jupiter-engine/src/test/java/DefaultPackageTestCase.java rename to jupiter-tests/src/test/java/DefaultPackageTestCase.java index 3b10c68b5d56..305995fcd0e2 100644 --- a/junit-jupiter-engine/src/test/java/DefaultPackageTestCase.java +++ b/jupiter-tests/src/test/java/DefaultPackageTestCase.java @@ -1,6 +1,6 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/example/B_TestCase.java b/jupiter-tests/src/test/java/example/B_TestCase.java similarity index 92% rename from junit-jupiter-engine/src/test/java/example/B_TestCase.java rename to jupiter-tests/src/test/java/example/B_TestCase.java index 95ab9959760f..db7cada43fea 100644 --- a/junit-jupiter-engine/src/test/java/example/B_TestCase.java +++ b/jupiter-tests/src/test/java/example/B_TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/JupiterTestSuite.java b/jupiter-tests/src/test/java/org/junit/jupiter/JupiterTestSuite.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/JupiterTestSuite.java rename to jupiter-tests/src/test/java/org/junit/jupiter/JupiterTestSuite.java index 470fb7be4461..e29ceeca445a 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/JupiterTestSuite.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/JupiterTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertAllAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertAllAssertionsTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertAllAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertAllAssertionsTests.java index ac8d045eeb25..13a4c58e0f96 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertAllAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertAllAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.Collection; @@ -102,8 +103,8 @@ void assertAllWithExecutablesThatThrowAssertionErrors() { // @formatter:off MultipleFailuresError multipleFailuresError = assertThrows(MultipleFailuresError.class, () -> assertAll( - () -> assertFalse(true), - () -> assertFalse(true) + () -> fail(), + () -> fail() ) ); // @formatter:on @@ -116,8 +117,8 @@ void assertAllWithCollectionOfExecutablesThatThrowAssertionErrors() { // @formatter:off MultipleFailuresError multipleFailuresError = assertThrows(MultipleFailuresError.class, () -> assertAll(asList( - () -> assertFalse(true), - () -> assertFalse(true) + () -> fail(), + () -> fail() )) ); // @formatter:on @@ -130,8 +131,8 @@ void assertAllWithStreamOfExecutablesThatThrowAssertionErrors() { // @formatter:off MultipleFailuresError multipleFailuresError = assertThrows(MultipleFailuresError.class, () -> assertAll(Stream.of( - () -> assertFalse(true), - () -> assertFalse(true) + () -> fail(), + () -> fail() )) ); // @formatter:on diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertArrayEqualsAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertArrayEqualsAssertionsTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertArrayEqualsAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertArrayEqualsAssertionsTests.java index 5900e356a2ad..90ce5920a825 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertArrayEqualsAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertArrayEqualsAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -27,18 +27,20 @@ class AssertArrayEqualsAssertionsTests { @Test void assertArrayEqualsWithNulls() { - assertArrayEquals((boolean[]) null, (boolean[]) null); - assertArrayEquals((char[]) null, (char[]) null); - assertArrayEquals((byte[]) null, (byte[]) null); - assertArrayEquals((int[]) null, (int[]) null); - assertArrayEquals((long[]) null, (long[]) null); - assertArrayEquals((float[]) null, (float[]) null); - assertArrayEquals((double[]) null, (double[]) null); - assertArrayEquals((Object[]) null, (Object[]) null); + assertArrayEquals(null, (boolean[]) null); + assertArrayEquals(null, (char[]) null); + assertArrayEquals(null, (byte[]) null); + assertArrayEquals(null, (int[]) null); + assertArrayEquals(null, (long[]) null); + assertArrayEquals(null, (float[]) null); + assertArrayEquals(null, (double[]) null); + assertArrayEquals(null, (Object[]) null); } @Test void assertArrayEqualsBooleanArrays() { + boolean[] array = {}; + assertArrayEquals(array, array); assertArrayEquals(new boolean[] {}, new boolean[] {}); assertArrayEquals(new boolean[] {}, new boolean[] {}, "message"); assertArrayEquals(new boolean[] {}, new boolean[] {}, () -> "message"); @@ -180,6 +182,8 @@ void assertArrayEqualsDifferentBooleanArraysAndMessageSupplier() { @Test void assertArrayEqualsCharArrays() { + char[] array = {}; + assertArrayEquals(array, array); assertArrayEquals(new char[] {}, new char[] {}); assertArrayEquals(new char[] {}, new char[] {}, "message"); assertArrayEquals(new char[] {}, new char[] {}, () -> "message"); @@ -320,6 +324,8 @@ void assertArrayEqualsDifferentCharArraysAndMessageSupplier() { @Test void assertArrayEqualsByteArrays() { + byte[] array = {}; + assertArrayEquals(array, array); assertArrayEquals(new byte[] {}, new byte[] {}); assertArrayEquals(new byte[] {}, new byte[] {}, "message"); assertArrayEquals(new byte[] {}, new byte[] {}, () -> "message"); @@ -461,6 +467,8 @@ void assertArrayEqualsDifferentByteArraysAndMessageSupplier() { @Test void assertArrayEqualsShortArrays() { + short[] array = {}; + assertArrayEquals(array, array); assertArrayEquals(new short[] {}, new short[] {}); assertArrayEquals(new short[] {}, new short[] {}, "message"); assertArrayEquals(new short[] {}, new short[] {}, () -> "message"); @@ -602,6 +610,8 @@ void assertArrayEqualsDifferentShortArraysAndMessageSupplier() { @Test void assertArrayEqualsIntArrays() { + int[] array = {}; + assertArrayEquals(array, array); assertArrayEquals(new int[] {}, new int[] {}); assertArrayEquals(new int[] {}, new int[] {}, "message"); assertArrayEquals(new int[] {}, new int[] {}, () -> "message"); @@ -746,6 +756,8 @@ void assertArrayEqualsDifferentIntArraysAndMessageSupplier() { @Test void assertArrayEqualsLongArrays() { + long[] array = {}; + assertArrayEquals(array, array); assertArrayEquals(new long[] {}, new long[] {}); assertArrayEquals(new long[] {}, new long[] {}, "message"); assertArrayEquals(new long[] {}, new long[] {}, () -> "message"); @@ -888,6 +900,8 @@ void assertArrayEqualsDifferentLongArraysAndMessageSupplier() { @Test void assertArrayEqualsFloatArrays() { + float[] array = {}; + assertArrayEquals(array, array); assertArrayEquals(new float[] {}, new float[] {}); assertArrayEquals(new float[] {}, new float[] {}, "message"); assertArrayEquals(new float[] {}, new float[] {}, () -> "message"); @@ -1043,6 +1057,8 @@ void assertArrayEqualsDifferentFloatArraysAndMessageSupplier() { @Test void assertArrayEqualsDeltaFloatArrays() { + float[] array = {}; + assertArrayEquals(array, array, 0.001F); assertArrayEquals(new float[] {}, new float[] {}, 0.001F); assertArrayEquals(new float[] {}, new float[] {}, 0.001F, "message"); assertArrayEquals(new float[] {}, new float[] {}, 0.001F, () -> "message"); @@ -1231,6 +1247,8 @@ void assertArrayEqualsDeltaDifferentFloatArraysAndMessageSupplier() { @Test void assertArrayEqualsDoubleArrays() { + double[] array = {}; + assertArrayEquals(array, array); assertArrayEquals(new double[] {}, new double[] {}); assertArrayEquals(new double[] {}, new double[] {}, "message"); assertArrayEquals(new double[] {}, new double[] {}, () -> "message"); @@ -1386,6 +1404,8 @@ void assertArrayEqualsDifferentDoubleArraysAndMessageSupplier() { @Test void assertArrayEqualsDeltaDoubleArrays() { + double[] array = {}; + assertArrayEquals(array, array, 0.5); assertArrayEquals(new double[] {}, new double[] {}, 0.5); assertArrayEquals(new double[] {}, new double[] {}, 0.5, "message"); assertArrayEquals(new double[] {}, new double[] {}, 0.5, () -> "message"); @@ -1624,9 +1644,11 @@ void assertArrayEqualsObjectArrays() { new int[] { 8 }, new Object[] { new long[] { 9 } } }); assertArrayEquals( - new Object[] { "a", new char[] { 'b', 'c' }, new int[] { 'd' }, + new Object[] { "a", new byte[] { 42 }, new short[] { 42 }, new char[] { 'b', 'c' }, new float[] { 42.23f }, + new double[] { 42.23 }, new boolean[] { true }, new Object[] { new Object[] { new String[] { "ef" }, new Object[] { new String[] { "ghi" } } } } }, - new Object[] { "a", new char[] { 'b', 'c' }, new int[] { 'd' }, + new Object[] { "a", new byte[] { 42 }, new short[] { 42 }, new char[] { 'b', 'c' }, new float[] { 42.23f }, + new double[] { 42.23 }, new boolean[] { true }, new Object[] { new Object[] { new String[] { "ef" }, new Object[] { new String[] { "ghi" } } } } }); } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertDoesNotThrowAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertDoesNotThrowAssertionsTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertDoesNotThrowAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertDoesNotThrowAssertionsTests.java index 77bc9ce42eec..ec290530a53e 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertDoesNotThrowAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertDoesNotThrowAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -73,7 +73,7 @@ void assertDoesNotThrowWithMethodReferenceForVoidReturnType() { assertDoesNotThrow(foo::normalMethod); // Explicitly as an Executable - assertDoesNotThrow((Executable) foo::normalMethod); + assertDoesNotThrow(foo::normalMethod); assertDoesNotThrow((Executable) foo::overloaded); } @@ -152,7 +152,7 @@ void assertDoesNotThrowWithExecutableThatThrowsARuntimeExceptionWithMessage() { @Test void assertDoesNotThrowWithExecutableThatThrowsAnError() { try { - assertDoesNotThrow((Executable) AssertionTestUtils::recurseIndefinitely); + assertDoesNotThrow(AssertionTestUtils::recurseIndefinitely); expectAssertionFailedError(); } catch (AssertionFailedError ex) { diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertEqualsAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertEqualsAssertionsTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertEqualsAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertEqualsAssertionsTests.java index 8c09c72c925d..9a41bf65882b 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertEqualsAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertEqualsAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertFalseAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertFalseAssertionsTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertFalseAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertFalseAssertionsTests.java index ee2f2f61a34c..e8a24a98af17 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertFalseAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertFalseAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertInstanceOfAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertInstanceOfAssertionsTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertInstanceOfAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertInstanceOfAssertionsTests.java index 70d5882b3915..56a75ced10e0 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertInstanceOfAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertInstanceOfAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertIterableEqualsAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertIterableEqualsAssertionsTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertIterableEqualsAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertIterableEqualsAssertionsTests.java index 843f2af7dcd9..bc492854d2ac 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertIterableEqualsAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertIterableEqualsAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertLinesMatchAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertLinesMatchAssertionsTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertLinesMatchAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertLinesMatchAssertionsTests.java index 97263cd66343..a76c5f6cc049 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertLinesMatchAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertLinesMatchAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -97,7 +97,7 @@ void assertLinesMatchUsingFastForwardMarkerWithLimit3() { @Test @SuppressWarnings({ "unchecked", "rawtypes" }) void assertLinesMatchWithNullFails() { - assertThrows(PreconditionViolationException.class, () -> assertLinesMatch((List) null, (List) null)); + assertThrows(PreconditionViolationException.class, () -> assertLinesMatch(null, (List) null)); assertThrows(PreconditionViolationException.class, () -> assertLinesMatch(null, Collections.emptyList())); assertThrows(PreconditionViolationException.class, () -> assertLinesMatch(Collections.emptyList(), null)); } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNotEqualsAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotEqualsAssertionsTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNotEqualsAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotEqualsAssertionsTests.java index 1606c6be17e8..09dfe6b9b9ea 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNotEqualsAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotEqualsAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNotNullAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotNullAssertionsTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNotNullAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotNullAssertionsTests.java index bf234197f224..3a59accdd344 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNotNullAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotNullAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNotSameAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotSameAssertionsTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNotSameAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotSameAssertionsTests.java index 4429f33b4bad..9c2ea0fd7c98 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNotSameAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotSameAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNullAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNullAssertionsTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNullAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNullAssertionsTests.java index e97ed12f3bd5..773488a8c8af 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertNullAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNullAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertSameAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertSameAssertionsTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertSameAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertSameAssertionsTests.java index c1299d8f5d04..debc87667a39 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertSameAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertSameAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertThrowsAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertThrowsAssertionsTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertThrowsAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertThrowsAssertionsTests.java index a1fa8197e90d..0ac7ef29fe97 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertThrowsAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertThrowsAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertThrowsExactlyAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertThrowsExactlyAssertionsTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertThrowsExactlyAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertThrowsExactlyAssertionsTests.java index ec0c20e47bd1..7ef6e59e680f 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertThrowsExactlyAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertThrowsExactlyAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertTimeoutAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTimeoutAssertionsTests.java similarity index 93% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertTimeoutAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTimeoutAssertionsTests.java index 65ef3620eeb5..3a3b9ae61fba 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertTimeoutAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTimeoutAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -102,10 +102,8 @@ void assertTimeoutForSupplierThatCompletesBeforeTheTimeout() { @Test void assertTimeoutForSupplierThatThrowsAnException() { RuntimeException exception = assertThrows(RuntimeException.class, () -> { - assertTimeout(ofMillis(500), () -> { - ExceptionUtils.throwAsUncheckedException(new RuntimeException("not this time")); - return "Tempus Fugit"; - }); + assertTimeout(ofMillis(500), + () -> ExceptionUtils.throwAsUncheckedException(new RuntimeException("not this time"))); }); assertMessageEquals(exception, "not this time"); } @@ -113,10 +111,7 @@ void assertTimeoutForSupplierThatThrowsAnException() { @Test void assertTimeoutForSupplierThatThrowsAnAssertionFailedError() { AssertionFailedError exception = assertThrows(AssertionFailedError.class, () -> { - assertTimeout(ofMillis(500), () -> { - fail("enigma"); - return "Tempus Fugit"; - }); + assertTimeout(ofMillis(500), () -> fail("enigma")); }); assertMessageEquals(exception, "enigma"); } @@ -158,11 +153,11 @@ void assertTimeoutWithMessageSupplierForSupplierThatCompletesAfterTheTimeout() { * Take a nap for 100 milliseconds. */ private void nap() throws InterruptedException { - long start = System.currentTimeMillis(); + long start = System.nanoTime(); // workaround for imprecise clocks (yes, Windows, I'm talking about you) do { Thread.sleep(100); - } while (System.currentTimeMillis() - start < 100); + } while (System.nanoTime() - start < 100_000_000L); } } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertTimeoutPreemptivelyAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTimeoutPreemptivelyAssertionsTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertTimeoutPreemptivelyAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTimeoutPreemptivelyAssertionsTests.java index c1570e6a004c..c445f86f67fc 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertTimeoutPreemptivelyAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTimeoutPreemptivelyAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -128,10 +128,8 @@ void assertTimeoutPreemptivelyForSupplierThatCompletesBeforeTheTimeout() { @Test void assertTimeoutPreemptivelyForSupplierThatThrowsAnException() { RuntimeException exception = assertThrows(RuntimeException.class, () -> { - assertTimeoutPreemptively(ofMillis(500), () -> { - ExceptionUtils.throwAsUncheckedException(new RuntimeException("not this time")); - return "Tempus Fugit"; - }); + assertTimeoutPreemptively(ofMillis(500), + () -> ExceptionUtils.throwAsUncheckedException(new RuntimeException("not this time"))); }); assertMessageEquals(exception, "not this time"); } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertTrueAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTrueAssertionsTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertTrueAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTrueAssertionsTests.java index edbda5c9b01c..930afebda7ea 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertTrueAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTrueAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertionTestUtils.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertionTestUtils.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertionTestUtils.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssertionTestUtils.java index 3db358427077..92ac86c4a2c3 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertionTestUtils.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertionTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssumptionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssumptionsTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssumptionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/AssumptionsTests.java index badeff93f66c..6c9412cd0974 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssumptionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssumptionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,9 +11,9 @@ package org.junit.jupiter.api; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.abort; import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -226,7 +226,7 @@ private static void assertTestAbortedException(String expectedMessage, Executabl expectTestAbortedException(); } catch (Throwable ex) { - assertTrue(ex instanceof TestAbortedException); + assertInstanceOf(TestAbortedException.class, ex); assertMessageEquals(ex, expectedMessage); } } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DisplayNameGenerationInheritanceTestCase.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/DisplayNameGenerationInheritanceTestCase.java similarity index 92% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DisplayNameGenerationInheritanceTestCase.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/DisplayNameGenerationInheritanceTestCase.java index 427ebdfc1fd0..e35aac0ba0b5 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DisplayNameGenerationInheritanceTestCase.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/DisplayNameGenerationInheritanceTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DisplayNameGenerationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/DisplayNameGenerationTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DisplayNameGenerationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/DisplayNameGenerationTests.java index a02a9adfe1a2..fe051e37e3ee 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DisplayNameGenerationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/DisplayNameGenerationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DynamicTestTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/DynamicTestTests.java similarity index 83% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DynamicTestTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/DynamicTestTests.java index 9036f8c006ce..1244dc697104 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DynamicTestTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/DynamicTestTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -90,6 +91,18 @@ void streamFromIteratorWithNamesPreconditions() { assertThrows(PreconditionViolationException.class, () -> DynamicTest.stream(emptyIterator(), null)); } + @Test + void streamFromStreamWithNamedExecutablesPreconditions() { + assertThrows(PreconditionViolationException.class, + () -> DynamicTest.stream((Stream) null)); + } + + @Test + void streamFromIteratorWithNamedExecutablesPreconditions() { + assertThrows(PreconditionViolationException.class, + () -> DynamicTest.stream((Iterator) null)); + } + @Test void streamFromStream() throws Throwable { Stream stream = DynamicTest.stream(Stream.of("foo", "bar", "baz"), String::toUpperCase, @@ -119,6 +132,26 @@ void streamFromIteratorWithNames() throws Throwable { assertStream(stream); } + @Test + void streamFromStreamWithNamedExecutables() throws Throwable { + Stream stream = DynamicTest.stream( + Stream.of(new DummyNamedExecutableForTests("foo", this::throwingConsumer), + new DummyNamedExecutableForTests("bar", this::throwingConsumer), + new DummyNamedExecutableForTests("baz", this::throwingConsumer))); + + assertStream(stream); + } + + @Test + void streamFromIteratorWithNamedExecutables() throws Throwable { + Stream stream = DynamicTest.stream( + List.of(new DummyNamedExecutableForTests("foo", this::throwingConsumer), + new DummyNamedExecutableForTests("bar", this::throwingConsumer), + new DummyNamedExecutableForTests("baz", this::throwingConsumer)).iterator()); + + assertStream(stream); + } + private void assertStream(Stream stream) throws Throwable { List dynamicTests = stream.collect(Collectors.toList()); @@ -200,4 +233,17 @@ private void assert1Equals50Reflectively() throws Throwable { method.invoke(null, 1, 50); } + record DummyNamedExecutableForTests(String name, ThrowingConsumer consumer) implements NamedExecutable { + + @Override + public String getName() { + return name.toUpperCase(Locale.ROOT); + } + + @Override + public void execute() throws Throwable { + consumer.accept(name); + } + } + } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/EnigmaThrowable.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/EnigmaThrowable.java similarity index 90% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/EnigmaThrowable.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/EnigmaThrowable.java index c3ce961104ff..43b66eeebb2e 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/EnigmaThrowable.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/EnigmaThrowable.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/FailAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/FailAssertionsTests.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/FailAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/FailAssertionsTests.java index fa2edce64b6b..95db2c35ccaa 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/FailAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/FailAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -113,7 +113,7 @@ void failWithThrowable() { @Test void failWithStringAndNullThrowable() { try { - fail("message", (Throwable) null); + fail("message", null); expectAssertionFailedError(); } catch (AssertionFailedError ex) { @@ -127,7 +127,7 @@ void failWithStringAndNullThrowable() { @Test void failWithNullStringAndThrowable() { try { - fail((String) null, new Throwable("cause")); + fail(null, new Throwable("cause")); expectAssertionFailedError(); } catch (AssertionFailedError ex) { diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IndicativeSentencesGenerationInheritanceTestCase.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesGenerationInheritanceTestCase.java similarity index 92% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IndicativeSentencesGenerationInheritanceTestCase.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesGenerationInheritanceTestCase.java index 1a506ee02968..e0e779cf750b 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IndicativeSentencesGenerationInheritanceTestCase.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesGenerationInheritanceTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IndicativeSentencesNestedTestCase.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesNestedTestCase.java similarity index 93% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IndicativeSentencesNestedTestCase.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesNestedTestCase.java index 8b6db62d1b20..691e8dc86647 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IndicativeSentencesNestedTestCase.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesNestedTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IndicativeSentencesTopLevelTestCase.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesTopLevelTestCase.java similarity index 93% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IndicativeSentencesTopLevelTestCase.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesTopLevelTestCase.java index d1cdbcdbbb98..69390065377d 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IndicativeSentencesTopLevelTestCase.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesTopLevelTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IterableFactory.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/IterableFactory.java similarity index 91% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IterableFactory.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/IterableFactory.java index 0bf31285d9b7..1b756c88ff46 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/IterableFactory.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/IterableFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/RandomlyOrderedTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/RandomlyOrderedTests.java similarity index 82% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/RandomlyOrderedTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/RandomlyOrderedTests.java index dc2d5329c469..f43b586c03de 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/RandomlyOrderedTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/RandomlyOrderedTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.jupiter.engine.extension; +package org.junit.jupiter.api; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.engine.Constants.DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME; @@ -20,11 +20,6 @@ import java.util.Set; import java.util.stream.IntStream; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.ClassOrderer; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; import org.junit.platform.testkit.engine.EngineTestKit; import org.junit.platform.testkit.engine.Events; @@ -39,7 +34,7 @@ class RandomlyOrderedTests { void randomSeedForClassAndMethodOrderingIsDeterministic() { IntStream.range(0, 20).forEach(i -> { callSequence.clear(); - var tests = executeTests(1618034L); + var tests = executeTests(1618034); tests.assertStatistics(stats -> stats.succeeded(callSequence.size())); assertThat(callSequence).containsExactlyInAnyOrder("B_TestCase#b", "B_TestCase#c", "B_TestCase#a", @@ -47,7 +42,7 @@ void randomSeedForClassAndMethodOrderingIsDeterministic() { }); } - private Events executeTests(long randomSeed) { + private Events executeTests(@SuppressWarnings("SameParameterValue") long randomSeed) { // @formatter:off return EngineTestKit .engine("junit-jupiter") @@ -64,8 +59,8 @@ abstract static class BaseTestCase { @BeforeEach void trackInvocations(TestInfo testInfo) { - var testClass = testInfo.getTestClass().get(); - var testMethod = testInfo.getTestMethod().get(); + var testClass = testInfo.getTestClass().orElseThrow(); + var testMethod = testInfo.getTestMethod().orElseThrow(); callSequence.add(testClass.getSimpleName() + "#" + testMethod.getName()); } @@ -83,12 +78,15 @@ void c() { } } + @SuppressWarnings("NewClassNamingConvention") static class A_TestCase extends BaseTestCase { } + @SuppressWarnings("NewClassNamingConvention") static class B_TestCase extends BaseTestCase { } + @SuppressWarnings("NewClassNamingConvention") static class C_TestCase extends BaseTestCase { } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/AbstractExecutionConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/AbstractExecutionConditionTests.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/AbstractExecutionConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/AbstractExecutionConditionTests.java index 7e02a7d3360b..5cd1462ac566 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/AbstractExecutionConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/AbstractExecutionConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,6 +12,7 @@ import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.platform.commons.util.ReflectionUtils.findMethod; import static org.junit.platform.commons.util.ReflectionUtils.findMethods; @@ -83,7 +84,7 @@ protected void evaluateCondition() { } protected void assertEnabled() { - assertTrue(!this.result.isDisabled(), "Should be enabled"); + assertFalse(this.result.isDisabled(), "Should be enabled"); } protected void assertDisabled() { diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeConditionTests.java similarity index 63% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeConditionTests.java index 7c4cf44ede4c..01036e36a5a7 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,21 +11,19 @@ package org.junit.jupiter.api.condition; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava10; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava11; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava12; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava13; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava14; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava15; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava16; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava17; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava18; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava19; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava20; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava21; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava22; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava8; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava9; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava10; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava11; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava12; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava13; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava14; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava15; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava16; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava17; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava18; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava19; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava8; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava9; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExecutionCondition; @@ -106,7 +104,7 @@ void javaMax18() { @Test void javaMin18() { evaluateCondition(); - assertDisabledOnCurrentJreIf(!(onJava17())); + assertDisabledOnCurrentJreIf(!onJava17()); } /** @@ -115,9 +113,7 @@ void javaMin18() { @Test void other() { evaluateCondition(); - assertDisabledOnCurrentJreIf( - !(onJava8() || onJava9() || onJava10() || onJava11() || onJava12() || onJava13() || onJava14() || onJava15() - || onJava16() || onJava17() || onJava18() || onJava19() || onJava20() || onJava21() || onJava22())); + assertDisabledOnCurrentJreIf(!onKnownVersion()); } private void assertDisabledOnCurrentJreIf(boolean condition) { diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeIntegrationTests.java similarity index 54% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeIntegrationTests.java index c1c782def80e..ff6ef050306b 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,25 +13,23 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava10; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava11; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava12; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava13; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava14; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava15; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava16; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava17; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava18; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava19; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava20; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava21; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava22; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava8; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava9; import static org.junit.jupiter.api.condition.JRE.JAVA_17; import static org.junit.jupiter.api.condition.JRE.JAVA_18; import static org.junit.jupiter.api.condition.JRE.JAVA_19; import static org.junit.jupiter.api.condition.JRE.OTHER; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava10; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava11; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava12; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava13; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava14; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava15; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava16; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava17; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava18; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava19; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava8; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava9; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -83,9 +81,7 @@ void javaMin18() { @Test @DisabledForJreRange(min = OTHER, max = OTHER) void other() { - assertTrue( - onJava8() || onJava9() || onJava10() || onJava11() || onJava12() || onJava13() || onJava14() || onJava15() - || onJava16() || onJava17() || onJava18() || onJava19() || onJava20() || onJava21() || onJava22()); + assertTrue(onKnownVersion()); } } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionClassLoaderTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionClassLoaderTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionClassLoaderTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionClassLoaderTests.java index 0e2ebac8a435..d7ae67545a06 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionClassLoaderTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionClassLoaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionTests.java index fc1524d89192..68c91b5733d1 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableConditionTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableConditionTests.java index 07a3e78242c8..b438f182d21d 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableIntegrationTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableIntegrationTests.java index cbc06823cc52..19c5ca37eaa2 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfIntegrationTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfIntegrationTests.java index c783d3229387..8f517b6bfe10 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyConditionTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyConditionTests.java index d7be117713aa..97a56de0e4a4 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyIntegrationTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyIntegrationTests.java index 7dd479d362f2..86b85672f0ec 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsConditionTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsConditionTests.java index bab7f53dc24b..06c4b316337e 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsIntegrationTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsIntegrationTests.java index d5a27e7ed29d..7dff43fb7d41 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeConditionTests.java similarity index 64% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeConditionTests.java index 3e4cf7db4b15..03b870b462ea 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,21 +11,19 @@ package org.junit.jupiter.api.condition; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava10; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava11; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava12; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava13; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava14; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava15; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava16; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava17; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava18; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava19; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava20; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava21; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava22; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava8; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava9; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava10; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava11; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava12; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava13; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava14; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava15; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava16; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava17; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava18; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava19; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava8; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava9; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExecutionCondition; @@ -114,9 +112,7 @@ void javaMin18() { @Test void other() { evaluateCondition(); - assertEnabledOnCurrentJreIf( - !(onJava8() || onJava9() || onJava10() || onJava11() || onJava12() || onJava13() || onJava14() || onJava15() - || onJava16() || onJava17() || onJava18() || onJava19() || onJava20() || onJava21() || onJava22())); + assertEnabledOnCurrentJreIf(!onKnownVersion()); } private void assertEnabledOnCurrentJreIf(boolean condition) { diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeIntegrationTests.java similarity index 53% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeIntegrationTests.java index ccf86a08eb6a..074b26f01da5 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,25 +13,23 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava10; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava11; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava12; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava13; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava14; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava15; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava16; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava17; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava18; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava19; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava20; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava21; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava22; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava8; -import static org.junit.jupiter.api.condition.EnabledOnJreIntegrationTests.onJava9; import static org.junit.jupiter.api.condition.JRE.JAVA_17; import static org.junit.jupiter.api.condition.JRE.JAVA_18; import static org.junit.jupiter.api.condition.JRE.JAVA_19; import static org.junit.jupiter.api.condition.JRE.OTHER; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava10; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava11; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava12; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava13; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava14; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava15; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava16; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava17; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava18; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava19; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava8; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava9; +import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -79,16 +77,15 @@ void javaMax18() { @Test @EnabledForJreRange(min = JAVA_18) void javaMin18() { - assertTrue(onJava18() || onJava19() || onJava20() || onJava21() || onJava22()); + assertTrue(onKnownVersion()); + assertTrue(JRE.currentVersion().compareTo(JAVA_18) >= 0); assertFalse(onJava17()); } @Test @EnabledForJreRange(min = OTHER, max = OTHER) void other() { - assertFalse( - onJava8() || onJava9() || onJava10() || onJava11() || onJava12() || onJava13() || onJava14() || onJava15() - || onJava16() || onJava17() || onJava18() || onJava19() || onJava20() || onJava21() || onJava22()); + assertFalse(onKnownVersion()); } } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionClassLoaderTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionClassLoaderTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionClassLoaderTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionClassLoaderTests.java index 3c07ce9bc065..fc74017cdc93 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionClassLoaderTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionClassLoaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionTests.java index ec249667d8be..de2c55c2f7fd 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableConditionTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableConditionTests.java index c90c28c226f8..4dcff0018ede 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableIntegrationTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableIntegrationTests.java index 2e0224c310bd..6e5d0a1d0ff9 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfIntegrationTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfIntegrationTests.java index 60eac3adcb41..6ff479f21209 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyConditionTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyConditionTests.java index 399687ddfe58..45d9ad15a657 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyIntegrationTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyIntegrationTests.java index 2a6010e38af1..325d42050345 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsConditionTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsConditionTests.java index 267e5e69f53c..9dda47713c51 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsIntegrationTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsIntegrationTests.java index 5e7b15c6b740..d79df561212b 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/JRETests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/JRETests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/JRETests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/JRETests.java index 7a2a2e6236ad..26e39ac49af6 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/JRETests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/JRETests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/StaticConditionMethods.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/StaticConditionMethods.java similarity index 89% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/StaticConditionMethods.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/condition/StaticConditionMethods.java index 1175c870824c..eb113d684bfd 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/condition/StaticConditionMethods.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/condition/StaticConditionMethods.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/CloseableResourceIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/extension/CloseableResourceIntegrationTests.java similarity index 90% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/CloseableResourceIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/extension/CloseableResourceIntegrationTests.java index f96e1b6341d2..7d7038849878 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/CloseableResourceIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/extension/CloseableResourceIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -15,6 +15,7 @@ import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure; import static org.junit.platform.testkit.engine.EventConditions.reportEntry; import static org.junit.platform.testkit.engine.EventConditions.test; +import static org.junit.platform.testkit.engine.TestExecutionResultConditions.cause; import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message; import static org.junit.platform.testkit.engine.TestExecutionResultConditions.suppressed; @@ -57,7 +58,10 @@ void exceptionsDuringCloseAreReportedAsSuppressed() { test(), // finishedWithFailure( // message("Exception in test"), // - suppressed(0, message("Exception in onClose"))))); + suppressed(0, // + message("Failed to close extension context"), // + cause(message("Exception in onClose")) // + )))); } @ExtendWith(ThrowingOnCloseExtension.class) diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/ExecutableInvokerIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/extension/ExecutableInvokerIntegrationTests.java similarity index 67% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/ExecutableInvokerIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/extension/ExecutableInvokerIntegrationTests.java index 82a96d168e0a..c3946331ec7e 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/ExecutableInvokerIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/extension/ExecutableInvokerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -45,8 +45,10 @@ static class ExecuteTestsTwiceTestCase { static int testInvocations = 0; @Test - void testWithResolvedParameter(TestInfo testInfo) { + void testWithResolvedParameter(TestInfo testInfo, + @ExtendWith(ExtensionContextParameterResolver.class) ExtensionContext extensionContext) { assertNotNull(testInfo); + assertEquals(testInfo.getTestMethod().orElseThrow(), extensionContext.getRequiredTestMethod()); testInvocations++; } @@ -57,14 +59,15 @@ static class ExecuteConstructorTwiceTestCase { static int constructorInvocations = 0; - public ExecuteConstructorTwiceTestCase(TestInfo testInfo) { + public ExecuteConstructorTwiceTestCase(TestInfo testInfo, + @ExtendWith(ExtensionContextParameterResolver.class) ExtensionContext extensionContext) { assertNotNull(testInfo); + assertEquals(testInfo.getTestClass().orElseThrow(), extensionContext.getRequiredTestClass()); constructorInvocations++; } @Test void test() { - } } @@ -84,9 +87,24 @@ static class ExecuteConstructorTwiceExtension implements BeforeAllCallback { @Override public void beforeAll(ExtensionContext context) throws Exception { context.getExecutableInvoker() // - .invoke(context.getRequiredTestClass().getConstructor(TestInfo.class)); + .invoke(context.getRequiredTestClass().getConstructor(TestInfo.class, ExtensionContext.class)); } } + static class ExtensionContextParameterResolver implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return ExtensionContext.class.equals(parameterContext.getParameter().getType()); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return extensionContext; + } + } + } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/ExtensionComposabilityTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/extension/ExtensionComposabilityTests.java similarity index 70% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/ExtensionComposabilityTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/extension/ExtensionComposabilityTests.java index 1ac243cfc520..60531a9a73bb 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/ExtensionComposabilityTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/extension/ExtensionComposabilityTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -14,6 +14,9 @@ import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.platform.commons.util.FunctionUtils.where; import java.lang.reflect.Method; @@ -21,8 +24,12 @@ import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DynamicContainer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; +import org.junit.platform.commons.support.ModifierSupport; import org.junit.platform.commons.util.ClassUtils; import org.junit.platform.commons.util.ReflectionUtils; @@ -43,8 +50,7 @@ class ExtensionComposabilityTests { void ensureJupiterExtensionApisAreComposable() { // 1) Find all existing top-level Extension APIs - List> extensionApis = ReflectionUtils.findAllClassesInPackage(Extension.class.getPackage().getName(), - this::isExtensionApi, name -> true); + List> extensionApis = findExtensionApis(); // 2) Determine which methods we expect the kitchen sink to implement... @@ -110,6 +116,34 @@ void ensureJupiterExtensionApisAreComposable() { // @formatter:on } + @TestFactory + Stream kitchenSinkExtensionImplementsAllExtensionApis() { + var declaredMethods = List.of(KitchenSinkExtension.class.getDeclaredMethods()); + return findExtensionApis().stream() // + .map(c -> dynamicContainer( // + c.getSimpleName(), // + Stream.concat( // + Stream.of( + dynamicTest("implements interface", () -> c.isAssignableFrom(KitchenSinkExtension.class))), // + Arrays.stream(c.getMethods()) // + .filter(ModifierSupport::isNotStatic).map(m -> dynamicTest( // + "overrides " + m.getName(), // + () -> assertTrue( // + declaredMethods.stream().anyMatch(it -> // + it.getName().equals(m.getName()) // + && it.getReturnType().equals(m.getReturnType()) // + && Arrays.equals(it.getParameterTypes(), m.getParameterTypes()) // + ))) // + ) // + ) // + )); + } + + private List> findExtensionApis() { + return ReflectionUtils.findAllClassesInPackage(Extension.class.getPackage().getName(), this::isExtensionApi, + name -> true); + } + private boolean isExtensionApi(Class candidate) { return candidate.isInterface() && (candidate != Extension.class) && Extension.class.isAssignableFrom(candidate); } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/KitchenSinkExtension.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/extension/KitchenSinkExtension.java similarity index 58% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/KitchenSinkExtension.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/extension/KitchenSinkExtension.java index 1740f609d951..47990d9bdce0 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/KitchenSinkExtension.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/extension/KitchenSinkExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,8 @@ package org.junit.jupiter.api.extension; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.util.Optional; import java.util.stream.Stream; @@ -177,4 +179,67 @@ public void testAborted(ExtensionContext context, Throwable cause) { public void testFailed(ExtensionContext context, Throwable cause) { } + // --- InvocationInterceptor ----------------------------------------------- + + @Override + public T interceptTestClassConstructor(Invocation invocation, + ReflectiveInvocationContext> invocationContext, ExtensionContext extensionContext) + throws Throwable { + return InvocationInterceptor.super.interceptTestClassConstructor(invocation, invocationContext, + extensionContext); + } + + @Override + public void interceptBeforeAllMethod(Invocation invocation, + ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { + InvocationInterceptor.super.interceptBeforeAllMethod(invocation, invocationContext, extensionContext); + } + + @Override + public void interceptBeforeEachMethod(Invocation invocation, + ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { + InvocationInterceptor.super.interceptBeforeEachMethod(invocation, invocationContext, extensionContext); + } + + @Override + public void interceptTestMethod(Invocation invocation, ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) throws Throwable { + InvocationInterceptor.super.interceptTestMethod(invocation, invocationContext, extensionContext); + } + + @Override + public T interceptTestFactoryMethod(Invocation invocation, + ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { + return InvocationInterceptor.super.interceptTestFactoryMethod(invocation, invocationContext, extensionContext); + } + + @Override + public void interceptTestTemplateMethod(Invocation invocation, + ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { + InvocationInterceptor.super.interceptTestTemplateMethod(invocation, invocationContext, extensionContext); + } + + @SuppressWarnings("deprecation") + @Override + public void interceptDynamicTest(Invocation invocation, ExtensionContext extensionContext) throws Throwable { + InvocationInterceptor.super.interceptDynamicTest(invocation, extensionContext); + } + + @Override + public void interceptDynamicTest(Invocation invocation, DynamicTestInvocationContext invocationContext, + ExtensionContext extensionContext) throws Throwable { + InvocationInterceptor.super.interceptDynamicTest(invocation, invocationContext, extensionContext); + } + + @Override + public void interceptAfterEachMethod(Invocation invocation, + ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { + InvocationInterceptor.super.interceptAfterEachMethod(invocation, invocationContext, extensionContext); + } + + @Override + public void interceptAfterAllMethod(Invocation invocation, + ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { + InvocationInterceptor.super.interceptAfterAllMethod(invocation, invocationContext, extensionContext); + } } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolverTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolverTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolverTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolverTests.java index 40a333efb5d8..178547888a2f 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolverTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssertionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssertionsTests.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssertionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssertionsTests.java index 0efa28c947bc..0347d7be2303 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssertionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssertionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssumptionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssumptionsTests.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssumptionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssumptionsTests.java index 0d5cc0bb062c..f7183446af82 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssumptionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssumptionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/AbstractJupiterTestEngineTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/AbstractJupiterTestEngineTests.java similarity index 87% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/AbstractJupiterTestEngineTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/AbstractJupiterTestEngineTests.java index 69264f903287..209f07cc4767 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/AbstractJupiterTestEngineTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/AbstractJupiterTestEngineTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -21,6 +21,7 @@ import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.UniqueId; import org.junit.platform.launcher.LauncherDiscoveryRequest; +import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; import org.junit.platform.testkit.engine.EngineExecutionResults; import org.junit.platform.testkit.engine.EngineTestKit; @@ -38,7 +39,11 @@ protected EngineExecutionResults executeTestsForClass(Class testClass) { } protected EngineExecutionResults executeTests(DiscoverySelector... selectors) { - return executeTests(request().selectors(selectors).build()); + return executeTests(request().selectors(selectors)); + } + + protected EngineExecutionResults executeTests(LauncherDiscoveryRequestBuilder builder) { + return executeTests(builder.build()); } protected EngineExecutionResults executeTests(LauncherDiscoveryRequest request) { diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/AtypicalJvmMethodNameTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/AtypicalJvmMethodNameTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/AtypicalJvmMethodNameTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/AtypicalJvmMethodNameTests.java index 38bef70a8411..9b54101ced9e 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/AtypicalJvmMethodNameTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/AtypicalJvmMethodNameTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/BeforeAllAndAfterAllComposedAnnotationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/BeforeAllAndAfterAllComposedAnnotationTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/BeforeAllAndAfterAllComposedAnnotationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/BeforeAllAndAfterAllComposedAnnotationTests.java index 2cbbf64ad53d..1176fea22381 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/BeforeAllAndAfterAllComposedAnnotationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/BeforeAllAndAfterAllComposedAnnotationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/BeforeEachAndAfterEachComposedAnnotationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/BeforeEachAndAfterEachComposedAnnotationTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/BeforeEachAndAfterEachComposedAnnotationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/BeforeEachAndAfterEachComposedAnnotationTests.java index e6c3804877ee..60091c7ae4a8 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/BeforeEachAndAfterEachComposedAnnotationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/BeforeEachAndAfterEachComposedAnnotationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DefaultExecutionModeTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/DefaultExecutionModeTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DefaultExecutionModeTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/DefaultExecutionModeTests.java index 7dd37e1ea3ca..aae9b97c1bc2 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DefaultExecutionModeTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/DefaultExecutionModeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DefaultMethodTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/DefaultMethodTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DefaultMethodTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/DefaultMethodTests.java index 45d86c7c1773..7fb690877718 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DefaultMethodTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/DefaultMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DisabledTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/DisabledTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DisabledTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/DisabledTests.java index 63f433671e64..62217371196b 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DisabledTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/DisabledTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DynamicNodeGenerationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/DynamicNodeGenerationTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DynamicNodeGenerationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/DynamicNodeGenerationTests.java index 87dcf40c4fc6..17e57d80ac61 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DynamicNodeGenerationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/DynamicNodeGenerationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/ExceptionHandlingTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/ExceptionHandlingTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/ExceptionHandlingTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/ExceptionHandlingTests.java index b1662d658fdc..80e730fa8566 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/ExceptionHandlingTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/ExceptionHandlingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/FailedAssumptionsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/FailedAssumptionsTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/FailedAssumptionsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/FailedAssumptionsTests.java index 726eb885b82e..be2d03755a6c 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/FailedAssumptionsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/FailedAssumptionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/InvalidLifecycleMethodConfigurationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/InvalidLifecycleMethodConfigurationTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/InvalidLifecycleMethodConfigurationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/InvalidLifecycleMethodConfigurationTests.java index 37dc164993ab..a7efbed7e73a 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/InvalidLifecycleMethodConfigurationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/InvalidLifecycleMethodConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/JupiterTestEngineBasicTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/JupiterTestEngineBasicTests.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/JupiterTestEngineBasicTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/JupiterTestEngineBasicTests.java index 5358e434e834..bc07210ac92d 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/JupiterTestEngineBasicTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/JupiterTestEngineBasicTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/LifecycleMethodOverridingAndSupersedingTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/LifecycleMethodOverridingTests.java similarity index 72% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/LifecycleMethodOverridingAndSupersedingTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/LifecycleMethodOverridingTests.java index f2a584fe02a1..f9e7f0b3ec89 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/LifecycleMethodOverridingAndSupersedingTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/LifecycleMethodOverridingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,7 @@ package org.junit.jupiter.engine; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.BeforeEach; @@ -19,19 +20,19 @@ import org.junit.jupiter.engine.subpackage.SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase; /** - * Integration tests that explicitly demonstrate the overriding and superseding - * rules for lifecycle methods in the {@link JupiterTestEngine}. + * Integration tests that explicitly demonstrate the overriding rules for + * lifecycle methods in the {@link JupiterTestEngine}. * * @since 5.9 */ -class LifecycleMethodOverridingAndSupersedingTests { +class LifecycleMethodOverridingTests { @Nested - @DisplayName("A package-private lifecycle super-method can be overridden by") + @DisplayName("A package-private lifecycle method can be overridden by") class PackagePrivateSuperClassTests { @Nested - @DisplayName("a protected lifecycle method in the derived class") + @DisplayName("a protected lifecycle method in a subclass") class ProtectedExtendsPackagePrivateLifecycleMethod extends SuperClassWithPackagePrivateLifecycleMethodTestCase { @@ -43,7 +44,7 @@ protected void beforeEach() { } @Nested - @DisplayName("a package-private lifecycle method in the derived class") + @DisplayName("a package-private lifecycle method in a subclass") class PackagePrivateExtendsPackagePrivateLifecycleMethod extends SuperClassWithPackagePrivateLifecycleMethodTestCase { @@ -55,7 +56,7 @@ void beforeEach() { } @Nested - @DisplayName("a public lifecycle method in the derived class") + @DisplayName("a public lifecycle method in a subclass") class PublicExtendsPackagePrivateLifecycleMethod extends SuperClassWithPackagePrivateLifecycleMethodTestCase { @Override @@ -67,52 +68,55 @@ public void beforeEach() { } @Nested - @DisplayName("A package-private lifecycle super-method from a different package can be superseded by") + @DisplayName("A package-private lifecycle method from a different package cannot be overridden by") class PackagePrivateSuperClassInDifferentPackageTests { @Nested - @DisplayName("a protected lifecycle method in the derived class") + @DisplayName("a protected lifecycle method in a subclass") class ProtectedExtendsPackagePrivateLifecycleMethod extends SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase { // @Override @BeforeEach protected void beforeEach() { + assertThat(super.beforeEachInvoked).isTrue(); } } @Nested - @DisplayName("a package-private lifecycle method in the derived class") + @DisplayName("a package-private lifecycle method in a subclass") class PackagePrivateExtendsPackagePrivateLifecycleMethod extends SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase { // @Override @BeforeEach void beforeEach() { + assertThat(super.beforeEachInvoked).isTrue(); } } @Nested - @DisplayName("a public lifecycle method in the derived class") + @DisplayName("a public lifecycle method in a subclass") class PublicExtendsPackagePrivateLifecycleMethod extends SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase { // @Override @BeforeEach public void beforeEach() { + assertThat(super.beforeEachInvoked).isTrue(); } } } @Nested - @DisplayName("A protected lifecycle super-method can be overridden by") + @DisplayName("A protected lifecycle method can be overridden by") class ProtectedSuperClassTests { @Nested - @DisplayName("a protected lifecycle method in the derived class") + @DisplayName("a protected lifecycle method in a subclass") class ProtectedExtendsPackagePrivate extends SuperClassWithProtectedLifecycleMethodTestCase { @Override @@ -123,7 +127,7 @@ protected void beforeEach() { } @Nested - @DisplayName("a public lifecycle method in the derived class") + @DisplayName("a public lifecycle method in a subclass") class PublicExtendsPackagePrivate extends SuperClassWithProtectedLifecycleMethodTestCase { @Override @@ -135,11 +139,11 @@ public void beforeEach() { } @Nested - @DisplayName("A public lifecycle super-method can be overridden by") + @DisplayName("A public lifecycle method can be overridden by") class PublicSuperClassTests { @Nested - @DisplayName("a public lifecycle method in the derived class") + @DisplayName("a public lifecycle method in a subclass") class PublicExtendsPackagePrivate extends SuperClassWithPublicLifecycleMethodTestCase { @Override diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/MultipleTestableAnnotationsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/MultipleTestableAnnotationsTests.java similarity index 92% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/MultipleTestableAnnotationsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/MultipleTestableAnnotationsTests.java index f8ded8659782..fd841d06e23e 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/MultipleTestableAnnotationsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/MultipleTestableAnnotationsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -29,11 +29,10 @@ * * @since 5.0 */ -@TrackLogRecords class MultipleTestableAnnotationsTests extends AbstractJupiterTestEngineTests { @Test - void testAndRepeatedTest(LogRecordListener listener) { + void testAndRepeatedTest(@TrackLogRecords LogRecordListener listener) { discoverTests(request().selectors(selectClass(TestCase.class)).build()); // @formatter:off diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NestedTestClassesTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedTestClassesTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NestedTestClassesTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedTestClassesTests.java index 2788b157021e..2fa1b21f524f 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NestedTestClassesTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedTestClassesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NestedWithInheritanceTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedWithInheritanceTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NestedWithInheritanceTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedWithInheritanceTests.java index 529026ecdbf8..032d9bb33df7 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NestedWithInheritanceTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedWithInheritanceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NestedWithSeparateInheritanceTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedWithSeparateInheritanceTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NestedWithSeparateInheritanceTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedWithSeparateInheritanceTests.java index 8b69ac205dee..c5f7f808ba9d 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NestedWithSeparateInheritanceTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedWithSeparateInheritanceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NonVoidTestableMethodIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/NonVoidTestableMethodIntegrationTests.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NonVoidTestableMethodIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/NonVoidTestableMethodIntegrationTests.java index 8f44af792f38..abc364ea5147 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/NonVoidTestableMethodIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/NonVoidTestableMethodIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/OverloadedTestMethodTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/OverloadedTestMethodTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/OverloadedTestMethodTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/OverloadedTestMethodTests.java index 6f17f3678cd8..5aa8eab7bbf2 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/OverloadedTestMethodTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/OverloadedTestMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/RecordTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/RecordTests.java similarity index 93% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/RecordTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/RecordTests.java index f5a05db35c2e..43930deffd6f 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/RecordTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/RecordTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/ReportingTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/ReportingTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/ReportingTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/ReportingTests.java index ca909b8db483..0e19f0466f16 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/ReportingTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/ReportingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/SealedClassTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/SealedClassTests.java similarity index 87% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/SealedClassTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/SealedClassTests.java index e1e0f7dc36a0..bcbf37408c68 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/SealedClassTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/SealedClassTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -23,10 +23,7 @@ void sealedTestClassesAreTestClasses() { .assertStatistics(stats -> stats.finished(2).succeeded(1).failed(1)); } - sealed - abstract static class AbstractTestCase - permits TestCase - { + sealed abstract static class AbstractTestCase permits TestCase { @Test void succeedingTest() { diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/StandardTestClassTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/StandardTestClassTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/StandardTestClassTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/StandardTestClassTests.java index 230d5ebc1d3d..3782fe7a9d5e 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/StandardTestClassTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/StandardTestClassTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/StaticNestedBeforeAllAndAfterAllMethodsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/StaticNestedBeforeAllAndAfterAllMethodsTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/StaticNestedBeforeAllAndAfterAllMethodsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/StaticNestedBeforeAllAndAfterAllMethodsTests.java index 4264860252da..16549a1c4c1a 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/StaticNestedBeforeAllAndAfterAllMethodsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/StaticNestedBeforeAllAndAfterAllMethodsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestClassInheritanceTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/TestClassInheritanceTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestClassInheritanceTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/TestClassInheritanceTests.java index 12aa4566ac36..7768b76a49df 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestClassInheritanceTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/TestClassInheritanceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleConfigurationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleConfigurationTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleConfigurationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleConfigurationTests.java index 87260717f3d6..e800c3fede1c 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleConfigurationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleKotlinTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleKotlinTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleKotlinTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleKotlinTests.java index 50d059df5093..6a1f1cf843f5 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleKotlinTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleKotlinTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleTests.java index 91548496d9ea..6c8b87fed8f1 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestTemplateInvocationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/TestTemplateInvocationTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestTemplateInvocationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/TestTemplateInvocationTests.java index 5b63eee0c373..84ad9e49c65d 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestTemplateInvocationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/TestTemplateInvocationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/AbstractNonGenericTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/AbstractNonGenericTests.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/AbstractNonGenericTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/AbstractNonGenericTests.java index 0bc7550085bb..fc4a7616f884 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/AbstractNonGenericTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/AbstractNonGenericTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/AbstractNumberTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/AbstractNumberTests.java similarity index 93% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/AbstractNumberTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/AbstractNumberTests.java index afecac3326b5..2f3f06bbb4d7 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/AbstractNumberTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/AbstractNumberTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/BridgeMethodTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/BridgeMethodTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/BridgeMethodTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/BridgeMethodTests.java index 13150e88b212..87a9c02cdd17 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/BridgeMethodTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/BridgeMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/ChildWithBridgeMethods.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/ChildWithBridgeMethods.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/ChildWithBridgeMethods.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/ChildWithBridgeMethods.java index bd3cb864ed5f..373b6a0e5bb0 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/ChildWithBridgeMethods.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/ChildWithBridgeMethods.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/ChildWithoutBridgeMethods.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/ChildWithoutBridgeMethods.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/ChildWithoutBridgeMethods.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/ChildWithoutBridgeMethods.java index 2a5169126c2a..52a8afcfa9b5 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/ChildWithoutBridgeMethods.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/ChildWithoutBridgeMethods.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/NumberResolver.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/NumberResolver.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/NumberResolver.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/NumberResolver.java index 114d6a6100e2..9da2a5c20721 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/NumberResolver.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/NumberResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/NumberTestGroup.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/NumberTestGroup.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/NumberTestGroup.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/NumberTestGroup.java index 5f2146f65c7f..4c66e26c42c9 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/NumberTestGroup.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/NumberTestGroup.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/PackagePrivateParent.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/PackagePrivateParent.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/PackagePrivateParent.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/PackagePrivateParent.java index 909139a09ae1..2aed5ec13288 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/bridge/PackagePrivateParent.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/PackagePrivateParent.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/config/CachingJupiterConfigurationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/config/CachingJupiterConfigurationTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/config/CachingJupiterConfigurationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/config/CachingJupiterConfigurationTests.java index a978c1883900..5d1214a68972 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/config/CachingJupiterConfigurationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/config/CachingJupiterConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/config/DefaultJupiterConfigurationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/config/DefaultJupiterConfigurationTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/config/DefaultJupiterConfigurationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/config/DefaultJupiterConfigurationTests.java index 44d05998be02..a6c1d9919e56 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/config/DefaultJupiterConfigurationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/config/DefaultJupiterConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -122,7 +122,7 @@ void shouldGetDefaultTempDirFactorySupplierWithConfigParamSet() { Supplier supplier = configuration.getDefaultTempDirFactorySupplier(); - assertThat(supplier.get()).isInstanceOf(TempDirFactory.class); + assertThat(supplier.get()).isInstanceOf(CustomFactory.class); } private static class CustomFactory implements TempDirFactory { diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverterTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverterTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverterTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverterTests.java index 5848cda93b4a..c5bb4939fc5c 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverterTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/CustomDisplayNameGenerator.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/CustomDisplayNameGenerator.java similarity index 93% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/CustomDisplayNameGenerator.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/CustomDisplayNameGenerator.java index 40ef487c7815..3e3c7c411d01 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/CustomDisplayNameGenerator.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/CustomDisplayNameGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/DisplayNameUtilsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/DisplayNameUtilsTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/DisplayNameUtilsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/DisplayNameUtilsTests.java index 5a8679e4acd7..905747468b45 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/DisplayNameUtilsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/DisplayNameUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -47,9 +47,8 @@ void shouldGetDisplayNameFromDisplayNameAnnotation() { } @Test - @TrackLogRecords void shouldGetDisplayNameFromSupplierIfNoDisplayNameAnnotationWithBlankStringPresent( - LogRecordListener listener) { + @TrackLogRecords LogRecordListener listener) { String displayName = DisplayNameUtils.determineDisplayName(BlankDisplayNameTestCase.class, () -> "default-name"); diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionContextTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionContextTests.java similarity index 75% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionContextTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionContextTests.java index af140f29f877..532f2679a0b3 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionContextTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -15,30 +15,33 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.DynamicTest.dynamicTest; +import static org.junit.jupiter.api.Named.named; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.lang.reflect.Method; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.stream.Stream; +import java.util.function.Function; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.parallel.ExecutionMode; import org.junit.jupiter.engine.config.DefaultJupiterConfiguration; import org.junit.jupiter.engine.config.JupiterConfiguration; import org.junit.jupiter.engine.execution.DefaultTestInstances; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.commons.support.ReflectionSupport; import org.junit.platform.engine.ConfigurationParameters; import org.junit.platform.engine.EngineExecutionListener; import org.junit.platform.engine.TestDescriptor; @@ -55,7 +58,6 @@ * {@link MethodExtensionContext}. * * @since 5.0 - * @see org.junit.jupiter.engine.execution.ExtensionValuesStoreTests */ public class ExtensionContextTests { @@ -73,34 +75,33 @@ void fromJupiterEngineDescriptor() { JupiterEngineDescriptor engineTestDescriptor = new JupiterEngineDescriptor( UniqueId.root("engine", "junit-jupiter"), configuration); - JupiterEngineExtensionContext engineContext = new JupiterEngineExtensionContext(null, engineTestDescriptor, - configuration, null); - - // @formatter:off - assertAll("engineContext", - () -> assertThat(engineContext.getElement()).isEmpty(), - () -> assertThat(engineContext.getTestClass()).isEmpty(), - () -> assertThat(engineContext.getTestInstance()).isEmpty(), - () -> assertThat(engineContext.getTestMethod()).isEmpty(), - () -> assertThrows(PreconditionViolationException.class, () -> engineContext.getRequiredTestClass()), - () -> assertThrows(PreconditionViolationException.class, () -> engineContext.getRequiredTestInstance()), - () -> assertThrows(PreconditionViolationException.class, () -> engineContext.getRequiredTestMethod()), - () -> assertThat(engineContext.getDisplayName()).isEqualTo(engineTestDescriptor.getDisplayName()), - () -> assertThat(engineContext.getParent()).isEmpty(), - () -> assertThat(engineContext.getRoot()).isSameAs(engineContext), - () -> assertThat(engineContext.getExecutionMode()).isEqualTo(ExecutionMode.SAME_THREAD) - ); + try (var engineContext = new JupiterEngineExtensionContext(null, engineTestDescriptor, configuration, + __ -> null)) { + // @formatter:off + assertAll("engineContext", + () -> assertThat(engineContext.getElement()).isEmpty(), + () -> assertThat(engineContext.getTestClass()).isEmpty(), + () -> assertThat(engineContext.getTestInstance()).isEmpty(), + () -> assertThat(engineContext.getTestMethod()).isEmpty(), + () -> assertThrows(PreconditionViolationException.class, engineContext::getRequiredTestClass), + () -> assertThrows(PreconditionViolationException.class, engineContext::getRequiredTestInstance), + () -> assertThrows(PreconditionViolationException.class, engineContext::getRequiredTestMethod), + () -> assertThat(engineContext.getDisplayName()).isEqualTo(engineTestDescriptor.getDisplayName()), + () -> assertThat(engineContext.getParent()).isEmpty(), + () -> assertThat(engineContext.getRoot()).isSameAs(engineContext), + () -> assertThat(engineContext.getExecutionMode()).isEqualTo(ExecutionMode.SAME_THREAD) + ); // @formatter:on + } } @Test - @SuppressWarnings("resource") void fromClassTestDescriptor() { NestedClassTestDescriptor nestedClassDescriptor = nestedClassDescriptor(); ClassTestDescriptor outerClassDescriptor = outerClassDescriptor(nestedClassDescriptor); ClassExtensionContext outerExtensionContext = new ClassExtensionContext(null, null, outerClassDescriptor, - configuration, null, null); + configuration, null, __ -> null); // @formatter:off assertAll("outerContext", @@ -109,8 +110,8 @@ void fromClassTestDescriptor() { () -> assertThat(outerExtensionContext.getTestInstance()).isEmpty(), () -> assertThat(outerExtensionContext.getTestMethod()).isEmpty(), () -> assertThat(outerExtensionContext.getRequiredTestClass()).isEqualTo(OuterClass.class), - () -> assertThrows(PreconditionViolationException.class, () -> outerExtensionContext.getRequiredTestInstance()), - () -> assertThrows(PreconditionViolationException.class, () -> outerExtensionContext.getRequiredTestMethod()), + () -> assertThrows(PreconditionViolationException.class, outerExtensionContext::getRequiredTestInstance), + () -> assertThrows(PreconditionViolationException.class, outerExtensionContext::getRequiredTestMethod), () -> assertThat(outerExtensionContext.getDisplayName()).isEqualTo(outerClassDescriptor.getDisplayName()), () -> assertThat(outerExtensionContext.getParent()).isEmpty(), () -> assertThat(outerExtensionContext.getExecutionMode()).isEqualTo(ExecutionMode.SAME_THREAD) @@ -118,12 +119,11 @@ void fromClassTestDescriptor() { // @formatter:on ClassExtensionContext nestedExtensionContext = new ClassExtensionContext(outerExtensionContext, null, - nestedClassDescriptor, configuration, null, null); + nestedClassDescriptor, configuration, null, __ -> null); assertThat(nestedExtensionContext.getParent()).containsSame(outerExtensionContext); } @Test - @SuppressWarnings("resource") void tagsCanBeRetrievedInExtensionContext() { NestedClassTestDescriptor nestedClassDescriptor = nestedClassDescriptor(); ClassTestDescriptor outerClassDescriptor = outerClassDescriptor(nestedClassDescriptor); @@ -131,25 +131,24 @@ void tagsCanBeRetrievedInExtensionContext() { outerClassDescriptor.addChild(methodTestDescriptor); ClassExtensionContext outerExtensionContext = new ClassExtensionContext(null, null, outerClassDescriptor, - configuration, null, null); + configuration, null, __ -> null); assertThat(outerExtensionContext.getTags()).containsExactly("outer-tag"); assertThat(outerExtensionContext.getRoot()).isSameAs(outerExtensionContext); ClassExtensionContext nestedExtensionContext = new ClassExtensionContext(outerExtensionContext, null, - nestedClassDescriptor, configuration, null, null); + nestedClassDescriptor, configuration, null, __ -> null); assertThat(nestedExtensionContext.getTags()).containsExactlyInAnyOrder("outer-tag", "nested-tag"); assertThat(nestedExtensionContext.getRoot()).isSameAs(outerExtensionContext); MethodExtensionContext methodExtensionContext = new MethodExtensionContext(outerExtensionContext, null, - methodTestDescriptor, configuration, new OpenTest4JAwareThrowableCollector(), null); + methodTestDescriptor, configuration, new OpenTest4JAwareThrowableCollector(), __ -> null); methodExtensionContext.setTestInstances(DefaultTestInstances.of(new OuterClass())); assertThat(methodExtensionContext.getTags()).containsExactlyInAnyOrder("outer-tag", "method-tag"); assertThat(methodExtensionContext.getRoot()).isSameAs(outerExtensionContext); } @Test - @SuppressWarnings("resource") void fromMethodTestDescriptor() { TestMethodTestDescriptor methodTestDescriptor = methodDescriptor(); ClassTestDescriptor classTestDescriptor = outerClassDescriptor(methodTestDescriptor); @@ -161,11 +160,11 @@ void fromMethodTestDescriptor() { Method testMethod = methodTestDescriptor.getTestMethod(); JupiterEngineExtensionContext engineExtensionContext = new JupiterEngineExtensionContext(null, engineDescriptor, - configuration, null); + configuration, __ -> null); ClassExtensionContext classExtensionContext = new ClassExtensionContext(engineExtensionContext, null, - classTestDescriptor, configuration, null, null); + classTestDescriptor, configuration, null, __ -> null); MethodExtensionContext methodExtensionContext = new MethodExtensionContext(classExtensionContext, null, - methodTestDescriptor, configuration, new OpenTest4JAwareThrowableCollector(), null); + methodTestDescriptor, configuration, new OpenTest4JAwareThrowableCollector(), __ -> null); methodExtensionContext.setTestInstances(DefaultTestInstances.of(testInstance)); // @formatter:off @@ -191,7 +190,7 @@ void reportEntriesArePublishedToExecutionContext() { ClassTestDescriptor classTestDescriptor = outerClassDescriptor(null); EngineExecutionListener engineExecutionListener = Mockito.spy(EngineExecutionListener.class); ExtensionContext extensionContext = new ClassExtensionContext(null, engineExecutionListener, - classTestDescriptor, configuration, null, null); + classTestDescriptor, configuration, null, __ -> null); Map map1 = Collections.singletonMap("key", "value"); Map map2 = Collections.singletonMap("other key", "other value"); @@ -222,9 +221,9 @@ void usingStore() { TestMethodTestDescriptor methodTestDescriptor = methodDescriptor(); ClassTestDescriptor classTestDescriptor = outerClassDescriptor(methodTestDescriptor); ExtensionContext parentContext = new ClassExtensionContext(null, null, classTestDescriptor, configuration, null, - null); + __ -> null); MethodExtensionContext childContext = new MethodExtensionContext(parentContext, null, methodTestDescriptor, - configuration, new OpenTest4JAwareThrowableCollector(), null); + configuration, new OpenTest4JAwareThrowableCollector(), __ -> null); childContext.setTestInstances(DefaultTestInstances.of(new OuterClass())); ExtensionContext.Store childStore = childContext.getStore(Namespace.GLOBAL); @@ -256,29 +255,40 @@ void usingStore() { assertEquals(parentValue, childStore.get(parentKey)); } - @TestFactory - Stream configurationParameter() throws Exception { + @ParameterizedTest + @MethodSource("extensionContextFactories") + void configurationParameter(Function extensionContextFactory) { JupiterConfiguration echo = new DefaultJupiterConfiguration(new EchoParameters()); String key = "123"; Optional expected = Optional.of(key); - UniqueId engineUniqueId = UniqueId.parse("[engine:junit-jupiter]"); - JupiterEngineDescriptor engineDescriptor = new JupiterEngineDescriptor(engineUniqueId, configuration); - - UniqueId classUniqueId = UniqueId.parse("[engine:junit-jupiter]/[class:MyClass]"); - ClassTestDescriptor classTestDescriptor = new ClassTestDescriptor(classUniqueId, getClass(), configuration); + var context = extensionContextFactory.apply(echo); - Method method = getClass().getDeclaredMethod("configurationParameter"); - UniqueId methodUniqueId = UniqueId.parse("[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]"); - TestMethodTestDescriptor methodTestDescriptor = new TestMethodTestDescriptor(methodUniqueId, getClass(), method, - configuration); + assertEquals(expected, context.getConfigurationParameter(key)); + } - return Stream.of( // - (ExtensionContext) new JupiterEngineExtensionContext(null, engineDescriptor, echo, null), // - new ClassExtensionContext(null, null, classTestDescriptor, echo, null, null), // - new MethodExtensionContext(null, null, methodTestDescriptor, echo, null, null) // - ).map(context -> dynamicTest(context.getClass().getSimpleName(), - () -> assertEquals(expected, context.getConfigurationParameter(key)))); + static List>> extensionContextFactories() { + Class testClass = ExtensionContextTests.class; + return List.of( // + named("engine", (JupiterConfiguration configuration) -> { + UniqueId engineUniqueId = UniqueId.parse("[engine:junit-jupiter]"); + JupiterEngineDescriptor engineDescriptor = new JupiterEngineDescriptor(engineUniqueId, configuration); + return new JupiterEngineExtensionContext(null, engineDescriptor, configuration, __ -> null); + }), // + named("class", (JupiterConfiguration configuration) -> { + UniqueId classUniqueId = UniqueId.parse("[engine:junit-jupiter]/[class:MyClass]"); + ClassTestDescriptor classTestDescriptor = new ClassTestDescriptor(classUniqueId, testClass, + configuration); + return new ClassExtensionContext(null, null, classTestDescriptor, configuration, null, __ -> null); + }), // + named("method", (JupiterConfiguration configuration) -> { + Method method = ReflectionSupport.findMethod(testClass, "extensionContextFactories").orElseThrow(); + UniqueId methodUniqueId = UniqueId.parse("[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]"); + TestMethodTestDescriptor methodTestDescriptor = new TestMethodTestDescriptor(methodUniqueId, testClass, + method, configuration); + return new MethodExtensionContext(null, null, methodTestDescriptor, configuration, null, __ -> null); + }) // + ); } private NestedClassTestDescriptor nestedClassDescriptor() { @@ -309,7 +319,7 @@ private TestMethodTestDescriptor methodDescriptor() { static class OuterClass { @Tag("nested-tag") - class NestedClass { + static class NestedClass { } @Tag("method-tag") @@ -329,8 +339,8 @@ public Optional getBoolean(String key) { throw new UnsupportedOperationException("getBoolean(String) should not be called"); } + @SuppressWarnings({ "deprecation", "RedundantSuppression" }) @Override - @SuppressWarnings("deprecation") public int size() { throw new UnsupportedOperationException("size() should not be called"); } diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionsUtilsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionsUtilsTests.java new file mode 100644 index 000000000000..b7afbb0c44b4 --- /dev/null +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionsUtilsTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.engine.descriptor; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; +import java.util.function.Function; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.engine.extension.ExtensionRegistrar; + +/** + * Tests for {@link ExtensionUtils}. + * + * @since 5.11.3 + */ +class ExtensionsUtilsTests { + + @Test + void registerExtensionsViaStaticFields() throws Exception { + Field field = TestCase.class.getDeclaredField("staticField"); + ExtensionRegistrar registrar = mock(); + ExtensionUtils.registerExtensionsFromStaticFields(registrar, TestCase.class); + verify(registrar).registerExtension(Extension1.class); + verify(registrar).registerExtension(Extension2.class); + verify(registrar).registerExtension(TestCase.staticField, field); + } + + @Test + @SuppressWarnings("unchecked") + void registerExtensionsViaInstanceFields() throws Exception { + Class testClass = TestCase.class; + Field field = testClass.getDeclaredField("instanceField"); + ExtensionRegistrar registrar = mock(); + ExtensionUtils.registerExtensionsFromInstanceFields(registrar, testClass); + verify(registrar).registerExtension(Extension1.class); + verify(registrar).registerExtension(Extension2.class); + verify(registrar).registerUninitializedExtension(eq(testClass), eq(field), any(Function.class)); + } + + static class Extension1 implements Extension { + } + + static class Extension2 implements Extension { + } + + static class Extension3 implements Extension { + } + + static class Extension4 implements Extension { + } + + @Retention(RetentionPolicy.RUNTIME) + @ExtendWith(Extension1.class) + @ExtendWith(Extension2.class) + @interface UseCustomExtensions { + } + + static class TestCase { + + @UseCustomExtensions + @RegisterExtension + static Extension3 staticField = new Extension3(); + + @UseCustomExtensions + @RegisterExtension + Extension4 instanceField = new Extension4(); + + } + +} diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptorTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptorTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptorTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptorTests.java index ecd65438b861..b81a9f658514 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptorTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtilsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtilsTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtilsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtilsTests.java index a77ec6abb918..85a8fed7160b 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtilsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/NamespaceTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/NamespaceTests.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/NamespaceTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/NamespaceTests.java index 67ed44ed28fd..8841c01c3efa 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/NamespaceTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/NamespaceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptorTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptorTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptorTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptorTests.java index 7718e771ae2f..b16f765f0e91 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptorTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -71,7 +71,7 @@ void fileSourceFromUriWithFilePosition() { assertThat(file).isFile(); FilePosition position = FilePosition.from(42, 21); - URI uri = URI.create(file.toURI().toString() + "?line=42&column=21"); + URI uri = URI.create(file.toURI() + "?line=42&column=21"); TestSource testSource = TestFactoryTestDescriptor.fromUri(uri); assertThat(testSource).isInstanceOf(FileSource.class); diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtilsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtilsTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtilsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtilsTests.java index 46fab2deece5..2bbdf1321a95 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtilsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptorTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptorTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptorTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptorTests.java index b00060eb8e0b..0e0834f3e105 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptorTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptorTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptorTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptorTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptorTests.java index f9649ddac33f..ecc410f2af2f 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptorTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class1WithTestCases.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class1WithTestCases.java similarity index 88% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class1WithTestCases.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class1WithTestCases.java index 25091d05bd80..8d34c3705df0 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class1WithTestCases.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class1WithTestCases.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class2WithTestCases.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class2WithTestCases.java similarity index 88% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class2WithTestCases.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class2WithTestCases.java index 2732b2742e53..49f87adb29e6 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class2WithTestCases.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class2WithTestCases.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithStaticInnerTestCases.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithStaticInnerTestCases.java similarity index 91% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithStaticInnerTestCases.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithStaticInnerTestCases.java index 139f38e6f287..0090eb336fcc 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithStaticInnerTestCases.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithStaticInnerTestCases.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithoutTestCases.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithoutTestCases.java similarity index 87% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithoutTestCases.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithoutTestCases.java index bb0d49fecd0c..e1c29f8e0c64 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithoutTestCases.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithoutTestCases.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolverTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolverTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolverTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolverTests.java index c51a116f8399..7e938d9eb954 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolverTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/DiscoveryTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/DiscoveryTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/DiscoveryTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/DiscoveryTests.java index 3ab385d7eded..87bbafe2485e 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/DiscoveryTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/DiscoveryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -84,7 +84,7 @@ void discoverMethodByUniqueIdForOverloadedMethodVariantThatAcceptsArguments() { @Test void discoverMethodByMethodReference() throws NoSuchMethodException { - Method testMethod = LocalTestCase.class.getDeclaredMethod("test3", new Class[0]); + Method testMethod = LocalTestCase.class.getDeclaredMethod("test3"); LauncherDiscoveryRequest request = request().selectors(selectMethod(LocalTestCase.class, testMethod)).build(); TestDescriptor engineDescriptor = discoverTests(request); diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsInnerClassTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsInnerClassTests.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsInnerClassTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsInnerClassTests.java index 99a4116617ea..dfc305708a55 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsInnerClassTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsInnerClassTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsNestedTestClassTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsNestedTestClassTests.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsNestedTestClassTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsNestedTestClassTests.java index 9ce907e8582f..434a105131fc 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsNestedTestClassTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsNestedTestClassTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsPotentialTestContainerTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsPotentialTestContainerTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsPotentialTestContainerTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsPotentialTestContainerTests.java index 09fee4aa77b5..8b2a36d9bf1f 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsPotentialTestContainerTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsPotentialTestContainerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestClassWithTestsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestClassWithTestsTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestClassWithTestsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestClassWithTestsTests.java index 53537e9afe61..d3bea9a00710 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestClassWithTestsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestClassWithTestsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethodTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethodTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethodTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethodTests.java index e814cee4c496..d0d390b7576d 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethodTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethodTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethodTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethodTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethodTests.java index f4d211f84fdd..ae1eaa1cc33b 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethodTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethodTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethodTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethodTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethodTests.java index a260e0d6cbc3..3b2a8c783266 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethodTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/AbstractExecutableInvokerTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/AbstractExecutableInvokerTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/AbstractExecutableInvokerTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/AbstractExecutableInvokerTests.java index 786782339281..e30d5a20ebb0 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/AbstractExecutableInvokerTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/AbstractExecutableInvokerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/DefaultExecutableInvokerTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DefaultExecutableInvokerTests.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/DefaultExecutableInvokerTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DefaultExecutableInvokerTests.java index ab54aade988c..b09c9cec7f83 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/DefaultExecutableInvokerTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DefaultExecutableInvokerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/DefaultTestInstancesTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DefaultTestInstancesTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/DefaultTestInstancesTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DefaultTestInstancesTests.java index d3881a3dfcea..24c836c173ee 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/DefaultTestInstancesTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DefaultTestInstancesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/DynamicTestIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DynamicTestIntegrationTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/DynamicTestIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DynamicTestIntegrationTests.java index c03c65813145..561ead14e647 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/DynamicTestIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DynamicTestIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreConcurrencyTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreConcurrencyTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreConcurrencyTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreConcurrencyTests.java index dd270822b176..305f94173a52 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreConcurrencyTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreConcurrencyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreTests.java index d19f97f2a31a..07ba68b15886 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvokerTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvokerTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvokerTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvokerTests.java index 544317661154..4ce912a74e73 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvokerTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvokerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContextTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContextTests.java similarity index 81% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContextTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContextTests.java index d6e619d886bd..0a2f8abe0270 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContextTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -17,15 +17,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.withSettings; -import java.util.logging.Level; -import java.util.logging.LogRecord; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.fixtures.TrackLogRecords; import org.junit.jupiter.engine.config.JupiterConfiguration; import org.junit.jupiter.engine.extension.MutableExtensionRegistry; -import org.junit.platform.commons.logging.LogRecordListener; import org.junit.platform.engine.EngineExecutionListener; /** @@ -89,12 +84,11 @@ void canOverrideAttributeWhenContextIsExtended() { } @Test - @TrackLogRecords - void closeAttemptExceptionWillBeThrownDownTheCallStack(LogRecordListener logRecordListener) throws Exception { + void closeAttemptExceptionWillBeThrownDownTheCallStack() throws Exception { ExtensionContext failingExtensionContext = mock(ExtensionContext.class, withSettings().extraInterfaces(AutoCloseable.class)); - Exception expectedException = new Exception("test message"); - doThrow(expectedException).when(((AutoCloseable) failingExtensionContext)).close(); + Exception expectedCause = new Exception("test message"); + doThrow(expectedCause).when(((AutoCloseable) failingExtensionContext)).close(); JupiterEngineExecutionContext newContext = originalContext.extend() // .withExtensionContext(failingExtensionContext) // @@ -102,10 +96,9 @@ void closeAttemptExceptionWillBeThrownDownTheCallStack(LogRecordListener logReco Exception actualException = assertThrows(Exception.class, newContext::close); - assertSame(expectedException, actualException); - assertThat(logRecordListener.stream(JupiterEngineExecutionContext.class, Level.SEVERE)) // - .extracting(LogRecord::getMessage) // - .containsOnly("Caught exception while closing extension context: " + failingExtensionContext); + assertThat(actualException) // + .hasMessage("Failed to close extension context") // + .hasCauseReference(expectedCause); } } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/ParameterResolutionUtilsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ParameterResolutionUtilsTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/ParameterResolutionUtilsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ParameterResolutionUtilsTests.java index 1c371f6f775f..895aed7a1618 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/ParameterResolutionUtilsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ParameterResolutionUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/UniqueIdParsingForArrayParameterIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/UniqueIdParsingForArrayParameterIntegrationTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/UniqueIdParsingForArrayParameterIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/UniqueIdParsingForArrayParameterIntegrationTests.java index a5184a8484d2..bae7f2e80be4 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/UniqueIdParsingForArrayParameterIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/UniqueIdParsingForArrayParameterIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotation.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotation.java similarity index 91% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotation.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotation.java index 01d89a574f3c..8fa982dc3a5e 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotation.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotation.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotationParameterResolver.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotationParameterResolver.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotationParameterResolver.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotationParameterResolver.java index b24aad8b792a..47dfdb80d95b 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotationParameterResolver.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotationParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomType.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomType.java similarity index 90% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomType.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomType.java index e9df48a082c3..d1dc6f4983b3 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomType.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomType.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomTypeParameterResolver.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomTypeParameterResolver.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomTypeParameterResolver.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomTypeParameterResolver.java index 2907826621d4..9a75754d2180 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomTypeParameterResolver.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomTypeParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/DoubleParameterResolver.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/DoubleParameterResolver.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/DoubleParameterResolver.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/DoubleParameterResolver.java index 3f4ee2c5487c..1659da6705a3 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/DoubleParameterResolver.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/DoubleParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/LongParameterResolver.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/LongParameterResolver.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/LongParameterResolver.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/LongParameterResolver.java index 9b796003a4f9..a59dc6f7a898 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/LongParameterResolver.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/LongParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfListsTypeBasedParameterResolver.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfListsTypeBasedParameterResolver.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfListsTypeBasedParameterResolver.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfListsTypeBasedParameterResolver.java index ce4f0f6ff0bb..b8a0172970e7 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfListsTypeBasedParameterResolver.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfListsTypeBasedParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfStringsParameterResolver.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfStringsParameterResolver.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfStringsParameterResolver.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfStringsParameterResolver.java index d8f94f09c90e..3d7d531e4b77 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfStringsParameterResolver.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfStringsParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NullIntegerParameterResolver.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NullIntegerParameterResolver.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NullIntegerParameterResolver.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NullIntegerParameterResolver.java index c44c9dd27876..7a73b82d8623 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NullIntegerParameterResolver.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NullIntegerParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NumberParameterResolver.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NumberParameterResolver.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NumberParameterResolver.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NumberParameterResolver.java index f1c826ae522a..66d8e4d7e85f 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NumberParameterResolver.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NumberParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveArrayParameterResolver.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveArrayParameterResolver.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveArrayParameterResolver.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveArrayParameterResolver.java index 2c6ce89a5c1e..d3fac903d770 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveArrayParameterResolver.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveArrayParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveIntegerParameterResolver.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveIntegerParameterResolver.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveIntegerParameterResolver.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveIntegerParameterResolver.java index 9893412bad23..e042489a471e 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveIntegerParameterResolver.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveIntegerParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/AutoCloseTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/AutoCloseTests.java new file mode 100644 index 000000000000..5c66a2ac0842 --- /dev/null +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/AutoCloseTests.java @@ -0,0 +1,734 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.engine.extension; + +import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD; +import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure; +import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message; + +import java.io.InputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AutoClose; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.fixtures.TrackLogRecords; +import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; +import org.junit.platform.commons.logging.LogRecordListener; +import org.junit.platform.testkit.engine.EngineExecutionResults; +import org.junit.platform.testkit.engine.Events; +import org.junit.platform.testkit.engine.Execution; + +/** + * Integration tests for {@link AutoClose @AutoClose} and the {@link AutoCloseExtension}. + * + * @since 5.11 + */ +class AutoCloseTests extends AbstractJupiterTestEngineTests { + + private static final List recorder = new ArrayList<>(); + + @BeforeEach + @AfterEach + void resetTracking() { + InstancePerClassTestCase.closed = false; + recorder.clear(); + } + + @Test + void blankCloseMethodName() { + Class testClass = BlankCloseMethodNameTestCase.class; + String msg = String.format("@AutoClose on field %s.field must specify a method name.", + testClass.getCanonicalName()); + Events tests = executeTestsForClass(testClass).testEvents(); + assertFailingWithMessage(tests, msg); + } + + @Test + void primitiveTypeCannotBeClosed() { + Class testClass = PrimitiveFieldTestCase.class; + String msg = String.format("@AutoClose is not supported on primitive field %s.x.", + testClass.getCanonicalName()); + Events tests = executeTestsForClass(testClass).testEvents(); + assertFailingWithMessage(tests, msg); + } + + @Test + void arrayCannotBeClosed() { + Class testClass = ArrayFieldTestCase.class; + String msg = String.format("@AutoClose is not supported on array field %s.x.", testClass.getCanonicalName()); + Events tests = executeTestsForClass(testClass).testEvents(); + assertFailingWithMessage(tests, msg); + } + + @Test + void nullCannotBeClosed(@TrackLogRecords LogRecordListener listener) { + Class testClass = NullCloseableFieldTestCase.class; + String msg = String.format("Cannot @AutoClose field %s.field because it is null.", + testClass.getCanonicalName()); + Events tests = executeTestsForClass(testClass).testEvents(); + tests.assertStatistics(stats -> stats.succeeded(1).failed(0)); + assertThat(listener.stream(Level.WARNING)).map(LogRecord::getMessage).anyMatch(msg::equals); + } + + @Test + void noCloseMethod() { + assertMissingCloseMethod(NoCloseMethodTestCase.class, "close"); + } + + @Test + void noShutdownMethod() { + assertMissingCloseMethod(NoShutdownMethodTestCase.class, "shutdown"); + } + + /** + * Tests prerequisites for the {@link AutoCloseSpy} implementation. + */ + @Test + void spyPermitsOnlyASingleAction() { + AutoCloseSpy spy = new AutoCloseSpy("preconditions"); + + spy.close(); + + assertThatIllegalStateException().isThrownBy(spy::run).withMessage("Already closed via close()"); + assertThatIllegalStateException().isThrownBy(spy::close).withMessage("Already closed via close()"); + assertThat(recorder).containsExactly("AutoCloseTests.preconditions.close()"); + } + + /** + * @see #3684 + */ + @Test + void fieldsAreProperlyClosedViaInterfaceMethods() { + // If the test method succeeds, that means there was no issue invoking + // the @AutoClose fields. No need to assert anything else for this use case. + executeTestsForClass(CloseMethodMustBeInvokedViaInterfaceTestCase.class)// + .testEvents()// + .assertStatistics(stats -> stats.succeeded(1)); + } + + @Test + void fieldsAreProperlyClosedWithInstancePerMethodTestClass() { + Events tests = executeTestsForClass(InstancePerMethodTestCase.class).testEvents(); + tests.assertStatistics(stats -> stats.succeeded(2)); + assertThat(recorder).containsExactly(// + // test1() + "InstancePerMethodTestCase.runnable.run()", // + "InstancePerMethodTestCase.closable.close()", // + // test2() + "InstancePerMethodTestCase.runnable.run()", // + "InstancePerMethodTestCase.closable.close()", // + // Class-level cleanup + "InstancePerMethodTestCase.staticClosable.close()"// + ); + } + + @Test + void fieldsAreProperlyClosedWithInstancePerClassTestClass() { + Events tests = executeTestsForClass(InstancePerClassTestCase.class).testEvents(); + tests.assertStatistics(stats -> stats.succeeded(2)); + assertThat(InstancePerClassTestCase.closed).isTrue(); + } + + @Test + void fieldsAreProperlyClosedWithNestedTestClassesWithInstancePerMethod() { + Events tests = executeTestsForClass(InstancePerMethodEnclosingTestCase.NestedTestCase.class).testEvents(); + tests.assertStatistics(stats -> stats.succeeded(1)); + assertThat(recorder).containsExactly(// + "NestedTestCase.nestedClosable.close()", // + "InstancePerMethodEnclosingTestCase.enclosingClosable.close()", // + "NestedTestCase.nestedStaticClosable.close()", // + "InstancePerMethodEnclosingTestCase.enclosingStaticClosable.close()"// + ); + + // Reset tracking + resetTracking(); + + tests = executeTestsForClass(InstancePerMethodEnclosingTestCase.class).testEvents(); + tests.assertStatistics(stats -> stats.succeeded(2)); + assertThat(recorder).containsExactly(// + "InstancePerMethodEnclosingTestCase.enclosingClosable.close()", // + "NestedTestCase.nestedClosable.close()", // + "InstancePerMethodEnclosingTestCase.enclosingClosable.close()", // + "NestedTestCase.nestedStaticClosable.close()", // + "InstancePerMethodEnclosingTestCase.enclosingStaticClosable.close()" // + ); + } + + @Test + void fieldsAreProperlyClosedWithNestedTestClassesWithInstancePerClass() { + // With test instance lifecycle "per class" mode, we actually expect the + // same behavior for the closing of all fields when the nested test class + // is run standalone AND when it's run along with its enclosing class. + String[] expected = { // + "NestedTestCase.nestedStaticClosable.close()", // + "NestedTestCase.nestedClosable.close()", // + "InstancePerClassEnclosingTestCase.enclosingStaticClosable.close()", // + "InstancePerClassEnclosingTestCase.enclosingClosable.close()" // + }; + + Events tests = executeTestsForClass(InstancePerClassEnclosingTestCase.NestedTestCase.class).testEvents(); + tests.assertStatistics(stats -> stats.succeeded(1)); + assertThat(recorder).containsExactly(expected); + + // Reset tracking + resetTracking(); + + tests = executeTestsForClass(InstancePerClassEnclosingTestCase.class).testEvents(); + tests.assertStatistics(stats -> stats.succeeded(2)); + assertThat(recorder).containsExactly(expected); + } + + @Test + void fieldsAreProperlyClosedWithinTestClassHierarchy() { + Events tests = executeTestsForClass(SuperTestCase.class).testEvents(); + tests.assertStatistics(stats -> stats.succeeded(1)); + assertThat(recorder).containsExactly(// + // superTest() + "SuperTestCase.superClosable.close()", // + // Class-level cleanup + "SuperTestCase.superStaticClosable.close()" // + ); + + // Reset tracking + resetTracking(); + + tests = executeTestsForClass(SubTestCase.class).testEvents(); + tests.assertStatistics(stats -> stats.succeeded(2)); + assertThat(recorder).containsExactly(// + // superTest() + "SubTestCase.subClosable.close()", // + "SuperTestCase.superClosable.close()", // + // subTest() + "SubTestCase.subClosable.close()", // + "SuperTestCase.superClosable.close()", // + // Class-level cleanup in subclass + "SubTestCase.subStaticClosable.close()", // + // Class-level cleanup in superclass + "SuperTestCase.superStaticClosable.close()" // + ); + } + + @Test + void allFieldsAreClosedIfAnyFieldThrowsAnException() { + // Prerequisites to ensure fields are "ordered" as expected (based on the hash codes for their names). + assertThat("staticField1".hashCode()).isLessThan("staticField2".hashCode()).isLessThan( + "staticField3".hashCode()); + assertThat("field1".hashCode()).isLessThan("field2".hashCode()).isLessThan("field3".hashCode()); + + Class testClass = FailingFieldsTestCase.class; + EngineExecutionResults allEvents = executeTestsForClass(testClass); + + Events tests = allEvents.testEvents(); + tests.assertStatistics(stats -> stats.succeeded(0).failed(1)); + + // Verify that ALL fields were closed in the proper order. + assertThat(recorder).containsExactly(// + "FailingFieldsTestCase.field1.close()", // + "FailingFieldsTestCase.field2.close()", // + "FailingFieldsTestCase.field3.close()", // + "FailingFieldsTestCase.staticField1.close()", // + "FailingFieldsTestCase.staticField2.close()", // + "FailingFieldsTestCase.staticField3.close()" // + ); + + // Test-level failures + assertThat(findFailure(tests, "test()")) // + .isExactlyInstanceOf(RuntimeException.class) // + .hasMessage("FailingFieldsTestCase.field1.close()")// + .hasNoCause()// + .hasSuppressedException(new RuntimeException("FailingFieldsTestCase.field2.close()")); + + Events containers = allEvents.containerEvents(); + containers.assertStatistics(stats -> stats.succeeded(1).failed(1)); + + // Container-level failures + assertThat(findFailure(containers, testClass.getSimpleName())) // + .isExactlyInstanceOf(RuntimeException.class) // + .hasMessage("FailingFieldsTestCase.staticField1.close()")// + .hasNoCause()// + .hasSuppressedException(new RuntimeException("FailingFieldsTestCase.staticField2.close()")); + } + + @Test + void allFieldsAreClosedIfAnyFieldThrowsAnExceptionWithNestedTestClassesWithInstancePerMethod() { + Class enclosingTestClass = FailingFieldsEnclosingTestCase.class; + Class nestedTestClass = FailingFieldsEnclosingTestCase.NestedTestCase.class; + + EngineExecutionResults allEvents = executeTestsForClass(nestedTestClass); + Events tests = allEvents.testEvents(); + tests.assertStatistics(stats -> stats.succeeded(0).failed(1)); + + // Verify that ALL fields were closed in the proper order. + assertThat(recorder).containsExactly(// + // Results from NestedTestCase instance + "NestedTestCase.nestedField1.close()", // + "NestedTestCase.nestedField2.close()", // + // Results from FailingFieldsEnclosingTestCase instance + "FailingFieldsEnclosingTestCase.enclosingField1.close()", // + "FailingFieldsEnclosingTestCase.enclosingField2.close()", // + // Results from NestedTestCase class + "NestedTestCase.nestedStaticField1.close()", // + "NestedTestCase.nestedStaticField2.close()", // + // Results from FailingFieldsEnclosingTestCase class + "FailingFieldsEnclosingTestCase.enclosingStaticField1.close()", // + "FailingFieldsEnclosingTestCase.enclosingStaticField2.close()"// + ); + + // Test-level failures + assertThat(findFailure(tests, "nestedTest()"))// + .isExactlyInstanceOf(RuntimeException.class)// + .hasMessage("NestedTestCase.nestedField1.close()")// + .hasNoCause()// + .hasSuppressedException(new RuntimeException("FailingFieldsEnclosingTestCase.enclosingField1.close()")); + + Events containers = allEvents.containerEvents(); + containers.assertStatistics(stats -> stats.succeeded(1).failed(2)); + + // Container-level failures + assertThat(findFailure(containers, nestedTestClass.getSimpleName()))// + .isExactlyInstanceOf(RuntimeException.class)// + .hasMessage("NestedTestCase.nestedStaticField1.close()")// + .hasNoCause()// + .hasNoSuppressedExceptions(); + assertThat(findFailure(containers, enclosingTestClass.getSimpleName()))// + .isExactlyInstanceOf(RuntimeException.class)// + .hasMessage("FailingFieldsEnclosingTestCase.enclosingStaticField1.close()")// + .hasNoCause()// + .hasNoSuppressedExceptions(); + + // Reset tracking + resetTracking(); + + allEvents = executeTestsForClass(enclosingTestClass); + tests = allEvents.testEvents(); + tests.assertStatistics(stats -> stats.succeeded(0).failed(2)); + + // Verify that ALL fields were closed in the proper order. + assertThat(recorder).containsExactly(// + // Results from FailingFieldsEnclosingTestCase instance + "FailingFieldsEnclosingTestCase.enclosingField1.close()", // + "FailingFieldsEnclosingTestCase.enclosingField2.close()", // + + // Results from NestedTestCase instance + "NestedTestCase.nestedField1.close()", // + "NestedTestCase.nestedField2.close()", // + // Results from FailingFieldsEnclosingTestCase instance + "FailingFieldsEnclosingTestCase.enclosingField1.close()", // + "FailingFieldsEnclosingTestCase.enclosingField2.close()", // + // Results from NestedTestCase class + "NestedTestCase.nestedStaticField1.close()", // + "NestedTestCase.nestedStaticField2.close()", // + // Results from FailingFieldsEnclosingTestCase class + "FailingFieldsEnclosingTestCase.enclosingStaticField1.close()", // + "FailingFieldsEnclosingTestCase.enclosingStaticField2.close()"// + ); + + // Test-level failures + assertThat(findFailure(tests, "enclosingTest()"))// + .isExactlyInstanceOf(RuntimeException.class)// + .hasMessage("FailingFieldsEnclosingTestCase.enclosingField1.close()")// + .hasNoCause()// + .hasNoSuppressedExceptions(); + assertThat(findFailure(tests, "nestedTest()"))// + .isExactlyInstanceOf(RuntimeException.class)// + .hasMessage("NestedTestCase.nestedField1.close()")// + .hasNoCause()// + .hasSuppressedException(new RuntimeException("FailingFieldsEnclosingTestCase.enclosingField1.close()")); + + containers = allEvents.containerEvents(); + containers.assertStatistics(stats -> stats.succeeded(1).failed(2)); + + // Container-level failures + assertThat(findFailure(containers, nestedTestClass.getSimpleName()))// + .isExactlyInstanceOf(RuntimeException.class)// + .hasMessage("NestedTestCase.nestedStaticField1.close()")// + .hasNoCause()// + .hasNoSuppressedExceptions(); + assertThat(findFailure(containers, enclosingTestClass.getSimpleName()))// + .isExactlyInstanceOf(RuntimeException.class)// + .hasMessage("FailingFieldsEnclosingTestCase.enclosingStaticField1.close()")// + .hasNoCause()// + .hasNoSuppressedExceptions(); + } + + private Throwable findFailure(Events tests, String displayName) { + return findExecution(tests, displayName)// + .getTerminationInfo().getExecutionResult().getThrowable().orElseThrow(); + } + + private static Execution findExecution(Events events, String displayName) { + return events.executions()// + .filter(execution -> execution.getTestDescriptor().getDisplayName().contains(displayName))// + .findFirst().get(); + } + + private static void assertFailingWithMessage(Events testEvents, String msg) { + testEvents// + .assertStatistics(stats -> stats.failed(1))// + .assertThatEvents().haveExactly(1, finishedWithFailure(message(msg))); + } + + private void assertMissingCloseMethod(Class testClass, String methodName) { + String msg = String.format("Cannot @AutoClose field %s.field because %s does not define method %s().", + testClass.getCanonicalName(), String.class.getName(), methodName); + Events tests = executeTestsForClass(testClass).testEvents(); + assertFailingWithMessage(tests, msg); + } + + interface TestInterface { + + @Test + default void test() { + } + } + + static class BlankCloseMethodNameTestCase implements TestInterface { + + @AutoClose("") + final String field = "blank"; + } + + static class PrimitiveFieldTestCase implements TestInterface { + + @AutoClose + final int x = 0; + } + + static class ArrayFieldTestCase implements TestInterface { + + @AutoClose + final int[] x = {}; + } + + static class NullCloseableFieldTestCase implements TestInterface { + + @AutoClose + final AutoCloseable field = null; + } + + static class NoCloseMethodTestCase implements TestInterface { + + @AutoClose + private final String field = ""; + } + + @Retention(RetentionPolicy.RUNTIME) + @AutoClose("shutdown") + @interface AutoShutdown { + } + + static class NoShutdownMethodTestCase implements TestInterface { + + @AutoShutdown + private final String field = ""; + } + + static class CloseMethodMustBeInvokedViaInterfaceTestCase implements TestInterface { + + @AutoClose + final InputStream inputStream = InputStream.nullInputStream(); + + @AutoClose("shutdown") + final ExecutorService service = Executors.newSingleThreadExecutor(); + } + + @TestInstance(PER_METHOD) + static class InstancePerMethodTestCase { + + @AutoClose + private static AutoCloseable staticClosable; + + @AutoClose + private static final AutoCloseable nullStatic = null; + + @AutoClose + private final AutoCloseable closable = new AutoCloseSpy("closable"); + + @AutoClose(" run ") // intentionally contains extra whitespace. + private final Runnable runnable = new AutoCloseSpy("runnable"); + + @AutoClose + private final AutoCloseable nullField = null; + + @BeforeAll + static void setup() { + staticClosable = new AutoCloseSpy("staticClosable"); + } + + @Test + void test1() { + } + + @Test + void test2() { + } + } + + @TestInstance(PER_CLASS) + static class InstancePerClassTestCase { + + static boolean closed = false; + + @AutoClose + final AutoCloseable field = () -> closed = true; + + @Test + void test1() { + assertThat(closed).isFalse(); + } + + @Test + void test2() { + assertThat(closed).isFalse(); + } + } + + @TestInstance(PER_METHOD) + static class InstancePerMethodEnclosingTestCase implements TestInterface { + + @AutoClose + static AutoCloseSpy enclosingStaticClosable; + + @AutoClose + final AutoCloseable enclosingClosable = new AutoCloseSpy("enclosingClosable"); + + @BeforeAll + static void setup() { + enclosingStaticClosable = new AutoCloseSpy("enclosingStaticClosable"); + } + + @Nested + @TestInstance(PER_METHOD) + class NestedTestCase implements TestInterface { + + @AutoClose + static AutoCloseSpy nestedStaticClosable; + + @AutoClose + final AutoCloseable nestedClosable = new AutoCloseSpy("nestedClosable"); + + @BeforeAll + static void setup() { + nestedStaticClosable = new AutoCloseSpy("nestedStaticClosable"); + } + } + } + + @TestInstance(PER_CLASS) + static class InstancePerClassEnclosingTestCase implements TestInterface { + + @AutoClose + static AutoCloseSpy enclosingStaticClosable; + + @AutoClose + final AutoCloseable enclosingClosable = new AutoCloseSpy("enclosingClosable"); + + @BeforeAll + static void setup() { + enclosingStaticClosable = new AutoCloseSpy("enclosingStaticClosable"); + } + + @Nested + @TestInstance(PER_CLASS) + class NestedTestCase implements TestInterface { + + @AutoClose + static AutoCloseSpy nestedStaticClosable; + + @AutoClose + final AutoCloseable nestedClosable = new AutoCloseSpy("nestedClosable"); + + @BeforeAll + static void setup() { + nestedStaticClosable = new AutoCloseSpy("nestedStaticClosable"); + } + } + } + + static class SuperTestCase { + + @AutoClose + static AutoCloseable superStaticClosable; + + @AutoClose + final AutoCloseable superClosable = new AutoCloseSpy("superClosable"); + + @BeforeAll + // WARNING: if this method is named setup() AND the @BeforeAll method in + // SubTestCase is also named setup(), the latter will "hide" the former. + static void superSetup() { + superStaticClosable = new AutoCloseSpy("superStaticClosable"); + } + + @Test + void superTest() { + } + } + + static class SubTestCase extends SuperTestCase { + + @AutoClose + static AutoCloseable subStaticClosable; + + @AutoClose + final AutoCloseable subClosable = new AutoCloseSpy("subClosable"); + + @BeforeAll + static void subSetup() { + subStaticClosable = new AutoCloseSpy("subStaticClosable"); + } + + @Test + void subTest() { + } + } + + static class FailingFieldsTestCase { + + @AutoClose + static AutoCloseable staticField1; + + @AutoClose + static AutoCloseable staticField2; + + @AutoClose + static AutoCloseable staticField3; + + @AutoClose + final AutoCloseable field1 = new AutoCloseSpy("field1", true); + + @AutoClose + final AutoCloseable field2 = new AutoCloseSpy("field2", true); + + @AutoClose + final AutoCloseable field3 = new AutoCloseSpy("field3", false); + + @BeforeAll + static void setup() { + staticField1 = new AutoCloseSpy("staticField1", true); + staticField2 = new AutoCloseSpy("staticField2", true); + staticField3 = new AutoCloseSpy("staticField3", false); + } + + @Test + void test() { + } + } + + static class FailingFieldsEnclosingTestCase { + + @AutoClose + static AutoCloseable enclosingStaticField1; + + @AutoClose + static AutoCloseable enclosingStaticField2; + + @AutoClose + final AutoCloseable enclosingField1 = new AutoCloseSpy("enclosingField1", true); + + @AutoClose + final AutoCloseable enclosingField2 = new AutoCloseSpy("enclosingField2", false); + + @BeforeAll + static void setup() { + enclosingStaticField1 = new AutoCloseSpy("enclosingStaticField1", true); + enclosingStaticField2 = new AutoCloseSpy("enclosingStaticField2", false); + } + + @Test + void enclosingTest() { + } + + @Nested + class NestedTestCase { + + @AutoClose + static AutoCloseable nestedStaticField1; + + @AutoClose + static AutoCloseable nestedStaticField2; + + @AutoClose + final AutoCloseable nestedField1 = new AutoCloseSpy("nestedField1", true); + + @AutoClose + final AutoCloseable nestedField2 = new AutoCloseSpy("nestedField2", false); + + @BeforeAll + static void setup() { + nestedStaticField1 = new AutoCloseSpy("nestedStaticField1", true); + nestedStaticField2 = new AutoCloseSpy("nestedStaticField2", false); + } + + @Test + void nestedTest() { + } + } + } + + static class AutoCloseSpy implements AutoCloseable, Runnable { + + private final String prefix; + private final boolean fail; + private String invokedMethod = null; + + AutoCloseSpy(String prefix) { + Class callerClass = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass(); + this.fail = false; + this.prefix = callerClass.getSimpleName() + "." + prefix + "."; + } + + AutoCloseSpy(String prefix, boolean fail) { + Class callerClass = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass(); + this.prefix = callerClass.getSimpleName() + "." + prefix + "."; + this.fail = fail; + } + + @Override + public void run() { + recordInvocation("run()"); + } + + @Override + public void close() { + recordInvocation("close()"); + } + + private void recordInvocation(String methodName) { + if (this.invokedMethod != null) { + throw new IllegalStateException("Already closed via " + this.invokedMethod); + } + this.invokedMethod = methodName; + String invocation = this.prefix + this.invokedMethod; + recorder.add(invocation); + if (this.fail) { + throw new RuntimeException(invocation); + } + } + } + +} diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterAllTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterAllTests.java similarity index 89% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterAllTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterAllTests.java index 57a128e46f1c..34c178c045e8 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterAllTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterAllTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -103,23 +103,31 @@ void beforeAllAndAfterAllCallbacksInSubSubclass() { assertThat(actualExceptionInAfterAllCallback).isEmpty(); } + /** + * Since static methods cannot be overridden, "static hiding" no longer occurs since 5.11. + */ @Test - void beforeAllAndAfterAllCallbacksInSubSubclassWithStaticMethodHiding() { + void beforeAllAndAfterAllCallbacksInSubSubclassWithoutStaticMethodHiding() { + // NOTE: both the @BeforeAll AND the @AfterAll methods in level 3 are + // executed as 1/2/3 due to the "stable" method sort order on the Platform. + // @formatter:off assertBeforeAllAndAfterAllCallbacks(ThirdLevelStaticHidingTestCase.class, "fooBeforeAllCallback", "barBeforeAllCallback", "bazBeforeAllCallback", "quuxBeforeAllCallback", - "beforeAllMethod-1-hidden", - "beforeAllMethod-2-hidden", - "beforeAllMethod-3", - "test-3", - // The @AfterAll methods are executed as 1/2/3 due to - // the "stable" method sort order on the Platform. - "afterAllMethod-1-hidden", - "afterAllMethod-2-hidden", - "afterAllMethod-3", + "beforeAllMethod-1", + "beforeAllMethod-2", + "beforeAllMethod-1-level3", + "beforeAllMethod-2-level3", + "beforeAllMethod-3-level3", + "test-3", + "afterAllMethod-1-level3", + "afterAllMethod-2-level3", + "afterAllMethod-3-level3", + "afterAllMethod-2", + "afterAllMethod-1", "quuxAfterAllCallback", "bazAfterAllCallback", "barAfterAllCallback", @@ -244,32 +252,32 @@ static class ThirdLevelStaticHidingTestCase extends SecondLevelTestCase { @BeforeAll static void beforeAll1() { - callSequence.add("beforeAllMethod-1-hidden"); + callSequence.add("beforeAllMethod-1-level3"); } @BeforeAll static void beforeAll2() { - callSequence.add("beforeAllMethod-2-hidden"); + callSequence.add("beforeAllMethod-2-level3"); } @BeforeAll static void beforeAll3() { - callSequence.add("beforeAllMethod-3"); + callSequence.add("beforeAllMethod-3-level3"); } @AfterAll static void afterAll1() { - callSequence.add("afterAllMethod-1-hidden"); + callSequence.add("afterAllMethod-1-level3"); } @AfterAll static void afterAll2() { - callSequence.add("afterAllMethod-2-hidden"); + callSequence.add("afterAllMethod-2-level3"); } @AfterAll static void afterAll3() { - callSequence.add("afterAllMethod-3"); + callSequence.add("afterAllMethod-3-level3"); } @Test diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterEachTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterEachTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterEachTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterEachTests.java index 93f03748c33c..8c7f59f7eda3 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterEachTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterEachTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterTestExecutionCallbackTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterTestExecutionCallbackTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterTestExecutionCallbackTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterTestExecutionCallbackTests.java index a06ceed8158d..89672560abba 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterTestExecutionCallbackTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterTestExecutionCallbackTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java new file mode 100644 index 000000000000..6a0645975813 --- /dev/null +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java @@ -0,0 +1,341 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.engine.extension; + +import static com.google.common.jimfs.Configuration.unix; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.nio.file.Files.createDirectory; +import static java.nio.file.Files.createFile; +import static java.nio.file.Files.createSymbolicLink; +import static java.nio.file.Files.createTempDirectory; +import static java.nio.file.Files.delete; +import static java.nio.file.Files.deleteIfExists; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.condition.OS.WINDOWS; +import static org.junit.jupiter.api.io.CleanupMode.ALWAYS; +import static org.junit.jupiter.api.io.CleanupMode.DEFAULT; +import static org.junit.jupiter.api.io.CleanupMode.NEVER; +import static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.nio.file.FileSystem; +import java.nio.file.Path; +import java.util.Optional; + +import com.google.common.jimfs.Jimfs; + +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.extension.AnnotatedElementContext; +import org.junit.jupiter.api.extension.ExtensionConfigurationException; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.io.CleanupMode; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.api.io.TempDirFactory; +import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; +import org.junit.jupiter.engine.execution.NamespaceAwareStore; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.engine.support.store.NamespacedHierarchicalStore; + +/** + * Integration tests for the creation and cleanup of the {@link TempDirectory}. + * + * @since 5.9 + */ +@DisplayName("Temporary directory") +class CloseablePathTests extends AbstractJupiterTestEngineTests { + + private final AnnotatedElementContext elementContext = mock(); + private final ExtensionContext extensionContext = mock(); + + private TempDirectory.CloseablePath closeablePath; + + @Target(METHOD) + @Retention(RUNTIME) + @ValueSource(classes = { File.class, Path.class }) + private @interface ElementTypeSource { + } + + @BeforeEach + void setUpExtensionContext() { + var store = new NamespaceAwareStore(new NamespacedHierarchicalStore<>(null), Namespace.GLOBAL); + when(extensionContext.getStore(any())).thenReturn(store); + } + + /** + * Integration tests for the creation of the {@link TempDirectory} based on the different result + * that {@link TempDirFactory#createTempDirectory(AnnotatedElementContext, ExtensionContext)} may provide. + * + * @since 5.11 + * + * @see TempDirFactory + */ + @Nested + @DisplayName("creation") + class Creation { + + private Path root; + + @BeforeEach + void setUpRootFolder() throws IOException { + root = createTempDirectory("root"); + } + + @AfterEach + void cleanupRoot() throws IOException { + delete(root); + } + + @DisplayName("succeeds if the factory returns a directory") + @ParameterizedTest + @ElementTypeSource + void factoryReturnsDirectoryDynamic(Class elementType) throws IOException { + TempDirFactory factory = (elementContext, extensionContext) -> createDirectory(root.resolve("directory")); + + closeablePath = TempDirectory.createTempDir(factory, DEFAULT, elementType, elementContext, + extensionContext); + assertThat(closeablePath.get()).isDirectory(); + + delete(closeablePath.get()); + } + + @DisplayName("succeeds if the factory returns a symbolic link to a directory") + @ParameterizedTest + @ElementTypeSource + @DisabledOnOs(WINDOWS) + void factoryReturnsSymbolicLinkToDirectory(Class elementType) throws IOException { + Path directory = createDirectory(root.resolve("directory")); + TempDirFactory factory = (elementContext, + extensionContext) -> createSymbolicLink(root.resolve("symbolicLink"), directory); + + closeablePath = TempDirectory.createTempDir(factory, DEFAULT, elementType, elementContext, + extensionContext); + assertThat(closeablePath.get()).isDirectory(); + + delete(closeablePath.get()); + delete(directory); + } + + @DisplayName("succeeds if the factory returns a directory on a non-default file system for a Path annotated element") + @Test + void factoryReturnsDirectoryOnNonDefaultFileSystemWithPath() throws IOException { + TempDirFactory factory = new JimfsFactory(); + + closeablePath = TempDirectory.createTempDir(factory, DEFAULT, Path.class, elementContext, extensionContext); + assertThat(closeablePath.get()).isDirectory(); + + delete(closeablePath.get()); + } + + @DisplayName("fails if the factory returns null") + @ParameterizedTest + @ElementTypeSource + void factoryReturnsNull(Class elementType) throws IOException { + TempDirFactory factory = spy(new Factory(null)); + + assertThatExtensionConfigurationExceptionIsThrownBy( + () -> TempDirectory.createTempDir(factory, DEFAULT, elementType, elementContext, extensionContext)); + + verify(factory).close(); + } + + @DisplayName("fails if the factory returns a file") + @ParameterizedTest + @ElementTypeSource + void factoryReturnsFile(Class elementType) throws IOException { + Path file = createFile(root.resolve("file")); + TempDirFactory factory = spy(new Factory(file)); + + assertThatExtensionConfigurationExceptionIsThrownBy( + () -> TempDirectory.createTempDir(factory, DEFAULT, elementType, elementContext, extensionContext)); + + verify(factory).close(); + assertThat(file).doesNotExist(); + } + + @DisplayName("fails if the factory returns a symbolic link to a file") + @ParameterizedTest + @ElementTypeSource + @DisabledOnOs(WINDOWS) + void factoryReturnsSymbolicLinkToFile(Class elementType) throws IOException { + Path file = createFile(root.resolve("file")); + Path symbolicLink = createSymbolicLink(root.resolve("symbolicLink"), file); + TempDirFactory factory = spy(new Factory(symbolicLink)); + + assertThatExtensionConfigurationExceptionIsThrownBy( + () -> TempDirectory.createTempDir(factory, DEFAULT, elementType, elementContext, extensionContext)); + + verify(factory).close(); + assertThat(symbolicLink).doesNotExist(); + + delete(file); + } + + @DisplayName("fails if the factory returns a directory on a non-default file system for a File annotated element") + @Test + void factoryReturnsDirectoryOnNonDefaultFileSystemWithFile() throws IOException { + TempDirFactory factory = spy(new JimfsFactory()); + + assertThatExceptionOfType(ExtensionConfigurationException.class)// + .isThrownBy(() -> TempDirectory.createTempDir(factory, DEFAULT, File.class, elementContext, + extensionContext))// + .withMessage("Failed to create default temp directory")// + .withCauseInstanceOf(PreconditionViolationException.class)// + .havingCause().withMessage("temp directory with non-default file system cannot be injected into " + + File.class.getName() + " target"); + + verify(factory).close(); + } + + // Mockito spying a lambda fails with: VM does not support modification of given type + private record Factory(Path path) implements TempDirFactory { + + @Override + public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) { + return path; + } + + } + + private static class JimfsFactory implements TempDirFactory { + + private final FileSystem fileSystem = Jimfs.newFileSystem(unix()); + + @Override + public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) + throws Exception { + return createDirectory(fileSystem.getPath("/").resolve("directory")); + } + + @Override + public void close() throws IOException { + TempDirFactory.super.close(); + } + } + + private static void assertThatExtensionConfigurationExceptionIsThrownBy(ThrowingCallable callable) { + assertThatExceptionOfType(ExtensionConfigurationException.class)// + .isThrownBy(callable)// + .withMessage("Failed to create default temp directory")// + .withCauseInstanceOf(PreconditionViolationException.class)// + .havingCause().withMessage("temp directory must be a directory"); + } + + } + + /** + * Integration tests for cleanup of the {@link TempDirectory} when the {@link CleanupMode} is + * set to {@link CleanupMode#ALWAYS}, {@link CleanupMode#NEVER}, or {@link CleanupMode#ON_SUCCESS}. + * + * @since 5.9 + * + * @see TempDir + * @see CleanupMode + */ + @Nested + @DisplayName("cleanup") + class Cleanup { + + private final TempDirFactory factory = spy(TempDirFactory.Standard.INSTANCE); + + @AfterEach + void cleanupTempDirectory() throws IOException { + deleteIfExists(closeablePath.get()); + } + + @DisplayName("is done for a cleanup mode of ALWAYS") + @ParameterizedTest + @ElementTypeSource + void always(Class elementType) throws IOException { + reset(factory); + + closeablePath = TempDirectory.createTempDir(factory, ALWAYS, elementType, elementContext, extensionContext); + assertThat(closeablePath.get()).isDirectory(); + + closeablePath.close(); + + verify(factory).close(); + assertThat(closeablePath.get()).doesNotExist(); + } + + @DisplayName("is not done for a cleanup mode of NEVER") + @ParameterizedTest + @ElementTypeSource + void never(Class elementType) throws IOException { + reset(factory); + + closeablePath = TempDirectory.createTempDir(factory, NEVER, elementType, elementContext, extensionContext); + assertThat(closeablePath.get()).isDirectory(); + + closeablePath.close(); + + verify(factory).close(); + assertThat(closeablePath.get()).exists(); + } + + @DisplayName("is not done for a cleanup mode of ON_SUCCESS, if there is an exception") + @ParameterizedTest + @ElementTypeSource + void onSuccessWithException(Class elementType) throws IOException { + reset(factory); + + when(extensionContext.getExecutionException()).thenReturn(Optional.of(new Exception())); + + closeablePath = TempDirectory.createTempDir(factory, ON_SUCCESS, elementType, elementContext, + extensionContext); + assertThat(closeablePath.get()).isDirectory(); + + closeablePath.close(); + + verify(factory).close(); + assertThat(closeablePath.get()).exists(); + } + + @DisplayName("is done for a cleanup mode of ON_SUCCESS, if there is no exception") + @ParameterizedTest + @ElementTypeSource + void onSuccessWithNoException(Class elementType) throws IOException { + reset(factory); + + when(extensionContext.getExecutionException()).thenReturn(Optional.empty()); + + closeablePath = TempDirectory.createTempDir(factory, ON_SUCCESS, elementType, elementContext, + extensionContext); + assertThat(closeablePath.get()).isDirectory(); + + closeablePath.close(); + + verify(factory).close(); + assertThat(closeablePath.get()).doesNotExist(); + } + + } + +} diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/EnigmaException.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/EnigmaException.java similarity index 88% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/EnigmaException.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/EnigmaException.java index db1823b1abb2..1db44a0f6198 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/EnigmaException.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/EnigmaException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/EventuallyInterruptibleInvocation.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/EventuallyInterruptibleInvocation.java similarity index 93% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/EventuallyInterruptibleInvocation.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/EventuallyInterruptibleInvocation.java index 811bde21c6b4..67d6314d4b6b 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/EventuallyInterruptibleInvocation.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/EventuallyInterruptibleInvocation.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExecutionConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExecutionConditionTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExecutionConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExecutionConditionTests.java index 33422f049fa9..b4c13b933d96 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExecutionConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExecutionConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExtensionContextExecutionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionContextExecutionTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExtensionContextExecutionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionContextExecutionTests.java index f87d38cbd995..8d5694ce3071 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExtensionContextExecutionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionContextExecutionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistrationViaParametersAndFieldsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistrationViaParametersAndFieldsTests.java similarity index 80% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistrationViaParametersAndFieldsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistrationViaParametersAndFieldsTests.java index 5b8a208d0492..d196ec732b58 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistrationViaParametersAndFieldsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistrationViaParametersAndFieldsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,12 +10,15 @@ package org.junit.jupiter.engine.extension; -import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.DynamicTest.dynamicTest; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.junit.platform.commons.util.AnnotationUtils.findAnnotatedFields; import static org.junit.platform.commons.util.ReflectionUtils.makeAccessible; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure; import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf; import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message; @@ -44,6 +47,7 @@ import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.TestInfo; @@ -51,7 +55,6 @@ import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.Extension; import org.junit.jupiter.api.extension.ExtensionContext; @@ -59,15 +62,21 @@ import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; import org.junit.jupiter.api.extension.TestTemplateInvocationContext; import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; import org.junit.jupiter.api.fixtures.TrackLogRecords; +import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; +import org.junit.jupiter.engine.config.JupiterConfiguration; import org.junit.jupiter.engine.execution.injection.sample.LongParameterResolver; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.logging.LogRecordListener; import org.junit.platform.commons.util.ExceptionUtils; import org.junit.platform.commons.util.ReflectionUtils; +import org.junit.platform.testkit.engine.EngineExecutionResults; /** * Integration tests that verify support for extension registration via @@ -75,6 +84,7 @@ * * @since 5.8 */ +@SuppressWarnings("JUnitMalformedDeclaration") class ExtensionRegistrationViaParametersAndFieldsTests extends AbstractJupiterTestEngineTests { @Test @@ -122,6 +132,12 @@ void testTemplateMethodParameter() { assertTestsSucceeded(TestTemplateMethodParameterTestCase.class, 2); } + @Test + void multipleRegistrationsViaParameter(@TrackLogRecords LogRecordListener listener) { + assertOneTestSucceeded(MultipleRegistrationsViaParameterTestCase.class); + assertThat(getRegisteredLocalExtensions(listener)).containsExactly("LongParameterResolver", "DummyExtension"); + } + @Test void staticField() { assertOneTestSucceeded(StaticFieldTestCase.class); @@ -137,10 +153,11 @@ void fieldsWithTestInstancePerClass() { assertOneTestSucceeded(TestInstancePerClassFieldTestCase.class); } - @Test - @TrackLogRecords - void multipleRegistrationsViaField(LogRecordListener listener) { - assertOneTestSucceeded(MultipleRegistrationsViaFieldTestCase.class); + @ParameterizedTest + @ValueSource(classes = { MultipleMixedRegistrationsViaFieldTestCase.class, + MultipleExtendWithRegistrationsViaFieldTestCase.class }) + void multipleRegistrationsViaField(Class testClass, @TrackLogRecords LogRecordListener listener) { + assertOneTestSucceeded(testClass); assertThat(getRegisteredLocalExtensions(listener)).containsExactly("LongParameterResolver", "DummyExtension"); } @@ -157,30 +174,11 @@ void duplicateRegistrationViaField() { finishedWithFailure(instanceOf(PreconditionViolationException.class), message(expectedMessage))); } - @Test - @TrackLogRecords - void registrationOrder(LogRecordListener listener) { - assertOneTestSucceeded(AllInOneWithTestInstancePerMethodTestCase.class); - assertThat(getRegisteredLocalExtensions(listener))// - .containsExactly(// - "ClassLevelExtension2", // @RegisterExtension on static field - "StaticField2", // @ExtendWith on static field - "ClassLevelExtension1", // @RegisterExtension on static field - "StaticField1", // @ExtendWith on static field - "ConstructorParameter", // @ExtendWith on parameter in constructor - "BeforeAllParameter", // @ExtendWith on parameter in static @BeforeAll method - "BeforeEachParameter", // @ExtendWith on parameter in @BeforeEach method - "AfterEachParameter", // @ExtendWith on parameter in @AfterEach method - "AfterAllParameter", // @ExtendWith on parameter in static @AfterAll method - "TestParameter", // @ExtendWith on parameter in @Test method - "InstanceLevelExtension1", // @RegisterExtension on instance field - "InstanceField1", // @ExtendWith on instance field - "InstanceLevelExtension2", // @RegisterExtension on instance field - "InstanceField2" // @ExtendWith on instance field - ); - - listener.clear(); - assertOneTestSucceeded(AllInOneWithTestInstancePerClassTestCase.class); + @ParameterizedTest(name = "{0}") + @ValueSource(classes = { AllInOneWithTestInstancePerMethodTestCase.class, + AllInOneWithTestInstancePerClassTestCase.class }) + void registrationOrder(Class testClass, @TrackLogRecords LogRecordListener listener) { + assertOneTestSucceeded(testClass); assertThat(getRegisteredLocalExtensions(listener))// .containsExactly(// "ClassLevelExtension2", // @RegisterExtension on static field @@ -200,20 +198,38 @@ void registrationOrder(LogRecordListener listener) { ); } + @Test + void registersProgrammaticTestInstancePostProcessors() { + assertOneTestSucceeded(ProgrammaticTestInstancePostProcessorTestCase.class); + } + + @Test + void createsExtensionPerInstance() { + var results = executeTests(request() // + .selectors(selectClass(InitializationPerInstanceTestCase.class)) // + .configurationParameter(JupiterConfiguration.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, "true") // + ); + assertTestsSucceeded(results, 100); + } + private List getRegisteredLocalExtensions(LogRecordListener listener) { - // @formatter:off - return listener.stream(MutableExtensionRegistry.class, Level.FINER) - .map(LogRecord::getMessage) - .filter(message -> message.contains("local extension")) - .map(message -> { - message = message.replaceAll("from source .+", ""); - int indexOfDollarSign = message.indexOf("$"); - int indexOfAtSign = message.indexOf("@"); - int endIndex = (indexOfDollarSign > 1 ? indexOfDollarSign : indexOfAtSign); - return message.substring(message.lastIndexOf('.') + 1, endIndex); - }) - .collect(toList()); - // @formatter:on + return listener.stream(MutableExtensionRegistry.class, Level.FINER) // + .map(LogRecord::getMessage) // + .filter(message -> message.contains("local extension")) // + .map(message -> { + message = message.replaceAll(" from source .+", ""); + int beginIndex = message.lastIndexOf('.') + 1; + if (message.contains("late-init")) { + return message.substring(beginIndex, message.indexOf("]")); + } + else { + int indexOfDollarSign = message.indexOf("$"); + int indexOfAtSign = message.indexOf("@"); + int endIndex = (indexOfDollarSign > 1 ? indexOfDollarSign : indexOfAtSign); + return message.substring(beginIndex, endIndex); + } + }) // + .toList(); } private void assertOneTestSucceeded(Class testClass) { @@ -221,7 +237,11 @@ private void assertOneTestSucceeded(Class testClass) { } private void assertTestsSucceeded(Class testClass, int expected) { - executeTestsForClass(testClass).testEvents().assertStatistics( + assertTestsSucceeded(executeTestsForClass(testClass), expected); + } + + private static void assertTestsSucceeded(EngineExecutionResults results, int expected) { + results.testEvents().assertStatistics( stats -> stats.started(expected).succeeded(expected).skipped(0).aborted(0).failed(0)); } @@ -502,7 +522,7 @@ Stream testFactory(TestInfo testInfo, @MagicParameter("method") Str assertThat(testInfo).isNotNull(); assertThat(text).isEqualTo("testFactory-1-method"); - return IntStream.of(2, 4).mapToObj(num -> dynamicTest("" + num, () -> assertTrue(num % 2 == 0))); + return IntStream.of(2, 4).mapToObj(num -> dynamicTest("" + num, () -> assertEquals(0, num % 2))); } @AfterEach @@ -555,14 +575,37 @@ private static TestTemplateInvocationContext emptyTestTemplateInvocationContext( } } - static class MultipleRegistrationsViaFieldTestCase { + @ExtendWith(LongParameterResolver.class) + static class MultipleRegistrationsViaParameterTestCase { + + @Test + void test(@ExtendWith(DummyExtension.class) @ExtendWith(LongParameterResolver.class) Long number) { + assertThat(number).isEqualTo(42L); + } + } + + static class MultipleMixedRegistrationsViaFieldTestCase { @ExtendWith(LongParameterResolver.class) @RegisterExtension - Extension dummy = new DummyExtension(); + DummyExtension dummy = new DummyExtension(); @Test - void test() { + void test(Long number) { + assertThat(number).isEqualTo(42L); + } + } + + static class MultipleExtendWithRegistrationsViaFieldTestCase { + + @SuppressWarnings("unused") + @ExtendWith(LongParameterResolver.class) + @ExtendWith(DummyExtension.class) + Object field; + + @Test + void test(Long number) { + assertThat(number).isEqualTo(42L); } } @@ -582,6 +625,7 @@ void test() { */ static class StaticFieldTestCase { + @SuppressWarnings("unused") @MagicField private static String staticField1; @@ -614,8 +658,8 @@ static class InstanceFieldTestCase { @Test void test() { - assertThat(instanceField1).isEqualTo("beforeEach - instanceField1"); - assertThat(instanceField2).isEqualTo("beforeEach - instanceField2"); + assertThat(instanceField1).isEqualTo("postProcessTestInstance - instanceField1"); + assertThat(instanceField2).isEqualTo("postProcessTestInstance - instanceField2"); } } @@ -635,13 +679,13 @@ static class TestInstancePerClassFieldTestCase { @BeforeAll void beforeAll() { assertThat(staticField).isEqualTo("beforeAll - staticField"); - assertThat(instanceField).isNull(); + assertThat(instanceField).isEqualTo("postProcessTestInstance - instanceField"); } @Test void test() { assertThat(staticField).isEqualTo("beforeAll - staticField"); - assertThat(instanceField).isEqualTo("beforeEach - instanceField"); + assertThat(instanceField).isEqualTo("postProcessTestInstance - instanceField"); } } @@ -674,11 +718,11 @@ static class AllInOneWithTestInstancePerMethodTestCase { @RegisterExtension @Order(1) - private Extension instanceLevelExtension1 = new InstanceLevelExtension1(); + private InstanceLevelExtension1 instanceLevelExtension1 = new InstanceLevelExtension1(); @RegisterExtension @Order(3) - Extension instanceLevelExtension2 = new InstanceLevelExtension2(); + InstanceLevelExtension2 instanceLevelExtension2 = new InstanceLevelExtension2(); AllInOneWithTestInstancePerMethodTestCase(@ConstructorParameter String text) { assertThat(text).isEqualTo("enigma"); @@ -696,8 +740,8 @@ void beforeEach(@BeforeEachParameter String text) { assertThat(text).isEqualTo("enigma"); assertThat(staticField1).isEqualTo("beforeAll - staticField1"); assertThat(staticField2).isEqualTo("beforeAll - staticField2"); - assertThat(instanceField1).isEqualTo("beforeEach - instanceField1"); - assertThat(instanceField2).isEqualTo("beforeEach - instanceField2"); + assertThat(instanceField1).isEqualTo("postProcessTestInstance - instanceField1"); + assertThat(instanceField2).isEqualTo("postProcessTestInstance - instanceField2"); } @Test @@ -705,8 +749,8 @@ void test(@TestParameter String text) { assertThat(text).isEqualTo("enigma"); assertThat(staticField1).isEqualTo("beforeAll - staticField1"); assertThat(staticField2).isEqualTo("beforeAll - staticField2"); - assertThat(instanceField1).isEqualTo("beforeEach - instanceField1"); - assertThat(instanceField2).isEqualTo("beforeEach - instanceField2"); + assertThat(instanceField1).isEqualTo("postProcessTestInstance - instanceField1"); + assertThat(instanceField2).isEqualTo("postProcessTestInstance - instanceField2"); } @AfterEach @@ -714,8 +758,8 @@ void afterEach(@AfterEachParameter String text) { assertThat(text).isEqualTo("enigma"); assertThat(staticField1).isEqualTo("beforeAll - staticField1"); assertThat(staticField2).isEqualTo("beforeAll - staticField2"); - assertThat(instanceField1).isEqualTo("beforeEach - instanceField1"); - assertThat(instanceField2).isEqualTo("beforeEach - instanceField2"); + assertThat(instanceField1).isEqualTo("postProcessTestInstance - instanceField1"); + assertThat(instanceField2).isEqualTo("postProcessTestInstance - instanceField2"); } @AfterAll @@ -735,6 +779,55 @@ static class AllInOneWithTestInstancePerClassTestCase extends AllInOneWithTestIn } } + static class ProgrammaticTestInstancePostProcessorTestCase { + + @RegisterExtension + static Extension resolver = new InstanceField2.Extension(); + + @InstanceField2 + String instanceField2; + + @Test + void test() { + assertThat(instanceField2).isEqualTo("postProcessTestInstance - instanceField2"); + } + } + + @Execution(CONCURRENT) + static class InitializationPerInstanceTestCase { + @RegisterExtension + Extension extension = new InstanceParameterResolver<>(this); + + @Nested + class Wrapper { + + @RegisterExtension + Extension extension = new InstanceParameterResolver<>(this); + + @RepeatedTest(100) + void test(InitializationPerInstanceTestCase outerInstance, Wrapper innerInstance) { + assertSame(InitializationPerInstanceTestCase.this, outerInstance); + assertSame(Wrapper.this, innerInstance); + } + + } + + private record InstanceParameterResolver(T instance) implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return instance.getClass().equals(parameterContext.getParameter().getType()); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return instance; + } + } + } + } @Target(ElementType.PARAMETER) @@ -840,7 +933,7 @@ class Extension extends BaseParameterExtension { class DummyExtension implements Extension { } -class BaseFieldExtension implements BeforeAllCallback, BeforeEachCallback { +class BaseFieldExtension implements BeforeAllCallback, TestInstancePostProcessor { private final Class annotationType; @@ -851,13 +944,13 @@ class BaseFieldExtension implements BeforeAllCallback, Bef } @Override - public final void beforeAll(ExtensionContext context) throws Exception { + public final void beforeAll(ExtensionContext context) { injectFields("beforeAll", context.getRequiredTestClass(), null, ReflectionUtils::isStatic); } @Override - public final void beforeEach(ExtensionContext context) throws Exception { - injectFields("beforeEach", context.getRequiredTestClass(), context.getRequiredTestInstance(), + public final void postProcessTestInstance(Object testInstance, ExtensionContext context) { + injectFields("postProcessTestInstance", context.getRequiredTestClass(), testInstance, ReflectionUtils::isNotStatic); } @@ -867,7 +960,7 @@ private void injectFields(String trigger, Class testClass, Object instance, P makeAccessible(field).set(instance, trigger + " - " + field.getName()); } catch (Throwable t) { - ExceptionUtils.throwAsUncheckedException(t); + throw ExceptionUtils.throwAsUncheckedException(t); } }); } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistryTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistryTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistryTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistryTests.java index cbe91c6ea9f6..180592fd09ec 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistryTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -39,7 +39,7 @@ */ class ExtensionRegistryTests { - private static final int NUM_DEFAULT_EXTENSIONS = 6; + private static final int NUM_DEFAULT_EXTENSIONS = 7; private final JupiterConfiguration configuration = mock(); diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/InvocationInterceptorTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/InvocationInterceptorTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/InvocationInterceptorTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/InvocationInterceptorTests.java index c04636c2367c..2dcc716ddc85 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/InvocationInterceptorTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/InvocationInterceptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -22,7 +22,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.EnumSet; import java.util.stream.Stream; @@ -43,6 +42,8 @@ import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.platform.commons.JUnitException; import org.junit.platform.engine.reporting.ReportEntry; import org.junit.platform.testkit.engine.EngineExecutionResults; @@ -126,17 +127,16 @@ void test() { } } - @TestFactory - Stream callsInterceptors() { + @ParameterizedTest(name = "{0}") + @EnumSource(InvocationType.class) + void callsInterceptors(InvocationType invocationType) { var results = executeTestsForClass(TestCaseWithThreeInterceptors.class); results.testEvents().assertStatistics(stats -> stats.failed(0).succeeded(3)); - return Arrays.stream(InvocationType.values()) // - .map(invocationType -> dynamicTest(invocationType.name(), () -> { - assertThat(getEvents(results, EnumSet.of(invocationType)).distinct()) // - .containsExactly("before:foo", "before:bar", "before:baz", "test", "after:baz", "after:bar", - "after:foo"); - })); + + assertThat(getEvents(results, EnumSet.of(invocationType)).distinct()) // + .containsExactly("before:foo", "before:bar", "before:baz", "test", "after:baz", "after:bar", + "after:foo"); } private Stream getEvents(EngineExecutionResults results, EnumSet types) { diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/LifecycleMethodExecutionExceptionHandlerTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/LifecycleMethodExecutionExceptionHandlerTests.java similarity index 77% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/LifecycleMethodExecutionExceptionHandlerTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/LifecycleMethodExecutionExceptionHandlerTests.java index 4214dcfb5679..b204cc241f45 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/LifecycleMethodExecutionExceptionHandlerTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/LifecycleMethodExecutionExceptionHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -66,30 +66,15 @@ void resetStatics() { throwExceptionAfterAll = true; handlerCalls.clear(); - SwallowExceptionHandler.beforeAllCalls = 0; - SwallowExceptionHandler.beforeEachCalls = 0; - SwallowExceptionHandler.afterEachCalls = 0; - SwallowExceptionHandler.afterAllCalls = 0; - - RethrowExceptionHandler.beforeAllCalls = 0; - RethrowExceptionHandler.beforeEachCalls = 0; - RethrowExceptionHandler.afterEachCalls = 0; - RethrowExceptionHandler.afterAllCalls = 0; - - ConvertExceptionHandler.beforeAllCalls = 0; - ConvertExceptionHandler.beforeEachCalls = 0; - ConvertExceptionHandler.afterEachCalls = 0; - ConvertExceptionHandler.afterAllCalls = 0; - - UnrecoverableExceptionHandler.beforeAllCalls = 0; - UnrecoverableExceptionHandler.beforeEachCalls = 0; - UnrecoverableExceptionHandler.afterEachCalls = 0; - UnrecoverableExceptionHandler.afterAllCalls = 0; - - ShouldNotBeCalledHandler.beforeAllCalls = 0; - ShouldNotBeCalledHandler.beforeEachCalls = 0; - ShouldNotBeCalledHandler.afterEachCalls = 0; - ShouldNotBeCalledHandler.afterAllCalls = 0; + SwallowExceptionHandler.callCounter.reset(); + + RethrowExceptionHandler.callCounter.reset(); + + ConvertExceptionHandler.callCounter.reset(); + + UnrecoverableExceptionHandler.callCounter.reset(); + + ShouldNotBeCalledHandler.callCounter.reset(); } @Test @@ -97,8 +82,8 @@ void classLevelExceptionHandlersRethrowException() { LauncherDiscoveryRequest request = request().selectors(selectClass(RethrowingTestCase.class)).build(); EngineExecutionResults executionResults = executeTests(request); - assertEquals(1, RethrowExceptionHandler.beforeAllCalls, "Exception should handled in @BeforeAll"); - assertEquals(1, RethrowExceptionHandler.afterAllCalls, "Exception should handled in @AfterAll"); + assertEquals(1, RethrowExceptionHandler.callCounter.beforeAllCalls, "Exception should handled in @BeforeAll"); + assertEquals(1, RethrowExceptionHandler.callCounter.afterAllCalls, "Exception should handled in @AfterAll"); executionResults.allEvents().assertEventsMatchExactly( // event(engine(), started()), // @@ -114,8 +99,10 @@ void testLevelExceptionHandlersRethrowException() { LauncherDiscoveryRequest request = request().selectors(selectClass(RethrowingTestCase.class)).build(); EngineExecutionResults executionResults = executeTests(request); - assertEquals(1, RethrowExceptionHandler.beforeEachCalls, "Exception should be handled in @BeforeEach"); - assertEquals(1, RethrowExceptionHandler.afterEachCalls, "Exception should be handled in @AfterEach"); + assertEquals(1, RethrowExceptionHandler.callCounter.beforeEachCalls, + "Exception should be handled in @BeforeEach"); + assertEquals(1, RethrowExceptionHandler.callCounter.afterEachCalls, + "Exception should be handled in @AfterEach"); executionResults.allEvents().assertEventsMatchExactly( // event(engine(), started()), // @@ -131,8 +118,8 @@ void classLevelExceptionHandlersConvertException() { LauncherDiscoveryRequest request = request().selectors(selectClass(ConvertingTestCase.class)).build(); EngineExecutionResults executionResults = executeTests(request); - assertEquals(1, ConvertExceptionHandler.beforeAllCalls, "Exception should handled in @BeforeAll"); - assertEquals(1, ConvertExceptionHandler.afterAllCalls, "Exception should handled in @AfterAll"); + assertEquals(1, ConvertExceptionHandler.callCounter.beforeAllCalls, "Exception should handled in @BeforeAll"); + assertEquals(1, ConvertExceptionHandler.callCounter.afterAllCalls, "Exception should handled in @AfterAll"); executionResults.allEvents().assertEventsMatchExactly( // event(engine(), started()), // @@ -148,8 +135,10 @@ void testLevelExceptionHandlersConvertException() { LauncherDiscoveryRequest request = request().selectors(selectClass(ConvertingTestCase.class)).build(); EngineExecutionResults executionResults = executeTests(request); - assertEquals(1, ConvertExceptionHandler.beforeEachCalls, "Exception should be handled in @BeforeEach"); - assertEquals(1, ConvertExceptionHandler.afterEachCalls, "Exception should be handled in @AfterEach"); + assertEquals(1, ConvertExceptionHandler.callCounter.beforeEachCalls, + "Exception should be handled in @BeforeEach"); + assertEquals(1, ConvertExceptionHandler.callCounter.afterEachCalls, + "Exception should be handled in @AfterEach"); executionResults.allEvents().assertEventsMatchExactly( // event(engine(), started()), // @@ -165,10 +154,13 @@ void exceptionHandlersSwallowException() { LauncherDiscoveryRequest request = request().selectors(selectClass(SwallowingTestCase.class)).build(); EngineExecutionResults executionResults = executeTests(request); - assertEquals(1, SwallowExceptionHandler.beforeAllCalls, "Exception should be handled in @BeforeAll"); - assertEquals(1, SwallowExceptionHandler.beforeEachCalls, "Exception should be handled in @BeforeEach"); - assertEquals(1, SwallowExceptionHandler.afterEachCalls, "Exception should be handled in @AfterEach"); - assertEquals(1, SwallowExceptionHandler.afterAllCalls, "Exception should be handled in @AfterAll"); + assertEquals(1, SwallowExceptionHandler.callCounter.beforeAllCalls, + "Exception should be handled in @BeforeAll"); + assertEquals(1, SwallowExceptionHandler.callCounter.beforeEachCalls, + "Exception should be handled in @BeforeEach"); + assertEquals(1, SwallowExceptionHandler.callCounter.afterEachCalls, + "Exception should be handled in @AfterEach"); + assertEquals(1, SwallowExceptionHandler.callCounter.afterAllCalls, "Exception should be handled in @AfterAll"); executionResults.allEvents().assertEventsMatchExactly( // event(engine(), started()), // @@ -183,10 +175,13 @@ void exceptionHandlersSwallowException() { void perClassLifecycleMethodsAreHandled() { LauncherDiscoveryRequest request = request().selectors(selectClass(PerClassLifecycleTestCase.class)).build(); EngineExecutionResults executionResults = executeTests(request); - assertEquals(2, SwallowExceptionHandler.beforeAllCalls, "Exception should be handled in @BeforeAll"); - assertEquals(1, SwallowExceptionHandler.beforeEachCalls, "Exception should be handled in @BeforeEach"); - assertEquals(1, SwallowExceptionHandler.afterEachCalls, "Exception should be handled in @AfterEach"); - assertEquals(2, SwallowExceptionHandler.afterAllCalls, "Exception should be handled in @AfterAll"); + assertEquals(2, SwallowExceptionHandler.callCounter.beforeAllCalls, + "Exception should be handled in @BeforeAll"); + assertEquals(1, SwallowExceptionHandler.callCounter.beforeEachCalls, + "Exception should be handled in @BeforeEach"); + assertEquals(1, SwallowExceptionHandler.callCounter.afterEachCalls, + "Exception should be handled in @AfterEach"); + assertEquals(2, SwallowExceptionHandler.callCounter.afterAllCalls, "Exception should be handled in @AfterAll"); executionResults.allEvents().assertEventsMatchExactly( // event(engine(), started()), // @@ -237,8 +232,10 @@ void unrecoverableExceptionsAreNotPropagatedInBeforeAll() { boolean unrecoverableExceptionThrown = executeThrowingOutOfMemoryException(); assertTrue(unrecoverableExceptionThrown, "Unrecoverable Exception should be thrown"); - assertEquals(1, UnrecoverableExceptionHandler.beforeAllCalls, "Exception should be handled in @BeforeAll"); - assertEquals(0, ShouldNotBeCalledHandler.beforeAllCalls, "Exception should not propagate in @BeforeAll"); + assertEquals(1, UnrecoverableExceptionHandler.callCounter.beforeAllCalls, + "Exception should be handled in @BeforeAll"); + assertEquals(0, ShouldNotBeCalledHandler.callCounter.beforeAllCalls, + "Exception should not propagate in @BeforeAll"); } @Test @@ -250,8 +247,10 @@ void unrecoverableExceptionsAreNotPropagatedInBeforeEach() { boolean unrecoverableExceptionThrown = executeThrowingOutOfMemoryException(); assertTrue(unrecoverableExceptionThrown, "Unrecoverable Exception should be thrown"); - assertEquals(1, UnrecoverableExceptionHandler.beforeEachCalls, "Exception should be handled in @BeforeEach"); - assertEquals(0, ShouldNotBeCalledHandler.beforeEachCalls, "Exception should not propagate in @BeforeEach"); + assertEquals(1, UnrecoverableExceptionHandler.callCounter.beforeEachCalls, + "Exception should be handled in @BeforeEach"); + assertEquals(0, ShouldNotBeCalledHandler.callCounter.beforeEachCalls, + "Exception should not propagate in @BeforeEach"); } @Test @@ -263,8 +262,10 @@ void unrecoverableExceptionsAreNotPropagatedInAfterEach() { boolean unrecoverableExceptionThrown = executeThrowingOutOfMemoryException(); assertTrue(unrecoverableExceptionThrown, "Unrecoverable Exception should be thrown"); - assertEquals(1, UnrecoverableExceptionHandler.afterEachCalls, "Exception should be handled in @AfterEach"); - assertEquals(0, ShouldNotBeCalledHandler.afterEachCalls, "Exception should not propagate in @AfterEach"); + assertEquals(1, UnrecoverableExceptionHandler.callCounter.afterEachCalls, + "Exception should be handled in @AfterEach"); + assertEquals(0, ShouldNotBeCalledHandler.callCounter.afterEachCalls, + "Exception should not propagate in @AfterEach"); } @Test @@ -276,8 +277,10 @@ void unrecoverableExceptionsAreNotPropagatedInAfterAll() { boolean unrecoverableExceptionThrown = executeThrowingOutOfMemoryException(); assertTrue(unrecoverableExceptionThrown, "Unrecoverable Exception should be thrown"); - assertEquals(1, UnrecoverableExceptionHandler.afterAllCalls, "Exception should be handled in @AfterAll"); - assertEquals(0, ShouldNotBeCalledHandler.afterAllCalls, "Exception should not propagate in @AfterAll"); + assertEquals(1, UnrecoverableExceptionHandler.callCounter.afterAllCalls, + "Exception should be handled in @AfterAll"); + assertEquals(0, ShouldNotBeCalledHandler.callCounter.afterAllCalls, + "Exception should not propagate in @AfterAll"); } private boolean executeThrowingOutOfMemoryException() { @@ -382,15 +385,12 @@ void afterAll() { // ------------------------------------------ static class RethrowExceptionHandler implements LifecycleMethodExecutionExceptionHandler { - static int beforeAllCalls = 0; - static int beforeEachCalls = 0; - static int afterEachCalls = 0; - static int afterAllCalls = 0; + static HandlerCallCounter callCounter = new HandlerCallCounter(); @Override public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - beforeAllCalls++; + callCounter.incrementBeforeAllCalls(); handlerCalls.add("RethrowExceptionBeforeAll"); throw throwable; } @@ -398,7 +398,7 @@ public void handleBeforeAllMethodExecutionException(ExtensionContext context, Th @Override public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - beforeEachCalls++; + callCounter.incrementBeforeEachCalls(); handlerCalls.add("RethrowExceptionBeforeEach"); throw throwable; } @@ -406,7 +406,7 @@ public void handleBeforeEachMethodExecutionException(ExtensionContext context, T @Override public void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - afterEachCalls++; + callCounter.incrementAfterEachCalls(); handlerCalls.add("RethrowExceptionAfterEach"); throw throwable; } @@ -414,57 +414,51 @@ public void handleAfterEachMethodExecutionException(ExtensionContext context, Th @Override public void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - afterAllCalls++; + callCounter.incrementAfterAllCalls(); handlerCalls.add("RethrowExceptionAfterAll"); throw throwable; } } static class SwallowExceptionHandler implements LifecycleMethodExecutionExceptionHandler { - static int beforeAllCalls = 0; - static int beforeEachCalls = 0; - static int afterEachCalls = 0; - static int afterAllCalls = 0; + static HandlerCallCounter callCounter = new HandlerCallCounter(); @Override public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable) { - beforeAllCalls++; + callCounter.incrementBeforeAllCalls(); handlerCalls.add("SwallowExceptionBeforeAll"); // Do not rethrow } @Override public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable) { - beforeEachCalls++; + callCounter.incrementBeforeEachCalls(); handlerCalls.add("SwallowExceptionBeforeEach"); // Do not rethrow } @Override public void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable) { - afterEachCalls++; + callCounter.incrementAfterEachCalls(); handlerCalls.add("SwallowExceptionAfterEach"); // Do not rethrow } @Override public void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable) { - afterAllCalls++; + callCounter.incrementAfterAllCalls(); handlerCalls.add("SwallowExceptionAfterAll"); // Do not rethrow } } static class ConvertExceptionHandler implements LifecycleMethodExecutionExceptionHandler { - static int beforeAllCalls = 0; - static int beforeEachCalls = 0; - static int afterEachCalls = 0; - static int afterAllCalls = 0; + static HandlerCallCounter callCounter = new HandlerCallCounter(); @Override public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - beforeAllCalls++; + callCounter.incrementBeforeAllCalls(); handlerCalls.add("ConvertExceptionBeforeAll"); throw new IOException(throwable); } @@ -472,7 +466,7 @@ public void handleBeforeAllMethodExecutionException(ExtensionContext context, Th @Override public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - beforeEachCalls++; + callCounter.incrementBeforeEachCalls(); handlerCalls.add("ConvertExceptionBeforeEach"); throw new IOException(throwable); } @@ -480,7 +474,7 @@ public void handleBeforeEachMethodExecutionException(ExtensionContext context, T @Override public void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - afterEachCalls++; + callCounter.incrementAfterEachCalls(); handlerCalls.add("ConvertExceptionAfterEach"); throw new IOException(throwable); } @@ -488,57 +482,51 @@ public void handleAfterEachMethodExecutionException(ExtensionContext context, Th @Override public void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - afterAllCalls++; + callCounter.incrementAfterAllCalls(); handlerCalls.add("ConvertExceptionAfterAll"); throw new IOException(throwable); } } static class UnrecoverableExceptionHandler implements LifecycleMethodExecutionExceptionHandler { - static int beforeAllCalls = 0; - static int beforeEachCalls = 0; - static int afterEachCalls = 0; - static int afterAllCalls = 0; + static HandlerCallCounter callCounter = new HandlerCallCounter(); @Override public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable) { - beforeAllCalls++; + callCounter.incrementBeforeAllCalls(); handlerCalls.add("UnrecoverableExceptionBeforeAll"); throw new OutOfMemoryError(); } @Override public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable) { - beforeEachCalls++; + callCounter.incrementBeforeEachCalls(); handlerCalls.add("UnrecoverableExceptionBeforeEach"); throw new OutOfMemoryError(); } @Override public void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable) { - afterEachCalls++; + callCounter.incrementAfterEachCalls(); handlerCalls.add("UnrecoverableExceptionAfterEach"); throw new OutOfMemoryError(); } @Override public void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable) { - afterAllCalls++; + callCounter.incrementAfterAllCalls(); handlerCalls.add("UnrecoverableExceptionAfterAll"); throw new OutOfMemoryError(); } } static class ShouldNotBeCalledHandler implements LifecycleMethodExecutionExceptionHandler { - static int beforeAllCalls = 0; - static int beforeEachCalls = 0; - static int afterEachCalls = 0; - static int afterAllCalls = 0; + static HandlerCallCounter callCounter = new HandlerCallCounter(); @Override public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - beforeAllCalls++; + callCounter.incrementBeforeAllCalls(); handlerCalls.add("ShouldNotBeCalledBeforeAll"); throw throwable; } @@ -546,7 +534,7 @@ public void handleBeforeAllMethodExecutionException(ExtensionContext context, Th @Override public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - ShouldNotBeCalledHandler.beforeEachCalls++; + ShouldNotBeCalledHandler.callCounter.incrementBeforeEachCalls(); handlerCalls.add("ShouldNotBeCalledBeforeEach"); throw throwable; } @@ -554,7 +542,7 @@ public void handleBeforeEachMethodExecutionException(ExtensionContext context, T @Override public void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - afterEachCalls++; + callCounter.incrementAfterEachCalls(); handlerCalls.add("ShouldNotBeCalledAfterEach"); throw throwable; } @@ -562,9 +550,44 @@ public void handleAfterEachMethodExecutionException(ExtensionContext context, Th @Override public void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - afterAllCalls++; + callCounter.incrementAfterAllCalls(); handlerCalls.add("ShouldNotBeCalledAfterAll"); throw throwable; } } + + static class HandlerCallCounter { + private int beforeAllCalls; + private int beforeEachCalls; + private int afterEachCalls; + private int afterAllCalls; + + public HandlerCallCounter() { + reset(); + } + + public void reset() { + this.beforeAllCalls = 0; + this.beforeEachCalls = 0; + this.afterEachCalls = 0; + this.afterAllCalls = 0; + } + + public void incrementBeforeAllCalls() { + beforeAllCalls++; + } + + public void incrementBeforeEachCalls() { + beforeEachCalls++; + } + + public void incrementAfterEachCalls() { + afterEachCalls++; + } + + public void incrementAfterAllCalls() { + afterAllCalls++; + } + + } } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/OrderedClassTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedClassTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/OrderedClassTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedClassTests.java index 09026c6b1362..19d28c9fcbb9 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/OrderedClassTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedClassTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -106,8 +106,7 @@ void orderAnnotationOnNestedTestClassesWithGlobalConfig() { } @Test - @TrackLogRecords - void orderAnnotationOnNestedTestClassesWithLocalConfig(LogRecordListener listener) { + void orderAnnotationOnNestedTestClassesWithLocalConfig(@TrackLogRecords LogRecordListener listener) { executeTests(ClassOrderer.class, selectClass(OuterWithLocalConfig.class))// .assertStatistics(stats -> stats.succeeded(callSequence.size())); diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java index ed86fd2a2448..6291a7b4ecfa 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -179,8 +179,7 @@ void defaultOrderer() { } @Test - @TrackLogRecords - void randomWithBogusSeedRepeatedly(LogRecordListener listener) { + void randomWithBogusSeedRepeatedly(@TrackLogRecords LogRecordListener listener) { var seed = "explode"; var expectedMessagePattern = Pattern.compile( "Failed to convert configuration parameter \\[" + Pattern.quote(Random.RANDOM_SEED_PROPERTY_NAME) @@ -209,8 +208,7 @@ void randomWithBogusSeedRepeatedly(LogRecordListener listener) { } @Test - @TrackLogRecords - void randomWithDifferentSeedConsecutively(LogRecordListener listener) { + void randomWithDifferentSeedConsecutively(@TrackLogRecords LogRecordListener listener) { Set uniqueSequences = new HashSet<>(); for (var i = 0; i < 10; i++) { @@ -240,8 +238,7 @@ void randomWithDifferentSeedConsecutively(LogRecordListener listener) { } @Test - @TrackLogRecords - void randomWithCustomSeed(LogRecordListener listener) { + void randomWithCustomSeed(@TrackLogRecords LogRecordListener listener) { var seed = "42"; var expectedMessage = "Using custom seed for configuration parameter [" + Random.RANDOM_SEED_PROPERTY_NAME + "] with value [" + seed + "]."; @@ -268,8 +265,7 @@ void randomWithCustomSeed(LogRecordListener listener) { } @Test - @TrackLogRecords - void misbehavingMethodOrdererThatAddsElements(LogRecordListener listener) { + void misbehavingMethodOrdererThatAddsElements(@TrackLogRecords LogRecordListener listener) { Class testClass = MisbehavingByAddingTestCase.class; executeTestsInParallel(testClass).assertStatistics(stats -> stats.succeeded(2)); @@ -283,8 +279,7 @@ void misbehavingMethodOrdererThatAddsElements(LogRecordListener listener) { } @Test - @TrackLogRecords - void misbehavingMethodOrdererThatRemovesElements(LogRecordListener listener) { + void misbehavingMethodOrdererThatRemovesElements(@TrackLogRecords LogRecordListener listener) { Class testClass = MisbehavingByRemovingTestCase.class; executeTestsInParallel(testClass).assertStatistics(stats -> stats.succeeded(3)); diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/OrderedProgrammaticExtensionRegistrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedProgrammaticExtensionRegistrationTests.java similarity index 59% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/OrderedProgrammaticExtensionRegistrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedProgrammaticExtensionRegistrationTests.java index 6d74a431d4b7..126f662d47ef 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/OrderedProgrammaticExtensionRegistrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedProgrammaticExtensionRegistrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,7 @@ package org.junit.jupiter.engine.extension; +import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Order.DEFAULT; import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; @@ -41,7 +42,7 @@ */ class OrderedProgrammaticExtensionRegistrationTests extends AbstractJupiterTestEngineTests { - private static final List callSequence = new ArrayList<>(); + private static final List callSequence = new ArrayList<>(); /** * This method basically verifies the implementation of @@ -69,17 +70,35 @@ void clearCallSequence() { @Test void instanceLevelWithDefaultOrder() { - assertOutcome(DefaultOrderInstanceLevelExtensionRegistrationTestCase.class, 1, 2, 3); + Class testClass = DefaultOrderInstanceLevelExtensionRegistrationTestCase.class; + String testClassName = testClass.getSimpleName(); + assertOutcome(testClass, // + testClassName + " :: extension1 :: before test", // + testClassName + " :: extension2 :: before test", // + testClassName + " :: extension3 :: before test" // + ); } @Test void instanceLevelWithExplicitOrder() { - assertOutcome(ExplicitOrderInstanceLevelExtensionRegistrationTestCase.class, 3, 2, 1); + Class testClass = ExplicitOrderInstanceLevelExtensionRegistrationTestCase.class; + String testClassName = testClass.getSimpleName(); + assertOutcome(testClass, // + testClassName + " :: extension3 :: before test", // + testClassName + " :: extension2 :: before test", // + testClassName + " :: extension1 :: before test" // + ); } @Test void instanceLevelWithDefaultOrderAndExplicitOrder() { - assertOutcome(DefaultOrderAndExplicitOrderInstanceLevelExtensionRegistrationTestCase.class, 3, 1, 2); + Class testClass = DefaultOrderAndExplicitOrderInstanceLevelExtensionRegistrationTestCase.class; + String testClassName = testClass.getSimpleName(); + assertOutcome(testClass, // + testClassName + " :: extension3 :: before test", // + testClassName + " :: extension1 :: before test", // + testClassName + " :: extension2 :: before test" // + ); } /** @@ -91,44 +110,90 @@ void instanceLevelWithDefaultOrderAndExplicitOrder() { */ @Test void instanceLevelWithDefaultOrderPlusOneAndDefaultOrder() { - assertOutcome(DefaultOrderPlusOneAndDefaultOrderInstanceLevelExtensionRegistrationTestCase.class, 1, 3, 2); + Class testClass = DefaultOrderPlusOneAndDefaultOrderInstanceLevelExtensionRegistrationTestCase.class; + String testClassName = testClass.getSimpleName(); + assertOutcome(testClass, // + testClassName + " :: extension1 :: after test", // + testClassName + " :: extension3 :: after test", // + testClassName + " :: extension2 :: after test" // + ); } @Test void instanceLevelWithDefaultOrderAndExplicitOrderWithTestInstancePerClassLifecycle() { - assertOutcome( - DefaultOrderAndExplicitOrderInstanceLevelExtensionRegistrationWithTestInstancePerClassLifecycleTestCase.class, - 3, 1, 2); + Class testClass = DefaultOrderAndExplicitOrderInstanceLevelExtensionRegistrationWithTestInstancePerClassLifecycleTestCase.class; + String testClassName = testClass.getSimpleName(); + assertOutcome(testClass, // + testClassName + " :: extension3 :: before test", // + testClassName + " :: extension1 :: before test", // + testClassName + " :: extension2 :: before test" // + ); } @Test void classLevelWithDefaultOrderAndExplicitOrder() { - assertOutcome(DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class, 3, 1, 2); + Class testClass = DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class; + String testClassName = testClass.getSimpleName(); + assertOutcome(testClass, // + testClassName + " :: extension3 :: before test", // + testClassName + " :: extension1 :: before test", // + testClassName + " :: extension2 :: before test" // + ); } @Test void classLevelWithDefaultOrderAndExplicitOrderInheritedFromSuperclass() { - assertOutcome(InheritedDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class, 3, 1, 2); + Class testClass = InheritedDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class; + Class parent = DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class; + String parentName = parent.getSimpleName(); + assertOutcome(testClass, // + parentName + " :: extension3 :: before test", // + parentName + " :: extension1 :: before test", // + parentName + " :: extension2 :: before test" // + ); } @Test - void classLevelWithDefaultOrderShadowingOrderFromSuperclass() { - assertOutcome(DefaultOrderShadowingDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class, 1, - 2, 3); + void classLevelWithDefaultOrderDoesNotShadowExtensionFromSuperclass() { + Class testClass = DefaultOrderShadowingDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class; + String testClassName = testClass.getSimpleName(); + Class parent = DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class; + String parentName = parent.getSimpleName(); + assertOutcome(testClass, // + parentName + " :: extension3 :: before test", // + parentName + " :: extension1 :: before test", // + parentName + " :: extension2 :: before test", // + testClassName + " :: extension3 :: before test" // + ); } @Test - void classLevelWithExplicitOrderShadowingOrderFromSuperclass() { - assertOutcome(ExplicitOrderShadowingDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class, - 3, 2, 1); + void classLevelWithExplicitOrderDoesNotShadowExtensionFromSuperclass() { + Class testClass = ExplicitOrderShadowingDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class; + String testClassName = testClass.getSimpleName(); + Class parent = DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class; + String parentName = parent.getSimpleName(); + assertOutcome(testClass, // + parentName + " :: extension3 :: before test", // + testClassName + " :: extension2 :: before test", // + parentName + " :: extension1 :: before test", // + parentName + " :: extension2 :: before test" // + ); } @Test void classLevelWithDefaultOrderAndExplicitOrderFromInterface() { - assertOutcome(DefaultOrderAndExplicitOrderExtensionRegistrationFromInterfaceTestCase.class, 3, 1, 2); + Class testClass = DefaultOrderAndExplicitOrderExtensionRegistrationFromInterfaceTestCase.class; + Class testInterface = DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationInterface.class; + String interfaceName = testInterface.getSimpleName(); + assertOutcome(testClass, // + interfaceName + " :: extension3 :: before test", // + interfaceName + " :: extension1 :: before test", // + interfaceName + " :: extension2 :: before test" // + ); } - private void assertOutcome(Class testClass, Integer... values) { + private void assertOutcome(Class testClass, String... values) { executeTestsForClass(testClass).testEvents().assertStatistics(stats -> stats.succeeded(1)); assertThat(callSequence).containsExactly(values); } @@ -280,30 +345,32 @@ static class DefaultOrderAndExplicitOrderExtensionRegistrationFromInterfaceTestC private static class BeforeEachExtension implements BeforeEachCallback { - private final int id; + private final String prefix; BeforeEachExtension(int id) { - this.id = id; + Class callerClass = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass(); + this.prefix = callerClass.getSimpleName() + " :: extension" + id + " :: before "; } @Override public void beforeEach(ExtensionContext context) { - callSequence.add(this.id); + callSequence.add(this.prefix + context.getRequiredTestMethod().getName()); } } private static class AfterEachExtension implements AfterEachCallback { - private final int id; + private final String prefix; AfterEachExtension(int id) { - this.id = id; + Class callerClass = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass(); + this.prefix = callerClass.getSimpleName() + " :: extension" + id + " :: after "; } @Override public void afterEach(ExtensionContext context) { - callSequence.add(this.id); + callSequence.add(this.prefix + context.getRequiredTestMethod().getName()); } } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ParameterResolverTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ParameterResolverTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ParameterResolverTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ParameterResolverTests.java index 9809dc3af816..5aa8f1205d64 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ParameterResolverTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ParameterResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ProgrammaticExtensionRegistrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ProgrammaticExtensionRegistrationTests.java similarity index 87% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ProgrammaticExtensionRegistrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ProgrammaticExtensionRegistrationTests.java index 5ae50c4cf50d..23e4cc9ec564 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ProgrammaticExtensionRegistrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ProgrammaticExtensionRegistrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,11 +10,13 @@ package org.junit.jupiter.engine.extension; +import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE; import static org.assertj.core.api.Assertions.allOf; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; +import static org.junit.platform.commons.util.ReflectionUtils.makeAccessible; import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure; import static org.junit.platform.testkit.engine.TestExecutionResultConditions.cause; import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf; @@ -47,7 +49,6 @@ import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.util.AnnotationUtils; import org.junit.platform.commons.util.ExceptionUtils; -import org.junit.platform.commons.util.ReflectionUtils; import org.junit.platform.testkit.engine.EngineExecutionResults; /** @@ -61,6 +62,11 @@ class ProgrammaticExtensionRegistrationTests extends AbstractJupiterTestEngineTe private static final List callSequence = new ArrayList<>(); + @BeforeEach + void clearCallSequence() { + callSequence.clear(); + } + @Test void instanceLevel() { assertOneTestSucceeded(InstanceLevelExtensionRegistrationTestCase.class); @@ -92,14 +98,13 @@ void classLevelFromInterface() { } @Test - void instanceLevelWithInheritedAndHiddenExtensions() { - callSequence.clear(); + void instanceLevelWithInheritedExtensions() { Class testClass = InstanceLevelExtensionRegistrationParentTestCase.class; String parent = testClass.getSimpleName(); assertOneTestSucceeded(testClass); assertThat(callSequence).containsExactly( // - parent + " :: extension1: before test", // - parent + " :: extension2: before test" // + parent + " :: extension1 :: before test", // + parent + " :: extension2 :: before test" // ); callSequence.clear(); @@ -107,21 +112,21 @@ void instanceLevelWithInheritedAndHiddenExtensions() { String child = testClass.getSimpleName(); assertOneTestSucceeded(testClass); assertThat(callSequence).containsExactly( // - parent + " :: extension1: before test", // - child + " :: extension2: before test", // - child + " :: extension3: before test" // + parent + " :: extension1 :: before test", // + parent + " :: extension2 :: before test", // + child + " :: extension2 :: before test", // + child + " :: extension3 :: before test" // ); } @Test - void classLevelWithInheritedAndHiddenExtensions() { - callSequence.clear(); + void classLevelWithInheritedExtensions() { Class testClass = ClassLevelExtensionRegistrationParentTestCase.class; String parent = testClass.getSimpleName(); assertOneTestSucceeded(testClass); assertThat(callSequence).containsExactly( // - parent + " :: extension1: before test", // - parent + " :: extension2: before test" // + parent + " :: extension1 :: before test", // + parent + " :: extension2 :: before test" // ); callSequence.clear(); @@ -129,9 +134,10 @@ void classLevelWithInheritedAndHiddenExtensions() { String child = testClass.getSimpleName(); assertOneTestSucceeded(testClass); assertThat(callSequence).containsExactly( // - parent + " :: extension1: before test", // - child + " :: extension2: before test", // - child + " :: extension3: before test" // + parent + " :: extension1 :: before test", // + parent + " :: extension2 :: before test", // + child + " :: extension2 :: before test", // + child + " :: extension3 :: before test" // ); } @@ -140,7 +146,6 @@ void classLevelWithInheritedAndHiddenExtensions() { */ @Test void instanceLevelWithFieldThatDoesNotImplementAnExtensionApi() { - callSequence.clear(); assertOneTestSucceeded(InstanceLevelCustomExtensionApiTestCase.class); assertThat(callSequence).containsExactly( // CustomExtensionImpl.class.getSimpleName() + " :: before test", // @@ -153,7 +158,6 @@ void instanceLevelWithFieldThatDoesNotImplementAnExtensionApi() { */ @Test void classLevelWithFieldThatDoesNotImplementAnExtensionApi() { - callSequence.clear(); assertOneTestSucceeded(ClassLevelCustomExtensionApiTestCase.class); assertThat(callSequence).containsExactly( // CustomExtensionImpl.class.getSimpleName() + " :: before test", // @@ -474,14 +478,10 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte static class ClassLevelExtensionRegistrationParentTestCase { @RegisterExtension - static BeforeEachCallback extension1 = context -> callSequence.add( - ClassLevelExtensionRegistrationParentTestCase.class.getSimpleName() + " :: extension1: before " - + context.getRequiredTestMethod().getName()); + static Extension extension1 = new BeforeEachExtension(1); @RegisterExtension - static BeforeEachCallback extension2 = context -> callSequence.add( - ClassLevelExtensionRegistrationParentTestCase.class.getSimpleName() + " :: extension2: before " - + context.getRequiredTestMethod().getName()); + static Extension extension2 = new BeforeEachExtension(2); @Test void test() { @@ -491,30 +491,22 @@ void test() { static class ClassLevelExtensionRegistrationChildTestCase extends ClassLevelExtensionRegistrationParentTestCase { - // "Hides" ClassLevelExtensionRegistrationParentTestCase.extension2 + // "Hides" ClassLevelExtensionRegistrationParentTestCase.extension2 in legacy mode @RegisterExtension - static BeforeEachCallback extension2 = context -> callSequence.add( - ClassLevelExtensionRegistrationChildTestCase.class.getSimpleName() + " :: extension2: before " - + context.getRequiredTestMethod().getName()); + static Extension extension2 = new BeforeEachExtension(2); @RegisterExtension - static BeforeEachCallback extension3 = context -> callSequence.add( - ClassLevelExtensionRegistrationChildTestCase.class.getSimpleName() + " :: extension3: before " - + context.getRequiredTestMethod().getName()); + static Extension extension3 = new BeforeEachExtension(3); } static class InstanceLevelExtensionRegistrationParentTestCase { @RegisterExtension - BeforeEachCallback extension1 = context -> callSequence.add( - InstanceLevelExtensionRegistrationParentTestCase.class.getSimpleName() + " :: extension1: before " - + context.getRequiredTestMethod().getName()); + Extension extension1 = new BeforeEachExtension(1); @RegisterExtension - BeforeEachCallback extension2 = context -> callSequence.add( - InstanceLevelExtensionRegistrationParentTestCase.class.getSimpleName() + " :: extension2: before " - + context.getRequiredTestMethod().getName()); + Extension extension2 = new BeforeEachExtension(2); @Test void test() { @@ -525,16 +517,28 @@ void test() { static class InstanceLevelExtensionRegistrationChildTestCase extends InstanceLevelExtensionRegistrationParentTestCase { - // "Hides" InstanceLevelExtensionRegistrationParentTestCase.extension2 + // "Hides" InstanceLevelExtensionRegistrationParentTestCase.extension2 in legacy mode @RegisterExtension - BeforeEachCallback extension2 = context -> callSequence.add( - InstanceLevelExtensionRegistrationChildTestCase.class.getSimpleName() + " :: extension2: before " - + context.getRequiredTestMethod().getName()); + Extension extension2 = new BeforeEachExtension(2); @RegisterExtension - BeforeEachCallback extension3 = context -> callSequence.add( - InstanceLevelExtensionRegistrationChildTestCase.class.getSimpleName() + " :: extension3: before " - + context.getRequiredTestMethod().getName()); + Extension extension3 = new BeforeEachExtension(3); + + } + + private static class BeforeEachExtension implements BeforeEachCallback { + + private final String prefix; + + BeforeEachExtension(int id) { + Class callerClass = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass(); + this.prefix = callerClass.getSimpleName() + " :: extension" + id + " :: before "; + } + + @Override + public void beforeEach(ExtensionContext context) { + callSequence.add(this.prefix + context.getRequiredTestMethod().getName()); + } } @@ -704,11 +708,10 @@ public void postProcessTestInstance(Object testInstance, ExtensionContext contex .findFirst() .ifPresent(field -> { try { - ReflectionUtils.makeAccessible(field); - field.set(testInstance, new CrystalBall("Outlook good")); + makeAccessible(field).set(testInstance, new CrystalBall("Outlook good")); } catch (Throwable t) { - ExceptionUtils.throwAsUncheckedException(t); + throw ExceptionUtils.throwAsUncheckedException(t); } }); // @formatter:on diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/RepeatedTestTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/RepeatedTestTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/RepeatedTestTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/RepeatedTestTests.java index 8a8c8f2b2248..42037190f24a 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/RepeatedTestTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/RepeatedTestTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocationTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocationTests.java index 81630493d66e..d7440eb1c985 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocationTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocationTests.java index c8d51e7987a8..23307e43dd7b 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ServiceLoaderExtension.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ServiceLoaderExtension.java similarity index 92% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ServiceLoaderExtension.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ServiceLoaderExtension.java index abaf97bd92d3..aab386b973e0 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/ServiceLoaderExtension.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ServiceLoaderExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryCleanupTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryCleanupTests.java similarity index 79% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryCleanupTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryCleanupTests.java index ddaaa214f786..ff7644a015b2 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryCleanupTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryCleanupTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -16,6 +16,7 @@ import static org.junit.jupiter.api.io.CleanupMode.ALWAYS; import static org.junit.jupiter.api.io.CleanupMode.NEVER; import static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod; import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; @@ -23,8 +24,11 @@ import java.nio.file.Path; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.io.CleanupMode; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; @@ -141,13 +145,49 @@ void cleanupModeOnSuccessFailingField() { assertThat(onSuccessFailingFieldDir).exists(); } + /** + * Ensure that ON_SUCCESS cleanup modes are obeyed for static fields when tests are failing. + *

    + * Expect the TempDir not to be cleaned up. + */ + @Test + void cleanupModeOnSuccessFailingStaticField() { + LauncherDiscoveryRequest request = request()// + .selectors(selectClass(OnSuccessFailingStaticFieldCase.class))// + .build(); + executeTests(request); + + assertThat(onSuccessFailingFieldDir).exists(); + } + + /** + * Ensure that ON_SUCCESS cleanup modes are obeyed for static fields when nested tests are failing. + *

    + * Expect the TempDir not to be cleaned up. + */ + @Test + void cleanupModeOnSuccessFailingStaticFieldWithNesting() { + LauncherDiscoveryRequest request = request()// + .selectors(selectClass(OnSuccessFailingStaticFieldWithNestingCase.class))// + .build(); + executeTests(request); + + assertThat(onSuccessFailingFieldDir).exists(); + } + @AfterAll static void afterAll() throws IOException { - deleteIfExists(defaultFieldDir); - deleteIfExists(neverFieldDir); - deleteIfExists(alwaysFieldDir); - deleteIfExists(onSuccessFailingFieldDir); - deleteIfExists(onSuccessPassingFieldDir); + deleteIfNotNullAndExists(defaultFieldDir); + deleteIfNotNullAndExists(neverFieldDir); + deleteIfNotNullAndExists(alwaysFieldDir); + deleteIfNotNullAndExists(onSuccessFailingFieldDir); + deleteIfNotNullAndExists(onSuccessPassingFieldDir); + } + + static void deleteIfNotNullAndExists(Path dir) throws IOException { + if (dir != null) { + deleteIfExists(dir); + } } // ------------------------------------------------------------------- @@ -208,6 +248,41 @@ void testOnSuccessFailingField() { } } + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + static class OnSuccessFailingStaticFieldCase { + + @TempDir(cleanup = ON_SUCCESS) + static Path onSuccessFailingFieldDir; + + @Test + @Order(1) + void failing() { + TempDirFieldTests.onSuccessFailingFieldDir = onSuccessFailingFieldDir; + fail(); + } + + @Test + @Order(2) + void passing() { + } + } + + static class OnSuccessFailingStaticFieldWithNestingCase { + + @TempDir(cleanup = ON_SUCCESS) + static Path onSuccessFailingFieldDir; + + @Nested + class NestedTestCase { + + @Test + void test() { + TempDirFieldTests.onSuccessFailingFieldDir = onSuccessFailingFieldDir; + fail(); + } + } + } + } @Nested @@ -314,11 +389,11 @@ void cleanupModeOnSuccessFailingParameter() { @AfterAll static void afterAll() throws IOException { - deleteIfExists(defaultParameterDir); - deleteIfExists(neverParameterDir); - deleteIfExists(alwaysParameterDir); - deleteIfExists(onSuccessFailingParameterDir); - deleteIfExists(onSuccessPassingParameterDir); + TempDirFieldTests.deleteIfNotNullAndExists(defaultParameterDir); + TempDirFieldTests.deleteIfNotNullAndExists(neverParameterDir); + TempDirFieldTests.deleteIfNotNullAndExists(alwaysParameterDir); + TempDirFieldTests.deleteIfNotNullAndExists(onSuccessFailingParameterDir); + TempDirFieldTests.deleteIfNotNullAndExists(onSuccessPassingParameterDir); } // ------------------------------------------------------------------- diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryMetaAnnotationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryMetaAnnotationTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryMetaAnnotationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryMetaAnnotationTests.java index 2dad2e09fe55..905672bfbec5 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryMetaAnnotationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryMetaAnnotationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java similarity index 94% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java index ac6f152814e3..c47bb9112815 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -54,14 +54,15 @@ import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.io.TempDirFactory; +import org.junit.jupiter.api.io.TempDirFactory.Standard; import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; +import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; import org.junit.platform.testkit.engine.EngineExecutionResults; -import org.junit.platform.testkit.engine.Events; /** * Integration tests for the legacy behavior of the {@link TempDirectory} - * extension to create a single temp directory per context, i.e. test class or + * extension to create a single temp directory per context, i.e., test class or * method. * * @since 5.4 @@ -74,6 +75,13 @@ protected EngineExecutionResults executeTestsForClass(Class testClass) { return executeTests(requestBuilder(testClass).build()); } + private EngineExecutionResults executeTestsForClassWithDefaultFactory(Class testClass, + Class factoryClass) { + return executeTests(requestBuilder(testClass) // + .configurationParameter(TempDir.DEFAULT_FACTORY_PROPERTY_NAME, factoryClass.getName()) // + .build()); + } + @SuppressWarnings("deprecation") private static LauncherDiscoveryRequestBuilder requestBuilder(Class testClass) { return request() // @@ -295,25 +303,20 @@ void resolvesSeparateTempDirWhenAnnotationIsUsedOnAfterAllMethodParameterOnly() @TestMethodOrder(OrderAnnotation.class) class DefaultFactory { - private Events executeTestsForClassWithDefaultFactory(Class testClass, - Class factoryClass) { - return TempDirectoryPerContextTests.super.executeTests(requestBuilder(testClass) // - .configurationParameter(TempDir.DEFAULT_FACTORY_PROPERTY_NAME, factoryClass.getName()) // - .build()).testEvents(); - } - @Test @DisplayName("set to Jupiter's default") void supportsStandardDefaultFactory() { - executeTestsForClassWithDefaultFactory(StandardDefaultFactoryTestCase.class, TempDirFactory.Standard.class) // - .assertStatistics(stats -> stats.started(1).succeeded(1)); + var results = executeTestsForClassWithDefaultFactory(StandardDefaultFactoryTestCase.class, Standard.class); + + results.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1)); } @Test @DisplayName("set to custom factory") void supportsCustomDefaultFactory() { - executeTestsForClassWithDefaultFactory(NonStandardDefaultFactoryTestCase.class, Factory.class) // - .assertStatistics(stats -> stats.started(1).succeeded(1)); + var results = executeTestsForClassWithDefaultFactory(CustomDefaultFactoryTestCase.class, Factory.class); + + results.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1)); } private static class Factory implements TempDirFactory { @@ -359,8 +362,7 @@ void onlySupportsParametersOfTypePathAndFile() { var results = executeTestsForClass(InvalidTestCase.class); // @formatter:off - TempDirectoryPerContextTests.assertSingleFailedTest(results, - instanceOf(ParameterResolutionException.class), + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), message(m -> m.matches("Failed to resolve parameter \\[java.lang.String .+] in method \\[.+]: .+")), cause( instanceOf(ExtensionConfigurationException.class), @@ -389,14 +391,13 @@ void doesNotSupportTempDirAnnotationOnConstructorParameterWithTestInstancePerCla } @Test - @DisplayName("when @TempDir factory is not Standard") + @DisplayName("when non-default @TempDir factory is set") @Order(32) - void onlySupportsStandardTempDirFactory() { - var results = executeTestsForClass(NonStandardFactoryTestCase.class); + void doesNotSupportNonDefaultTempDirFactory() { + var results = executeTestsForClass(NonDefaultFactoryTestCase.class); // @formatter:off - TempDirectoryPerContextTests.assertSingleFailedTest(results, - instanceOf(ParameterResolutionException.class), + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), message(m -> m.matches("Failed to resolve parameter \\[.+] in method \\[.+]: .+")), cause( instanceOf(ExtensionConfigurationException.class), @@ -405,6 +406,35 @@ void onlySupportsStandardTempDirFactory() { // @formatter:on } + @Test + @DisplayName("when default @TempDir factory does not return directory") + @Order(33) + void doesNotSupportCustomDefaultTempDirFactoryNotReturningDirectory() { + var results = executeTestsForClassWithDefaultFactory( + CustomDefaultFactoryNotReturningDirectoryTestCase.class, FactoryNotReturningDirectory.class); + + // @formatter:off + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), + message(m -> m.matches("Failed to resolve parameter \\[.+] in method \\[.+]: .+")), + cause( + instanceOf(ExtensionConfigurationException.class), + message("Failed to create default temp directory"), + cause( + instanceOf(PreconditionViolationException.class), + message("temp directory must be a directory") + ) + )); + // @formatter:on + } + + private static class FactoryNotReturningDirectory implements TempDirFactory { + + @Override + public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) { + return null; + } + } + } @Nested @@ -685,7 +715,7 @@ static class AnnotationOnConstructorParameterWithTestInstancePerClassTestCase } } - static class NonStandardFactoryTestCase { + static class NonDefaultFactoryTestCase { @Test void test(@SuppressWarnings("unused") @TempDir(factory = Factory.class) Path tempDir) { @@ -697,7 +727,7 @@ private static class Factory implements TempDirFactory { @Override public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) throws Exception { - return Files.createTempDirectory("junit"); + return Files.createTempDirectory("junit-"); } } @@ -712,7 +742,7 @@ void test(@TempDir Path tempDir1, @TempDir Path tempDir2) { } - static class NonStandardDefaultFactoryTestCase { + static class CustomDefaultFactoryTestCase { @Test void test(@TempDir Path tempDir1, @TempDir Path tempDir2) { @@ -721,6 +751,15 @@ void test(@TempDir Path tempDir1, @TempDir Path tempDir2) { } + static class CustomDefaultFactoryNotReturningDirectoryTestCase { + + @Test + void test(@SuppressWarnings("unused") @TempDir Path tempDir) { + // never called + } + + } + static class AnnotationOnBeforeAllMethodParameterTestCase extends BaseSharedTempDirParameterInjectionTestCase { @BeforeAll diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java similarity index 84% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java index f0575de592c0..cfd8fbc452af 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; -import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; @@ -43,11 +42,9 @@ import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import java.util.stream.Stream; import com.github.marschall.memoryfilesystem.MemoryFileSystemBuilder; import com.google.common.jimfs.Configuration; @@ -59,12 +56,10 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestMethodOrder; @@ -83,6 +78,10 @@ import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; import org.junit.jupiter.engine.Constants; import org.junit.jupiter.engine.extension.TempDirectory.FileOperations; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.engine.reporting.ReportEntry; import org.junit.platform.testkit.engine.EngineExecutionResults; @@ -95,57 +94,62 @@ @DisplayName("TempDirectory extension (per declaration)") class TempDirectoryPerDeclarationTests extends AbstractJupiterTestEngineTests { + private EngineExecutionResults executeTestsForClassWithDefaultFactory(Class testClass, + Class factoryClass) { + return executeTests(request() // + .selectors(selectClass(testClass)) // + .configurationParameter(TempDir.DEFAULT_FACTORY_PROPERTY_NAME, factoryClass.getName()) // + .build()); + } + @BeforeEach @AfterEach void resetStaticVariables() { AllPossibleDeclarationLocationsTestCase.tempDirs.clear(); } - @TestFactory + @ParameterizedTest(name = "{0}") + @EnumSource(TestInstance.Lifecycle.class) @DisplayName("resolves separate temp dirs for each annotation declaration") - Stream resolvesSeparateTempDirsForEachAnnotationDeclaration() { - return Arrays.stream(TestInstance.Lifecycle.values()).map( - lifecycle -> dynamicTest("with " + lifecycle + " lifecycle", () -> { - - var results = executeTests(request() // - .selectors(selectClass(AllPossibleDeclarationLocationsTestCase.class)) // - .configurationParameter(Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME, - lifecycle.name()).build()); - - results.containerEvents().assertStatistics(stats -> stats.started(2).succeeded(2)); - results.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2)); - - assertThat(AllPossibleDeclarationLocationsTestCase.tempDirs).hasSize(3); - - var classTempDirs = AllPossibleDeclarationLocationsTestCase.tempDirs.get("class"); - assertThat(classTempDirs).containsOnlyKeys("staticField1", "staticField2", "beforeAll1", "beforeAll2", - "afterAll1", "afterAll2"); - assertThat(classTempDirs.values()).hasSize(6).doesNotHaveDuplicates(); - - var testATempDirs = AllPossibleDeclarationLocationsTestCase.tempDirs.get("testA"); - assertThat(testATempDirs).containsOnlyKeys("staticField1", "staticField2", "instanceField1", - "instanceField2", "beforeEach1", "beforeEach2", "test1", "test2", "afterEach1", "afterEach2"); - assertThat(testATempDirs.values()).hasSize(10).doesNotHaveDuplicates(); - - var testBTempDirs = AllPossibleDeclarationLocationsTestCase.tempDirs.get("testB"); - assertThat(testBTempDirs).containsOnlyKeys("staticField1", "staticField2", "instanceField1", - "instanceField2", "beforeEach1", "beforeEach2", "test1", "test2", "afterEach1", "afterEach2"); - assertThat(testBTempDirs.values()).hasSize(10).doesNotHaveDuplicates(); - - assertThat(testATempDirs).containsEntry("staticField1", classTempDirs.get("staticField1")); - assertThat(testBTempDirs).containsEntry("staticField1", classTempDirs.get("staticField1")); - assertThat(testATempDirs).containsEntry("staticField2", classTempDirs.get("staticField2")); - assertThat(testBTempDirs).containsEntry("staticField2", classTempDirs.get("staticField2")); - - assertThat(testATempDirs).doesNotContainEntry("instanceField1", testBTempDirs.get("instanceField1")); - assertThat(testATempDirs).doesNotContainEntry("instanceField2", testBTempDirs.get("instanceField2")); - assertThat(testATempDirs).doesNotContainEntry("beforeEach1", testBTempDirs.get("beforeEach1")); - assertThat(testATempDirs).doesNotContainEntry("beforeEach2", testBTempDirs.get("beforeEach2")); - assertThat(testATempDirs).doesNotContainEntry("test1", testBTempDirs.get("test1")); - assertThat(testATempDirs).doesNotContainEntry("test2", testBTempDirs.get("test2")); - assertThat(testATempDirs).doesNotContainEntry("afterEach1", testBTempDirs.get("afterEach1")); - assertThat(testATempDirs).doesNotContainEntry("afterEach2", testBTempDirs.get("afterEach2")); - })); + void resolvesSeparateTempDirsForEachAnnotationDeclaration(TestInstance.Lifecycle lifecycle) { + var results = executeTests(request() // + .selectors(selectClass(AllPossibleDeclarationLocationsTestCase.class)) // + .configurationParameter(Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME, + lifecycle.name()).build()); + + results.containerEvents().assertStatistics(stats -> stats.started(2).succeeded(2)); + results.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2)); + + assertThat(AllPossibleDeclarationLocationsTestCase.tempDirs).hasSize(3); + + var classTempDirs = AllPossibleDeclarationLocationsTestCase.tempDirs.get("class"); + assertThat(classTempDirs).containsOnlyKeys("staticField1", "staticField2", "beforeAll1", "beforeAll2", + "afterAll1", "afterAll2"); + assertThat(classTempDirs.values()).hasSize(6).doesNotHaveDuplicates(); + + var testATempDirs = AllPossibleDeclarationLocationsTestCase.tempDirs.get("testA"); + assertThat(testATempDirs).containsOnlyKeys("staticField1", "staticField2", "instanceField1", "instanceField2", + "beforeEach1", "beforeEach2", "test1", "test2", "afterEach1", "afterEach2"); + assertThat(testATempDirs.values()).hasSize(10).doesNotHaveDuplicates(); + + var testBTempDirs = AllPossibleDeclarationLocationsTestCase.tempDirs.get("testB"); + assertThat(testBTempDirs).containsOnlyKeys("staticField1", "staticField2", "instanceField1", "instanceField2", + "beforeEach1", "beforeEach2", "test1", "test2", "afterEach1", "afterEach2"); + assertThat(testBTempDirs.values()).hasSize(10).doesNotHaveDuplicates(); + + assertThat(testATempDirs).containsEntry("staticField1", classTempDirs.get("staticField1")); + assertThat(testBTempDirs).containsEntry("staticField1", classTempDirs.get("staticField1")); + assertThat(testATempDirs).containsEntry("staticField2", classTempDirs.get("staticField2")); + assertThat(testBTempDirs).containsEntry("staticField2", classTempDirs.get("staticField2")); + + assertThat(testATempDirs).doesNotContainEntry("instanceField1", testBTempDirs.get("instanceField1")); + assertThat(testATempDirs).doesNotContainEntry("instanceField2", testBTempDirs.get("instanceField2")); + assertThat(testATempDirs).doesNotContainEntry("beforeEach1", testBTempDirs.get("beforeEach1")); + assertThat(testATempDirs).doesNotContainEntry("beforeEach2", testBTempDirs.get("beforeEach2")); + assertThat(testATempDirs).doesNotContainEntry("test1", testBTempDirs.get("test1")); + assertThat(testATempDirs).doesNotContainEntry("test2", testBTempDirs.get("test2")); + assertThat(testATempDirs).doesNotContainEntry("afterEach1", testBTempDirs.get("afterEach1")); + assertThat(testATempDirs).doesNotContainEntry("afterEach2", testBTempDirs.get("afterEach2")); } @Test @@ -213,16 +217,10 @@ void canBeUsedViaStaticFieldInsideNestedTestClasses() { .assertStatistics(stats -> stats.started(2).succeeded(2)); } - @TestFactory + @ParameterizedTest(name = "{0}") + @ValueSource(classes = { UndeletableDirectoryTestCase.class, UndeletableFileTestCase.class }) @DisplayName("only attempts to delete undeletable paths once") - Stream onlyAttemptsToDeleteUndeletablePathsOnce() { - return Stream.of( // - dynamicTest("directory", () -> onlyAttemptsToDeleteUndeletablePathOnce(UndeletableDirectoryTestCase.class)), // - dynamicTest("file", () -> onlyAttemptsToDeleteUndeletablePathOnce(UndeletableFileTestCase.class)) // - ); - } - - private void onlyAttemptsToDeleteUndeletablePathOnce(Class testClass) { + void onlyAttemptsToDeleteUndeletablePathsOnce(Class testClass) { var results = executeTestsForClass(testClass); var tempDir = results.testEvents().reportingEntryPublished().stream().map( @@ -230,11 +228,14 @@ private void onlyAttemptsToDeleteUndeletablePathOnce(Class Path.of(it.getKeyValuePairs().get(UndeletableTestCase.TEMP_DIR))).findAny().orElseThrow(); assertSingleFailedTest(results, // - instanceOf(IOException.class), // - message("Failed to delete temp directory " + tempDir.toAbsolutePath() + ". " + // - "The following paths could not be deleted (see suppressed exceptions for details): , undeletable"), // - suppressed(0, instanceOf(DirectoryNotEmptyException.class)), // - suppressed(1, instanceOf(IOException.class), message("Simulated failure"))); + cause( // + instanceOf(IOException.class), // + message("Failed to delete temp directory " + tempDir.toAbsolutePath() + ". " + // + "The following paths could not be deleted (see suppressed exceptions for details): , undeletable"), // + suppressed(0, instanceOf(DirectoryNotEmptyException.class)), // + suppressed(1, instanceOf(IOException.class), message("Simulated failure")) // + ) // + ); } @Nested @@ -269,8 +270,7 @@ void onlySupportsParametersOfTypePathAndFile() { var results = executeTestsForClass(InvalidTestCase.class); // @formatter:off - TempDirectoryPerDeclarationTests.assertSingleFailedTest(results, - instanceOf(ParameterResolutionException.class), + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), message(m -> m.matches("Failed to resolve parameter \\[java.lang.String .+] in method \\[.+]: .+")), cause( instanceOf(ExtensionConfigurationException.class), @@ -298,6 +298,77 @@ void doesNotSupportTempDirAnnotationOnConstructorParameterWithTestInstancePerCla "@TempDir is not supported on constructor parameters. Please use field injection instead."); } + @Test + @DisplayName("when @TempDir factory does not return directory") + @Order(32) + void doesNotSupportTempDirFactoryNotReturningDirectory() { + var results = executeTestsForClass(FactoryNotReturningDirectoryTestCase.class); + + // @formatter:off + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), + message(m -> m.matches("Failed to resolve parameter \\[.+] in method \\[.+]: .+")), + cause( + instanceOf(ExtensionConfigurationException.class), + message("Failed to create default temp directory"), + cause( + instanceOf(PreconditionViolationException.class), + message("temp directory must be a directory") + ) + )); + // @formatter:on + } + + @Test + @DisplayName("when default @TempDir factory does not return directory") + @Order(33) + void doesNotSupportCustomDefaultTempDirFactoryNotReturningDirectory() { + var results = executeTestsForClassWithDefaultFactory( + CustomDefaultFactoryNotReturningDirectoryTestCase.class, FactoryNotReturningDirectory.class); + + // @formatter:off + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), + message(m -> m.matches("Failed to resolve parameter \\[.+] in method \\[.+]: .+")), + cause( + instanceOf(ExtensionConfigurationException.class), + message("Failed to create default temp directory"), + cause( + instanceOf(PreconditionViolationException.class), + message("temp directory must be a directory") + ) + )); + // @formatter:on + } + + private static class FactoryNotReturningDirectory implements TempDirFactory { + + @Override + public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) { + return null; + } + } + + @Test + @DisplayName("when @TempDir factory returns a non-default file system path for a File annotated element") + @Order(34) + void doesNotSupportNonDefaultFileSystemTempDirFactoryOnFileAnnotatedElement() { + var results = executeTestsForClass( + FactoryReturningNonDefaultFileSystemPathForFileAnnotatedElementTestCase.class); + + // @formatter:off + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), + message(m -> m.matches("Failed to resolve parameter \\[.+] in method \\[.+]: .+")), + cause( + instanceOf(ExtensionConfigurationException.class), + message("Failed to create default temp directory"), + cause( + instanceOf(PreconditionViolationException.class), + message("temp directory with non-default file system cannot be injected into " + + File.class.getName() + " target") + ) + )); + // @formatter:on + } + } @Nested @@ -376,14 +447,6 @@ void supportsFactoryWithCustomMetaAnnotation() { @TestMethodOrder(OrderAnnotation.class) class DefaultFactory { - private EngineExecutionResults executeTestsForClassWithDefaultFactory(Class testClass, - Class factoryClass) { - return TempDirectoryPerDeclarationTests.super.executeTests(request() // - .selectors(selectClass(testClass)) // - .configurationParameter(TempDir.DEFAULT_FACTORY_PROPERTY_NAME, factoryClass.getName()) // - .build()); - } - @Test @DisplayName("set to Jupiter's default") void supportsStandardDefaultFactory() { @@ -405,8 +468,8 @@ void supportsCustomDefaultFactory() { void supportsCustomDefaultFactoryWithStandardFactoryOnDeclaration() { executeTestsForClassWithDefaultFactory( // CustomDefaultFactoryWithStandardDeclarationTestCase.class, Factory.class) // - .testEvents()// - .assertStatistics(stats -> stats.started(1).succeeded(1)); + .testEvents()// + .assertStatistics(stats -> stats.started(1).succeeded(1)); } private static class Factory implements TempDirFactory { @@ -1366,6 +1429,48 @@ public Path createTempDirectory(AnnotatedElementContext elementContext, Extensio } + static class FactoryNotReturningDirectoryTestCase { + + @Test + void test(@SuppressWarnings("unused") @TempDir(factory = Factory.class) Path tempDir) { + // never called + } + + private static class Factory implements TempDirFactory { + + @Override + public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) { + return null; + } + } + + } + + static class FactoryReturningNonDefaultFileSystemPathForFileAnnotatedElementTestCase { + + @Test + void test(@SuppressWarnings("unused") @TempDir(factory = Factory.class) File tempDir) { + // never called + } + + private static class Factory implements TempDirFactory { + + private final FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix()); + + @Override + public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) + throws Exception { + return Files.createTempDirectory(fileSystem.getPath("/"), "prefix"); + } + + @Override + public void close() throws IOException { + fileSystem.close(); + } + } + + } + static class StandardDefaultFactoryTestCase { @Test @@ -1399,4 +1504,13 @@ void test(@TempDir Path tempDir1, @TempDir(factory = Standard.class) Path tempDi } + static class CustomDefaultFactoryNotReturningDirectoryTestCase { + + @Test + void test(@SuppressWarnings("unused") @TempDir Path tempDir) { + // never called + } + + } + } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPreconditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPreconditionTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPreconditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPreconditionTests.java index 4a5b020e0fde..659dfca5771a 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPreconditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPreconditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestExecutionExceptionHandlerTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestExecutionExceptionHandlerTests.java similarity index 96% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestExecutionExceptionHandlerTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestExecutionExceptionHandlerTests.java index 1c4409c03dd3..71a5b9e65934 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestExecutionExceptionHandlerTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestExecutionExceptionHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,6 +12,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod; import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; @@ -168,7 +169,7 @@ static class RethrowException implements TestExecutionExceptionHandler { @Override public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - assertTrue(throwable instanceof IOException); + assertInstanceOf(IOException.class, throwable); handleExceptionCalled = true; handlerCalls.add("rethrow"); @@ -182,7 +183,7 @@ static class SwallowException implements TestExecutionExceptionHandler { @Override public void handleTestExecutionException(ExtensionContext context, Throwable throwable) { - assertTrue(throwable instanceof IOException); + assertInstanceOf(IOException.class, throwable); handleExceptionCalled = true; handlerCalls.add("swallow"); //swallow exception by not rethrowing it @@ -195,7 +196,7 @@ static class ConvertException implements TestExecutionExceptionHandler { @Override public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { - assertTrue(throwable instanceof RuntimeException); + assertInstanceOf(RuntimeException.class, throwable); handleExceptionCalled = true; handlerCalls.add("convert"); throw new IOException("checked"); diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInfoParameterResolverTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInfoParameterResolverTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInfoParameterResolverTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInfoParameterResolverTests.java index ba4610797714..2a66c6f4ae2a 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInfoParameterResolverTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInfoParameterResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstanceFactoryTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstanceFactoryTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstanceFactoryTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstanceFactoryTests.java index fa729e13b79b..b338cb0567a8 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstanceFactoryTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstanceFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorAndPreDestroyCallbackTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorAndPreDestroyCallbackTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorAndPreDestroyCallbackTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorAndPreDestroyCallbackTests.java index 2b684fa941b3..fec5ad33bb04 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorAndPreDestroyCallbackTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorAndPreDestroyCallbackTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorTests.java index 225e89de951b..a9b4fe75cf03 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreConstructCallbackTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreConstructCallbackTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreConstructCallbackTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreConstructCallbackTests.java index 100cb30fddb2..fdf323d21291 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreConstructCallbackTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreConstructCallbackTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackTests.java similarity index 99% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackTests.java index a20d3460b5af..a417a00e8db7 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackUtilityMethodTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackUtilityMethodTests.java similarity index 72% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackUtilityMethodTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackUtilityMethodTests.java index 4ffff6264b37..23dd06a39efa 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackUtilityMethodTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackUtilityMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,6 @@ package org.junit.jupiter.engine.extension; -import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; import static org.junit.platform.testkit.engine.EventConditions.event; import static org.junit.platform.testkit.engine.EventConditions.reportEntry; @@ -18,36 +17,33 @@ import static org.junit.platform.testkit.engine.EventConditions.test; import java.util.Map; -import java.util.stream.Stream; -import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.TestInstancePostProcessor; import org.junit.jupiter.api.extension.TestInstancePreDestroyCallback; import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; public class TestInstancePreDestroyCallbackUtilityMethodTests extends AbstractJupiterTestEngineTests { - @TestFactory - Stream destroysWhatWasPostProcessed() { - var testClasses = Stream.of(PerMethodLifecycleOnAllLevels.class, PerMethodWithinPerClassLifecycle.class, - PerClassWithinPerMethodLifecycle.class, PerClassLifecycleOnAllLevels.class); - return testClasses.map(testClass -> dynamicTest( // - testClass.getSimpleName(), // - () -> executeTestsForClass(testClass).allEvents().debug() // - .assertStatistics(stats -> stats.reportingEntryPublished(4)) // - .assertEventsMatchLooselyInOrder( // - reportEntry(Map.of("post-process", testClass.getSimpleName())), - reportEntry(Map.of("post-process", "Inner")), // - event(test(), started()), // - reportEntry(Map.of("pre-destroy", "Inner")), // - reportEntry(Map.of("pre-destroy", testClass.getSimpleName())) // - ))); + @ParameterizedTest(name = "{0}") + @ValueSource(classes = { PerMethodLifecycleOnAllLevels.class, PerMethodWithinPerClassLifecycle.class, + PerClassWithinPerMethodLifecycle.class, PerClassLifecycleOnAllLevels.class }) + void destroysWhatWasPostProcessed(Class testClass) { + executeTestsForClass(testClass).allEvents().debug() // + .assertStatistics(stats -> stats.reportingEntryPublished(4)) // + .assertEventsMatchLooselyInOrder( // + reportEntry(Map.of("post-process", testClass.getSimpleName())), + reportEntry(Map.of("post-process", "Inner")), // + event(test(), started()), // + reportEntry(Map.of("pre-destroy", "Inner")), // + reportEntry(Map.of("pre-destroy", testClass.getSimpleName())) // + ); } @ExtendWith(TestInstanceLifecycleExtension.class) diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestReporterParameterResolverTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestReporterParameterResolverTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestReporterParameterResolverTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestReporterParameterResolverTests.java index 6acbcafa6126..913e310c4e93 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestReporterParameterResolverTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestReporterParameterResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestWatcherTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestWatcherTests.java similarity index 86% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestWatcherTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestWatcherTests.java index 6ada22bf2527..a8400aa63ba3 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TestWatcherTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestWatcherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,8 +11,8 @@ package org.junit.jupiter.engine.extension; import static java.util.function.Predicate.not; -import static java.util.stream.Collectors.toUnmodifiableList; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -42,8 +42,11 @@ import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.TestWatcher; import org.junit.jupiter.api.fixtures.TrackLogRecords; @@ -60,14 +63,15 @@ */ class TestWatcherTests extends AbstractJupiterTestEngineTests { - private static final List testWatcherMethodNames = Arrays.stream(TestWatcher.class.getDeclaredMethods())// - .filter(not(Method::isSynthetic))// - .map(Method::getName)// - .collect(toUnmodifiableList()); + private static final List testWatcherMethodNames = Arrays.stream(TestWatcher.class.getDeclaredMethods()) // + .filter(not(Method::isSynthetic)) // + .map(Method::getName) // + .toList(); @BeforeEach void clearResults() { TrackingTestWatcher.results.clear(); + DataRetrievingTestWatcher.results.clear(); } @Test @@ -107,8 +111,7 @@ void testWatcherIsNotInvokedForTestFactoryMethods() { } @Test - @TrackLogRecords - void testWatcherExceptionsAreLoggedAndSwallowed(LogRecordListener logRecordListener) { + void testWatcherExceptionsAreLoggedAndSwallowed(@TrackLogRecords LogRecordListener logRecordListener) { assertCommonStatistics(executeTestsForClass(ExceptionThrowingTestWatcherTestCase.class)); // @formatter:off @@ -169,6 +172,16 @@ void testWatcherSemanticsWhenRegisteredAtMethodLevel() { assertThat(TrackingTestWatcher.results.get("testDisabled")).containsExactly("test", "repeatedTest"); } + @Test + void testWatcherCanRetrieveDataFromTheExtensionContextStore() { + Class testClass = DataRetrievingTestWatcherTestCase.class; + EngineExecutionResults results = executeTestsForClass(testClass); + + results.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1).failed(0)); + + assertThat(DataRetrievingTestWatcher.results).containsExactly(entry("key", "enigma")); + } + private void assertCommonStatistics(EngineExecutionResults results) { results.containerEvents().assertStatistics(stats -> stats.started(3).succeeded(3).failed(0)); results.testEvents().assertStatistics(stats -> stats.skipped(2).started(6).succeeded(2).aborted(2).failed(2)); @@ -291,7 +304,7 @@ Stream abortedTest() { @TestFactory @Disabled Stream skippedTest() { - return Stream.of("A", "B").map(text -> dynamicTest(text, () -> assertTrue(false))); + return Stream.of("A", "B").map(text -> dynamicTest(text, () -> fail())); } } @@ -416,4 +429,40 @@ public void testFailed(ExtensionContext context, Throwable cause) { } + /** + * {@link TestWatcher} that retrieves data from the {@link ExtensionContext.Store}. + * @see #3944 + */ + static class DataRetrievingTestWatcher implements BeforeTestExecutionCallback, TestWatcher { + + private static final Namespace NAMESPACE = Namespace.create(DataRetrievingTestWatcher.class); + + private static final String KEY = "key"; + + private static final Map results = new HashMap<>(); + + @Override + public void beforeTestExecution(ExtensionContext context) throws Exception { + getStore(context).put(KEY, "enigma"); + } + + @Override + public void testSuccessful(ExtensionContext context) { + results.put(KEY, getStore(context).get(KEY, String.class)); + } + + private static Store getStore(ExtensionContext context) { + return context.getStore(NAMESPACE); + } + } + + @ExtendWith(DataRetrievingTestWatcher.class) + static class DataRetrievingTestWatcherTestCase { + + @Test + public void test() { + //no-op + } + } + } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutConfigurationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutConfigurationTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutConfigurationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutConfigurationTests.java index 4886f2718607..52dd96bb08f3 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutConfigurationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -124,8 +124,7 @@ void specificTimeoutsAreUsedIfSet() { } @Test - @TrackLogRecords - void logsInvalidValues(LogRecordListener logRecordListener) { + void logsInvalidValues(@TrackLogRecords LogRecordListener logRecordListener) { when(extensionContext.getConfigurationParameter(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn( Optional.of("invalid")); @@ -143,8 +142,7 @@ void specificThreadModeIsUsed() { } @Test - @TrackLogRecords - void logsInvalidThreadModeValueAndReturnEmpty(LogRecordListener logRecordListener) { + void logsInvalidThreadModeValueAndReturnEmpty(@TrackLogRecords LogRecordListener logRecordListener) { when(extensionContext.getConfigurationParameter(DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME)).thenReturn( Optional.of("invalid")); diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationParserTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationParserTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationParserTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationParserTests.java index 7eee8ae416a0..14fe2ce0f5e0 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationParserTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationTests.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationTests.java index 494ea9dfa6ee..cf20a202ad4a 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactoryTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactoryTests.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactoryTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactoryTests.java index 66ca3ce8d613..34a621391141 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactoryTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutExtensionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutExtensionTests.java similarity index 90% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutExtensionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutExtensionTests.java index 6fc772440c22..7a317a6f946f 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutExtensionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutExtensionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,8 +13,8 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; -import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.jupiter.api.Timeout.ThreadMode.SAME_THREAD; @@ -27,14 +27,12 @@ import static org.junit.jupiter.engine.Constants.DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME; import static org.junit.jupiter.engine.Constants.DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME; import static org.junit.jupiter.engine.Constants.TIMEOUT_MODE_PROPERTY_NAME; -import static org.junit.platform.commons.util.CollectionUtils.getOnlyElement; import static org.junit.platform.engine.TestExecutionResult.Status.FAILED; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod; import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; import java.time.Duration; -import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeoutException; import java.util.stream.Stream; @@ -54,6 +52,10 @@ import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.util.RuntimeUtils; import org.junit.platform.engine.TestExecutionResult.Status; @@ -161,29 +163,27 @@ void appliesTimeoutOnAnnotatedTestFactoryMethods() { .hasMessage("testFactoryMethod() timed out after 10 milliseconds"); } - @TestFactory + @ParameterizedTest(name = "{0}") + @ValueSource(classes = { TimeoutAnnotatedClassTestCase.class, InheritedTimeoutAnnotatedClassTestCase.class }) @DisplayName("is applied on testable methods in annotated classes") - Stream appliesTimeoutOnTestableMethodsInAnnotatedClasses() { - return Stream.of(TimeoutAnnotatedClassTestCase.class, InheritedTimeoutAnnotatedClassTestCase.class).map( - testClass -> dynamicTest(testClass.getSimpleName(), () -> { - EngineExecutionResults results = executeTests(request() // - .selectors(selectClass(testClass)) // - .configurationParameter(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME, "42ns") // - .configurationParameter(DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME, "42ns") // - .configurationParameter(DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME, "42ns") // - .build()); - - Stream.of("testMethod()", "repetition 1", "repetition 2", "testFactoryMethod()").forEach( - displayName -> { - Execution execution = findExecution(results.allEvents(), displayName); - assertThat(execution.getDuration()) // - .isGreaterThanOrEqualTo(Duration.ofMillis(10)) // - .isLessThan(Duration.ofSeconds(1)); - assertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) // - .isInstanceOf(TimeoutException.class) // - .hasMessageEndingWith("timed out after 10000000 nanoseconds"); - }); - })); + void appliesTimeoutOnTestableMethodsInAnnotatedClasses(Class testClass) { + EngineExecutionResults results = executeTests(request() // + .selectors(selectClass(testClass)) // + .configurationParameter(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME, "42ns") // + .configurationParameter(DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME, "42ns") // + .configurationParameter(DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME, "42ns") // + .build()); + + assertAll(Stream.of("testMethod()", "repetition 1", "repetition 2", "testFactoryMethod()") // + .map(displayName -> () -> { + Execution execution = findExecution(results.allEvents(), displayName); + assertThat(execution.getDuration()) // + .isGreaterThanOrEqualTo(Duration.ofMillis(10)) // + .isLessThan(Duration.ofSeconds(1)); + assertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) // + .isInstanceOf(TimeoutException.class) // + .hasMessageEndingWith("timed out after 10000000 nanoseconds"); + })); } @Test @@ -271,28 +271,32 @@ void appliesTimeoutOnAnnotatedAfterAllMethods() { .hasMessage("tearDown() timed out after 10 milliseconds"); } - @TestFactory + @ParameterizedTest(name = "{0}") + @MethodSource @DisplayName("is applied from configuration parameters by default") - Stream appliesDefaultTimeoutsFromConfigurationParameters() { - return Map.of(DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME, "beforeAll()", // - DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME, "beforeEach()", // - DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME, "test()", // - DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME, "testTemplate()", // - DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME, "testFactory()", // - DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME, "afterEach()", // - DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME, "afterAll()" // - ).entrySet().stream().map(entry -> dynamicTest("uses " + entry.getKey() + " config param", () -> { - PlainTestCase.slowMethod = entry.getValue(); - EngineExecutionResults results = executeTests(request() // - .selectors(selectClass(PlainTestCase.class)) // - .configurationParameter(entry.getKey(), "1ns") // - .build()); - var failure = results.allEvents().executions().failed() // - .map(execution -> execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) // - .findFirst(); - assertThat(failure).containsInstanceOf(TimeoutException.class); - assertThat(failure.get()).hasMessage(entry.getValue() + " timed out after 1 nanosecond"); - })); + void appliesDefaultTimeoutsFromConfigurationParameters(String propertyName, String slowMethod) { + PlainTestCase.slowMethod = slowMethod; + EngineExecutionResults results = executeTests(request() // + .selectors(selectClass(PlainTestCase.class)) // + .configurationParameter(propertyName, "1ns") // + .build()); + var failure = results.allEvents().executions().failed() // + .map(execution -> execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) // + .findFirst(); + assertThat(failure).containsInstanceOf(TimeoutException.class); + assertThat(failure.orElseThrow()).hasMessage(slowMethod + " timed out after 1 nanosecond"); + } + + static Stream appliesDefaultTimeoutsFromConfigurationParameters() { + return Stream.of( // + Arguments.of(DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME, "beforeAll()"), // + Arguments.of(DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME, "beforeEach()"), // + Arguments.of(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME, "test()"), // + Arguments.of(DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME, "testTemplate()"), // + Arguments.of(DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME, "testFactory()"), // + Arguments.of(DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME, "afterEach()"), // + Arguments.of(DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME, "afterAll()") // + ); } @Test @@ -333,11 +337,10 @@ void reportsIllegalTimeoutDurations() { .hasMessage("timeout duration must be a positive number: 0"); } - private Execution findExecution(Events events, String displayName) { - return getOnlyElement(events // - .executions() // - .filter(execution -> execution.getTestDescriptor().getDisplayName().contains(displayName)) // - .collect(toList())); + private static Execution findExecution(Events events, String displayName) { + return events.executions()// + .filter(execution -> execution.getTestDescriptor().getDisplayName().contains(displayName))// + .findFirst().get(); } @Nested diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactoryTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactoryTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactoryTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactoryTests.java index 9d3c46487dab..ba4536af5594 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactoryTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/sub/AlwaysDisabledCondition.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/AlwaysDisabledCondition.java similarity index 95% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/sub/AlwaysDisabledCondition.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/AlwaysDisabledCondition.java index 9e9533e45223..ba60a5f27ac5 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/sub/AlwaysDisabledCondition.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/AlwaysDisabledCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/sub/AnotherAlwaysDisabledCondition.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/AnotherAlwaysDisabledCondition.java similarity index 93% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/sub/AnotherAlwaysDisabledCondition.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/AnotherAlwaysDisabledCondition.java index d8846b32aa58..2dd26dfcda19 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/sub/AnotherAlwaysDisabledCondition.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/AnotherAlwaysDisabledCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/sub/SystemPropertyCondition.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/SystemPropertyCondition.java similarity index 97% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/sub/SystemPropertyCondition.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/SystemPropertyCondition.java index f2f2cab45200..a4336b4b5d14 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/sub/SystemPropertyCondition.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/SystemPropertyCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/subpackage/SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/subpackage/SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase.java similarity index 69% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/subpackage/SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/subpackage/SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase.java index c2d184262083..77e807247f7b 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/subpackage/SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/subpackage/SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,7 @@ package org.junit.jupiter.engine.subpackage; -import static org.junit.jupiter.api.Assertions.fail; +import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -20,13 +20,16 @@ */ public class SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase { + protected boolean beforeEachInvoked = false; + @BeforeEach void beforeEach() { - fail(); + this.beforeEachInvoked = true; } @Test void test() { + assertThat(this.beforeEachInvoked).isTrue(); } } diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollectorTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollectorTests.java similarity index 98% rename from junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollectorTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollectorTests.java index a22b9f33683d..17a195eea29a 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollectorTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/JupiterMigrationSupportTestSuite.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/JupiterMigrationSupportTestSuite.java similarity index 95% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/JupiterMigrationSupportTestSuite.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/JupiterMigrationSupportTestSuite.java index f37c15b81a90..80cca83a0b5c 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/JupiterMigrationSupportTestSuite.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/JupiterMigrationSupportTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreAnnotationIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreAnnotationIntegrationTests.java similarity index 97% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreAnnotationIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreAnnotationIntegrationTests.java index 3befccdf3373..0c9487556d82 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreAnnotationIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreAnnotationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreConditionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreConditionTests.java similarity index 98% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreConditionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreConditionTests.java index 95be5fb351e8..f427d7d6769d 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreConditionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/AbstractTestRuleAdapterTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/AbstractTestRuleAdapterTests.java similarity index 97% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/AbstractTestRuleAdapterTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/AbstractTestRuleAdapterTests.java index 4829db6d1634..3457cf3ed04c 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/AbstractTestRuleAdapterTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/AbstractTestRuleAdapterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupportWithBothRuleTypesTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupportWithBothRuleTypesTests.java similarity index 97% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupportWithBothRuleTypesTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupportWithBothRuleTypesTests.java index 00185dfd91be..14ef7fb260e8 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupportWithBothRuleTypesTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupportWithBothRuleTypesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupportTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupportTests.java similarity index 98% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupportTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupportTests.java index 548459026e3f..be8df6202fd0 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupportTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForDifferentDeclaredReturnTypesRulesTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForDifferentDeclaredReturnTypesRulesTests.java similarity index 97% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForDifferentDeclaredReturnTypesRulesTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForDifferentDeclaredReturnTypesRulesTests.java index 96629e2ec313..9f9db6def7b3 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForDifferentDeclaredReturnTypesRulesTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForDifferentDeclaredReturnTypesRulesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMixedMethodAndFieldRulesTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMixedMethodAndFieldRulesTests.java similarity index 98% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMixedMethodAndFieldRulesTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMixedMethodAndFieldRulesTests.java index 45a5e81e7413..e02d4d774ccc 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMixedMethodAndFieldRulesTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMixedMethodAndFieldRulesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleFieldRulesTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleFieldRulesTests.java similarity index 96% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleFieldRulesTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleFieldRulesTests.java index 28bedf135ffb..6cc63fd9828e 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleFieldRulesTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleFieldRulesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleMethodRulesTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleMethodRulesTests.java similarity index 97% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleMethodRulesTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleMethodRulesTests.java index 7e15b4abc866..593c02cff54c 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleMethodRulesTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleMethodRulesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForTemporaryFolderFieldTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForTemporaryFolderFieldTests.java similarity index 94% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForTemporaryFolderFieldTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForTemporaryFolderFieldTests.java index 4119170dba5f..8495213209e7 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForTemporaryFolderFieldTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForTemporaryFolderFieldTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportWithInheritanceTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportWithInheritanceTests.java similarity index 88% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportWithInheritanceTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportWithInheritanceTests.java index dc6f4fcaee05..230e6e0b59b4 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportWithInheritanceTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportWithInheritanceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceWithoutAdapterTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceWithoutAdapterTests.java similarity index 78% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceWithoutAdapterTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceWithoutAdapterTests.java index 67c395b46a58..474135bd0fca 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceWithoutAdapterTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceWithoutAdapterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,7 +10,7 @@ package org.junit.jupiter.migrationsupport.rules; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.Rule; import org.junit.jupiter.api.BeforeEach; @@ -28,7 +28,7 @@ void setup() { folder.newFile("temp.txt"); } catch (Exception exception) { - assertTrue(exception.getMessage().equals("the temporary folder has not yet been created")); + assertEquals("the temporary folder has not yet been created", exception.getMessage()); } } diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/FailAfterAllHelper.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/FailAfterAllHelper.java similarity index 90% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/FailAfterAllHelper.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/FailAfterAllHelper.java index 08d51f17968a..57fc525000a6 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/FailAfterAllHelper.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/FailAfterAllHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/LauncherBasedEnableRuleMigrationSupportTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/LauncherBasedEnableRuleMigrationSupportTests.java similarity index 98% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/LauncherBasedEnableRuleMigrationSupportTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/LauncherBasedEnableRuleMigrationSupportTests.java index 37774078b0f0..558a9c490336 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/LauncherBasedEnableRuleMigrationSupportTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/LauncherBasedEnableRuleMigrationSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/VerifierSupportForMixedMethodAndFieldRulesTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/VerifierSupportForMixedMethodAndFieldRulesTests.java similarity index 96% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/VerifierSupportForMixedMethodAndFieldRulesTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/VerifierSupportForMixedMethodAndFieldRulesTests.java index a42db4c56638..50f5158ded28 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/VerifierSupportForMixedMethodAndFieldRulesTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/VerifierSupportForMixedMethodAndFieldRulesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierFieldTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierFieldTests.java similarity index 95% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierFieldTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierFieldTests.java index 9b559cc0a2e6..b2a7335794b6 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierFieldTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierFieldTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierMethodTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierMethodTests.java similarity index 95% rename from junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierMethodTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierMethodTests.java index 9d566a581b77..12ff43594287 100644 --- a/junit-jupiter-migrationsupport/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierMethodTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestExtensionTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestExtensionTests.java similarity index 99% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestExtensionTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestExtensionTests.java index 40c1eeeec28a..69db68af81ba 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestExtensionTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestExtensionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java similarity index 69% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java index 2f248bfdad42..7733c8fc5104 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,7 @@ package org.junit.jupiter.params; +import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.within; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -19,6 +20,7 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.jupiter.api.Named.named; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; import static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.appendTestTemplateInvocationSegment; import static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForTestTemplateMethod; import static org.junit.jupiter.params.provider.Arguments.arguments; @@ -39,7 +41,6 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; @@ -50,6 +51,7 @@ import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.NavigableMap; import java.util.NavigableSet; @@ -58,6 +60,7 @@ import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; +import java.util.function.Supplier; import java.util.stream.Stream; import org.junit.jupiter.api.AfterAll; @@ -74,12 +77,15 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestReporter; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.engine.JupiterTestEngine; +import org.junit.jupiter.params.ParameterizedTestIntegrationTests.RepeatableSourcesTestCase.Action; import org.junit.jupiter.params.aggregator.AggregateWith; import org.junit.jupiter.params.aggregator.ArgumentsAccessor; import org.junit.jupiter.params.aggregator.ArgumentsAggregationException; @@ -93,6 +99,8 @@ import org.junit.jupiter.params.provider.CsvFileSource; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.EmptySource; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.FieldSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.NullSource; @@ -111,6 +119,47 @@ */ class ParameterizedTestIntegrationTests { + private final Locale originalLocale = Locale.getDefault(Locale.Category.FORMAT); + + @AfterEach + void restoreLocale() { + Locale.setDefault(Locale.Category.FORMAT, originalLocale); + } + + @ParameterizedTest + @CsvSource(textBlock = """ + apple, True + banana, true + lemon, false + kumquat, FALSE + """) + void sweetFruit(String fruit, Boolean sweet) { + switch (fruit) { + case "apple" -> assertThat(sweet).isTrue(); + case "banana" -> assertThat(sweet).isTrue(); + case "lemon" -> assertThat(sweet).isFalse(); + case "kumquat" -> assertThat(sweet).isFalse(); + default -> fail("Unexpected fruit : " + fruit); + } + } + + @ParameterizedTest + @CsvSource(nullValues = "null", textBlock = """ + apple, True + banana, true + lemon, false + kumquat, null + """) + void sweetFruitWithNullableBoolean(String fruit, Boolean sweet) { + switch (fruit) { + case "apple" -> assertThat(sweet).isTrue(); + case "banana" -> assertThat(sweet).isTrue(); + case "lemon" -> assertThat(sweet).isFalse(); + case "kumquat" -> assertThat(sweet).isNull(); // null --> null + default -> fail("Unexpected fruit : " + fruit); + } + } + @ParameterizedTest @CsvSource(quoteCharacter = '"', textBlock = """ @@ -217,6 +266,16 @@ void executesWithCustomName() { .haveExactly(1, event(test(), displayName("bar and 42"), finishedWithFailure(message("bar, 42")))); } + @Test + void executesWithMessageFormat() { + Locale.setDefault(Locale.Category.FORMAT, Locale.ROOT); + + var results = execute("testWithMessageFormat", double.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, + event(test(), displayName("3.1416"), finishedWithFailure(message(String.valueOf(Math.PI))))); + } + /** * @since 5.2 */ @@ -836,6 +895,146 @@ private EngineExecutionResults execute(String methodName, Class... methodPara } + /** + * @since 5.11 + */ + @Nested + class FieldSourceIntegrationTests { + + @Test + void oneDimensionalPrimitiveArray() { + execute("oneDimensionalPrimitiveArray", int.class).testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("1"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("2")))); + } + + @Test + void twoDimensionalPrimitiveArray() { + execute("twoDimensionalPrimitiveArray", int[].class).testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("[1, 2]"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("[3, 4]")))); + } + + @Test + void oneDimensionalObjectArray() { + execute("oneDimensionalObjectArray", Object.class).testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("one"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("2"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("three")))); + } + + @Test + void oneDimensionalStringArray() { + execute("oneDimensionalStringArray", String.class).testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("one"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("two")))); + } + + @Test + void twoDimensionalObjectArray() { + execute("twoDimensionalObjectArray", String.class, int.class).testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("one:2"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("three:4")))); + } + + @Test + void twoDimensionalStringArray() { + execute("twoDimensionalStringArray", String.class, String.class).testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("one:two"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("three:four")))); + } + + @Test + void supplierOfStreamOfOneDimensionalPrimitiveArrays() { + execute("supplierOfStreamOfOneDimensionalPrimitiveArrays", int[].class).testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("[1, 2]"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("[3, 4]")))); + } + + @Test + void supplierOfStreamOfTwoDimensionalPrimitiveArrays() { + assertStreamOfTwoDimensionalPrimitiveArrays("supplierOfStreamOfTwoDimensionalPrimitiveArrays"); + } + + @Test + void supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays() { + assertStreamOfTwoDimensionalPrimitiveArrays( + "supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays"); + } + + @Test + void supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInArguments() { + assertStreamOfTwoDimensionalPrimitiveArrays( + "supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInArguments"); + } + + private void assertStreamOfTwoDimensionalPrimitiveArrays(String methodName) { + execute(methodName, int[][].class).testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("[[1, 2], [3, 4]]"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("[[5, 6], [7, 8]]")))); + } + + @Test + void supplierOfStreamOfOneDimensionalObjectArrays() { + execute("supplierOfStreamOfOneDimensionalObjectArrays", String.class, int.class).testEvents()// + .assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("one:2"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("three:4")))); + } + + @Test + void supplierOfStreamOfTwoDimensionalObjectArrays() { + execute("supplierOfStreamOfTwoDimensionalObjectArrays", Object[][].class).testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("[[one, 2], [three, 4]]"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("[[five, 6], [seven, 8]]")))); + } + + @Test + void listOfNamedParameters() { + execute("listOfNamedParameters", String.class).allEvents().assertThatEvents() // + .haveAtLeast(1, + event(test(), displayName("cool name"), finishedWithFailure(message("parameter value")))) // + .haveAtLeast(1, + event(test(), displayName("default name"), finishedWithFailure(message("default name")))); + } + + @Test + void nonStaticFieldInTopLevelTestClass() { + Class testClass = BaseLifecyclePerClassFieldSourceTestCase.class; + execute(testClass, "test", String.class).testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("base-1"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("base-2")))); + } + + @Test + void nonStaticFieldInSubclassTakesPrecedenceOverFieldInSuperclass() { + Class testClass = SubclassOfBaseLifecyclePerClassFieldSourceTestCase.class; + execute(testClass, "test", String.class).testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("sub-1"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("sub-2")))); + } + + @Test + void nonStaticFieldInNestedTestClass() { + Class testClass = EnclosingFieldSourceTestCase.NestedLifecyclePerClassFieldSourceTestCase.class; + execute(testClass, "nonStaticFieldSource", String.class)// + .testEvents().assertThatEvents()// + .haveExactly(1, event(test(), finishedWithFailure(message("apple"))))// + .haveExactly(1, event(test(), finishedWithFailure(message("banana")))); + } + + private EngineExecutionResults execute(String methodName, Class... methodParameterTypes) { + return execute(FieldSourceTestCase.class, methodName, methodParameterTypes); + } + + private EngineExecutionResults execute(Class testClass, String methodName, + Class... methodParameterTypes) { + + return ParameterizedTestIntegrationTests.this.execute(testClass, methodName, methodParameterTypes); + } + + } + @Nested class UnusedArgumentsIntegrationTests { @@ -875,6 +1074,15 @@ void executesWithMethodSourceProvidingUnusedArguments() { event(test(), displayName("[2] argument=bar"), finishedWithFailure(message("bar")))); } + @Test + void executesWithFieldSourceProvidingUnusedArguments() { + var results = execute("testWithFieldSourceProvidingUnusedArguments", String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(test(), displayName("[1] argument=foo"), finishedWithFailure(message("foo")))) // + .haveExactly(1, + event(test(), displayName("[2] argument=bar"), finishedWithFailure(message("bar")))); + } + private EngineExecutionResults execute(String methodName, Class... methodParameterTypes) { return ParameterizedTestIntegrationTests.this.execute(UnusedArgumentsTestCase.class, methodName, methodParameterTypes); @@ -882,6 +1090,109 @@ private EngineExecutionResults execute(String methodName, Class... methodPara } + @Nested + class RepeatableSourcesIntegrationTests { + + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableCsvFileSource", "testWithRepeatableCsvFileSourceAsMetaAnnotation" }) + void executesWithRepeatableCsvFileSource(String methodName) { + var results = execute(methodName, String.class, String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, + event(test(), displayName("[1] column1=foo, column2=1"), finishedWithFailure(message("foo 1")))) // + .haveExactly(1, event(test(), displayName("[5] column1=FRUIT = apple, column2=RANK = 1"), + finishedWithFailure(message("apple 1")))); + } + + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableCsvSource", "testWithRepeatableCsvSourceAsMetaAnnotation" }) + void executesWithRepeatableCsvSource(String methodName) { + var results = execute(methodName, String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(test(), displayName("[1] argument=a"), finishedWithFailure(message("a")))) // + .haveExactly(1, event(test(), displayName("[2] argument=b"), finishedWithFailure(message("b")))); + } + + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableMethodSource", "testWithRepeatableMethodSourceAsMetaAnnotation" }) + void executesWithRepeatableMethodSource(String methodName) { + var results = execute(methodName, String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, + event(test(), displayName("[1] argument=some"), finishedWithFailure(message("some")))) // + .haveExactly(1, + event(test(), displayName("[2] argument=other"), finishedWithFailure(message("other")))); + } + + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableEnumSource", "testWithRepeatableEnumSourceAsMetaAnnotation" }) + void executesWithRepeatableEnumSource(String methodName) { + var results = execute(methodName, Action.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(test(), displayName("[1] argument=FOO"), finishedWithFailure(message("FOO")))) // + .haveExactly(1, + event(test(), displayName("[2] argument=BAR"), finishedWithFailure(message("BAR")))); + } + + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableValueSource", "testWithRepeatableValueSourceAsMetaAnnotation" }) + void executesWithRepeatableValueSource(String methodName) { + var results = execute(methodName, String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(test(), displayName("[1] argument=foo"), finishedWithFailure(message("foo")))) // + .haveExactly(1, + event(test(), displayName("[2] argument=bar"), finishedWithFailure(message("bar")))); + } + + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableFieldSource", "testWithRepeatableFieldSourceAsMetaAnnotation" }) + void executesWithRepeatableFieldSource(String methodName) { + var results = execute(methodName, String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, + event(test(), displayName("[1] argument=some"), finishedWithFailure(message("some")))) // + .haveExactly(1, + event(test(), displayName("[2] argument=other"), finishedWithFailure(message("other")))); + } + + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableArgumentsSource", + "testWithRepeatableArgumentsSourceAsMetaAnnotation" }) + void executesWithRepeatableArgumentsSource(String methodName) { + var results = execute(methodName, String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(test(), displayName("[1] argument=foo"), finishedWithFailure(message("foo")))) // + .haveExactly(1, event(test(), displayName("[2] argument=bar"), finishedWithFailure(message("bar")))) // + .haveExactly(1, event(test(), displayName("[3] argument=foo"), finishedWithFailure(message("foo")))) // + .haveExactly(1, + event(test(), displayName("[4] argument=bar"), finishedWithFailure(message("bar")))); + + } + + @Test + void executesWithSameRepeatableAnnotationMultipleTimes() { + var results = execute("testWithSameRepeatableAnnotationMultipleTimes", String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(test(), started())) // + .haveExactly(1, event(test(), finishedWithFailure(message("foo")))); + } + + @Test + void executesWithDifferentRepeatableAnnotations() { + var results = execute("testWithDifferentRepeatableAnnotations", String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(test(), displayName("[1] argument=a"), finishedWithFailure(message("a")))) // + .haveExactly(1, event(test(), displayName("[2] argument=b"), finishedWithFailure(message("b")))) // + .haveExactly(1, event(test(), displayName("[3] argument=c"), finishedWithFailure(message("c")))) // + .haveExactly(1, event(test(), displayName("[4] argument=d"), finishedWithFailure(message("d")))); + } + + private EngineExecutionResults execute(String methodName, Class... methodParameterTypes) { + return ParameterizedTestIntegrationTests.this.execute(RepeatableSourcesTestCase.class, methodName, + methodParameterTypes); + } + } + @Test void closeAutoCloseableArgumentsAfterTest() { var results = execute("testWithAutoCloseableArgument", AutoCloseableArgument.class); @@ -955,6 +1266,12 @@ void testWithErroneousConverter(@ConvertWith(ErroneousConverter.class) Object ig fail("this should never be called"); } + @ParameterizedTest(name = "{0,number,#.####}") + @ValueSource(doubles = Math.PI) + void testWithMessageFormat(double argument) { + fail(String.valueOf(argument)); + } + @ParameterizedTest @CsvSource({ "ab, cd", "ef, gh" }) void testWithAggregator(@AggregateWith(StringAggregator.class) String concatenation) { @@ -974,13 +1291,13 @@ void testWithIgnoreLeadingAndTrailingWhitespaceSetToTrueForCsvSource(String argu } @ParameterizedTest - @CsvFileSource(resources = "/leading-trailing-spaces.csv", ignoreLeadingAndTrailingWhitespace = false) + @CsvFileSource(resources = "provider/leading-trailing-spaces.csv", ignoreLeadingAndTrailingWhitespace = false) void testWithIgnoreLeadingAndTrailingWhitespaceSetToFalseForCsvFileSource(String argument1, String argument2) { fail("arguments: '" + argument1 + "', '" + argument2 + "'"); } @ParameterizedTest - @CsvFileSource(resources = "/leading-trailing-spaces.csv", ignoreLeadingAndTrailingWhitespace = true) + @CsvFileSource(resources = "provider/leading-trailing-spaces.csv", ignoreLeadingAndTrailingWhitespace = true) void testWithIgnoreLeadingAndTrailingWhitespaceSetToTrueForCsvFileSource(String argument1, String argument2) { fail("arguments: '" + argument1 + "', '" + argument2 + "'"); } @@ -1233,13 +1550,14 @@ void testWithNullAndEmptySourceForTwoDimensionalStringArray(String[][] argument) static class MethodSourceTestCase { @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) + @Retention(RUNTIME) @ParameterizedTest(name = "{arguments}") @MethodSource @interface MethodSourceTest { } @MethodSourceTest + @Order(0) void emptyMethodSource(String argument) { fail(argument); } @@ -1453,6 +1771,173 @@ private static Stream test() { } + @TestMethodOrder(OrderAnnotation.class) + static class FieldSourceTestCase { + + @Target(ElementType.METHOD) + @Retention(RUNTIME) + @ParameterizedTest(name = "{arguments}") + @FieldSource + @interface FieldSourceTest { + } + + @FieldSourceTest + @Order(1) + void oneDimensionalPrimitiveArray(int x) { + fail("" + x); + } + + @FieldSourceTest + @Order(2) + void twoDimensionalPrimitiveArray(int[] array) { + fail(Arrays.toString(array)); + } + + @FieldSourceTest + @Order(3) + void oneDimensionalObjectArray(Object o) { + fail("" + o); + } + + @FieldSourceTest + @Order(4) + void oneDimensionalStringArray(String s) { + fail(s); + } + + @FieldSourceTest + @Order(5) + void twoDimensionalObjectArray(String s, int x) { + fail(s + ":" + x); + } + + @FieldSourceTest + @Order(6) + void twoDimensionalStringArray(String s1, String s2) { + fail(s1 + ":" + s2); + } + + @FieldSourceTest + @Order(7) + void supplierOfStreamOfOneDimensionalPrimitiveArrays(int[] array) { + fail(Arrays.toString(array)); + } + + @FieldSourceTest + @Order(8) + void supplierOfStreamOfTwoDimensionalPrimitiveArrays(int[][] array) { + fail(Arrays.deepToString(array)); + } + + @FieldSourceTest + @Order(9) + void supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays(int[][] array) { + fail(Arrays.deepToString(array)); + } + + @FieldSourceTest + @Order(10) + void supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInArguments(int[][] array) { + fail(Arrays.deepToString(array)); + } + + @FieldSourceTest + @Order(11) + void supplierOfStreamOfOneDimensionalObjectArrays(String s, int x) { + fail(s + ":" + x); + } + + @FieldSourceTest + @Order(12) + void supplierOfStreamOfTwoDimensionalObjectArrays(Object[][] array) { + fail(Arrays.deepToString(array)); + } + + @FieldSourceTest + @Order(13) + void listOfNamedParameters(String string) { + fail(string); + } + + // --------------------------------------------------------------------- + + static int[] oneDimensionalPrimitiveArray = new int[] { 1, 2 }; + + static int[][] twoDimensionalPrimitiveArray = new int[][] { { 1, 2 }, { 3, 4 } }; + + static Object[] oneDimensionalObjectArray = new Object[] { "one", 2, "three" }; + + static Object[] oneDimensionalStringArray = new Object[] { "one", "two" }; + + static Object[][] twoDimensionalObjectArray = new Object[][] { { "one", 2 }, { "three", 4 } }; + + static String[][] twoDimensionalStringArray = new String[][] { { "one", "two" }, { "three", "four" } }; + + static Supplier> supplierOfStreamOfOneDimensionalPrimitiveArrays = // + () -> Stream.of(new int[] { 1, 2 }, new int[] { 3, 4 }); + + static Supplier> supplierOfStreamOfTwoDimensionalPrimitiveArrays = // + () -> Stream.of(new int[][] { { 1, 2 }, { 3, 4 } }, new int[][] { { 5, 6 }, { 7, 8 } }); + + static Supplier> supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays = () -> Stream.of( + new Object[] { new int[][] { { 1, 2 }, { 3, 4 } } }, new Object[] { new int[][] { { 5, 6 }, { 7, 8 } } }); + + static Supplier> supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInArguments = () -> Stream.of( + arguments((Object) new int[][] { { 1, 2 }, { 3, 4 } }), + arguments((Object) new int[][] { { 5, 6 }, { 7, 8 } })); + + static Supplier> supplierOfStreamOfOneDimensionalObjectArrays = () -> Stream.of( + new Object[] { "one", 2 }, new Object[] { "three", 4 }); + + static Supplier> supplierOfStreamOfTwoDimensionalObjectArrays = () -> Stream.of( + new Object[][] { { "one", 2 }, { "three", 4 } }, new Object[][] { { "five", 6 }, { "seven", 8 } }); + + static List listOfNamedParameters = // + List.of(arguments(named("cool name", "parameter value")), arguments("default name")); + + } + + @TestInstance(PER_CLASS) + static class BaseLifecyclePerClassFieldSourceTestCase { + + final List field = List.of("base-1", "base-2"); + + @ParameterizedTest + @FieldSource("field") + void test(String value) { + fail(value); + } + } + + static class SubclassOfBaseLifecyclePerClassFieldSourceTestCase extends BaseLifecyclePerClassFieldSourceTestCase { + + final List field = List.of("sub-1", "sub-2"); + + @ParameterizedTest + @FieldSource("field") + @Override + void test(String value) { + fail(value); + } + } + + static class EnclosingFieldSourceTestCase { + + @Nested + @TestInstance(Lifecycle.PER_CLASS) + class NestedLifecyclePerClassFieldSourceTestCase { + + // Non-static field + final List fruits = List.of("apple", "banana"); + + @ParameterizedTest + @FieldSource("fruits") + void nonStaticFieldSource(String fruit) { + fail(fruit); + } + } + } + static class UnusedArgumentsTestCase { @ParameterizedTest @@ -1483,6 +1968,15 @@ static Stream unusedArgumentsProviderMethod() { return Stream.of(arguments("foo", "unused1"), arguments("bar", "unused2")); } + @ParameterizedTest + @FieldSource("unusedArgumentsProviderField") + void testWithFieldSourceProvidingUnusedArguments(String argument) { + fail(argument); + } + + static Supplier> unusedArgumentsProviderField = // + () -> Stream.of(arguments("foo", "unused1"), arguments("bar", "unused2")); + } static class LifecycleTestCase { @@ -1540,6 +2034,183 @@ static Stream providerMethod() { } + static class RepeatableSourcesTestCase { + + @ParameterizedTest + @CsvFileSource(resources = "two-column.csv") + @CsvFileSource(resources = "two-column-with-headers.csv", delimiter = '|', useHeadersInDisplayName = true, nullValues = "NIL") + void testWithRepeatableCsvFileSource(String column1, String column2) { + fail("%s %s".formatted(column1, column2)); + } + + @CsvFileSource(resources = "two-column.csv") + @CsvFileSource(resources = "two-column-with-headers.csv", delimiter = '|', useHeadersInDisplayName = true, nullValues = "NIL") + @Retention(RUNTIME) + @interface TwoCsvFileSources { + } + + @ParameterizedTest + @TwoCsvFileSources + void testWithRepeatableCsvFileSourceAsMetaAnnotation(String column1, String column2) { + fail("%s %s".formatted(column1, column2)); + } + + @ParameterizedTest + @CsvSource({ "a" }) + @CsvSource({ "b" }) + void testWithRepeatableCsvSource(String argument) { + fail(argument); + } + + @CsvSource({ "a" }) + @CsvSource({ "b" }) + @Retention(RUNTIME) + @interface TwoCsvSources { + } + + @ParameterizedTest + @TwoCsvSources + void testWithRepeatableCsvSourceAsMetaAnnotation(String argument) { + fail(argument); + } + + @ParameterizedTest + @EnumSource(SmartAction.class) + @EnumSource(QuickAction.class) + void testWithRepeatableEnumSource(Action argument) { + fail(argument.toString()); + } + + @EnumSource(SmartAction.class) + @EnumSource(QuickAction.class) + @Retention(RUNTIME) + @interface TwoEnumSources { + } + + @ParameterizedTest + @TwoEnumSources + void testWithRepeatableEnumSourceAsMetaAnnotation(Action argument) { + fail(argument.toString()); + } + + interface Action { + } + + private enum SmartAction implements Action { + FOO + } + + private enum QuickAction implements Action { + BAR + } + + @ParameterizedTest + @MethodSource("someArgumentsMethodSource") + @MethodSource("otherArgumentsMethodSource") + void testWithRepeatableMethodSource(String argument) { + fail(argument); + } + + @MethodSource("someArgumentsMethodSource") + @MethodSource("otherArgumentsMethodSource") + @Retention(RUNTIME) + @interface TwoMethodSources { + } + + @ParameterizedTest + @TwoMethodSources + void testWithRepeatableMethodSourceAsMetaAnnotation(String argument) { + fail(argument); + } + + public static Stream someArgumentsMethodSource() { + return Stream.of(Arguments.of("some")); + } + + public static Stream otherArgumentsMethodSource() { + return Stream.of(Arguments.of("other")); + } + + @ParameterizedTest + @FieldSource("someArgumentsContainer") + @FieldSource("otherArgumentsContainer") + void testWithRepeatableFieldSource(String argument) { + fail(argument); + } + + @FieldSource("someArgumentsContainer") + @FieldSource("otherArgumentsContainer") + @Retention(RUNTIME) + @interface TwoFieldSources { + } + + @ParameterizedTest + @TwoFieldSources + void testWithRepeatableFieldSourceAsMetaAnnotation(String argument) { + fail(argument); + } + + static List someArgumentsContainer = List.of("some"); + static List otherArgumentsContainer = List.of("other"); + + @ParameterizedTest + @ValueSource(strings = "foo") + @ValueSource(strings = "bar") + void testWithRepeatableValueSource(String argument) { + fail(argument); + } + + @ValueSource(strings = "foo") + @ValueSource(strings = "bar") + @Retention(RUNTIME) + @interface TwoValueSources { + } + + @ParameterizedTest + @TwoValueSources + void testWithRepeatableValueSourceAsMetaAnnotation(String argument) { + fail(argument); + } + + @ParameterizedTest + @ValueSource(strings = "foo") + @ValueSource(strings = "foo") + @ValueSource(strings = "foo") + @ValueSource(strings = "foo") + @ValueSource(strings = "foo") + void testWithSameRepeatableAnnotationMultipleTimes(String argument) { + fail(argument); + } + + @ParameterizedTest + @ValueSource(strings = "a") + @ValueSource(strings = "b") + @CsvSource({ "c" }) + @CsvSource({ "d" }) + void testWithDifferentRepeatableAnnotations(String argument) { + fail(argument); + } + + @ParameterizedTest + @ArgumentsSource(TwoSingleStringArgumentsProvider.class) + @ArgumentsSource(TwoUnusedStringArgumentsProvider.class) + void testWithRepeatableArgumentsSource(String argument) { + fail(argument); + } + + @ArgumentsSource(TwoSingleStringArgumentsProvider.class) + @ArgumentsSource(TwoUnusedStringArgumentsProvider.class) + @Retention(RUNTIME) + @interface TwoArgumentsSources { + } + + @ParameterizedTest + @TwoArgumentsSources + void testWithRepeatableArgumentsSourceAsMetaAnnotation(String argument) { + fail(argument); + } + } + private static class TwoSingleStringArgumentsProvider implements ArgumentsProvider { @Override diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestMethodContextTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestMethodContextTests.java similarity index 97% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestMethodContextTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestMethodContextTests.java index 038c350210da..3bf17b6d5759 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestMethodContextTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestMethodContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestNameFormatterTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestNameFormatterTests.java similarity index 57% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestNameFormatterTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestNameFormatterTests.java index 1cbabc827991..bba252b4fbd0 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestNameFormatterTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestNameFormatterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,14 +11,20 @@ package org.junit.jupiter.params; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.params.ParameterizedTest.ARGUMENTS_PLACEHOLDER; import static org.junit.jupiter.params.ParameterizedTest.ARGUMENTS_WITH_NAMES_PLACEHOLDER; +import static org.junit.jupiter.params.ParameterizedTest.ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER; +import static org.junit.jupiter.params.ParameterizedTest.ARGUMENT_SET_NAME_PLACEHOLDER; +import static org.junit.jupiter.params.ParameterizedTest.DEFAULT_DISPLAY_NAME; import static org.junit.jupiter.params.ParameterizedTest.DISPLAY_NAME_PLACEHOLDER; import static org.junit.jupiter.params.ParameterizedTest.INDEX_PLACEHOLDER; +import static org.junit.jupiter.params.provider.Arguments.argumentSet; +import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.Mockito.mock; import java.lang.reflect.Method; @@ -31,11 +37,14 @@ import java.util.Locale; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionConfigurationException; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.params.aggregator.AggregateWith; import org.junit.jupiter.params.aggregator.ArgumentsAccessor; import org.junit.jupiter.params.aggregator.ArgumentsAggregator; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvSource; import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.util.ReflectionUtils; @@ -56,8 +65,8 @@ void restoreLocale() { void formatsDisplayName() { var formatter = formatter(DISPLAY_NAME_PLACEHOLDER, "enigma"); - assertEquals("enigma", formatter.format(1)); - assertEquals("enigma", formatter.format(2)); + assertEquals("enigma", format(formatter, 1, arguments())); + assertEquals("enigma", format(formatter, 2, arguments())); } @Test @@ -65,8 +74,8 @@ void formatsDisplayNameContainingApostrophe() { String displayName = "display'Zero"; var formatter = formatter(DISPLAY_NAME_PLACEHOLDER, "display'Zero"); - assertEquals(displayName, formatter.format(1)); - assertEquals(displayName, formatter.format(2)); + assertEquals(displayName, format(formatter, 1, arguments())); + assertEquals(displayName, format(formatter, 2, arguments())); } @Test @@ -74,23 +83,32 @@ void formatsDisplayNameContainingFormatElements() { String displayName = "{enigma} {0} '{1}'"; var formatter = formatter(DISPLAY_NAME_PLACEHOLDER, displayName); - assertEquals(displayName, formatter.format(1)); - assertEquals(displayName, formatter.format(2)); + assertEquals(displayName, format(formatter, 1, arguments())); + assertEquals(displayName, format(formatter, 2, arguments())); } @Test void formatsInvocationIndex() { var formatter = formatter(INDEX_PLACEHOLDER, "enigma"); - assertEquals("1", formatter.format(1)); - assertEquals("2", formatter.format(2)); + assertEquals("1", format(formatter, 1, arguments())); + assertEquals("2", format(formatter, 2, arguments())); + } + + @Test + void defaultDisplayName() { + var formatter = formatter(DEFAULT_DISPLAY_NAME, "IGNORED"); + + var formattedName = format(formatter, 1, arguments("apple", "banana")); + + assertThat(formattedName).isEqualTo("[1] apple, banana"); } @Test void formatsIndividualArguments() { var formatter = formatter("{0} -> {1}", "enigma"); - assertEquals("foo -> 42", formatter.format(1, "foo", 42)); + assertEquals("foo -> 42", format(formatter, 1, arguments("foo", 42))); } @Test @@ -98,17 +116,18 @@ void formatsCompleteArgumentsList() { var formatter = formatter(ARGUMENTS_PLACEHOLDER, "enigma"); // @formatter:off - assertEquals("42, 99, enigma, null, [1, 2, 3], [foo, bar], [[2, 4], [3, 9]]", - formatter.format(1, - 42, - 99, - "enigma", - null, - new int[] { 1, 2, 3 }, - new String[] { "foo", "bar" }, - new Integer[][] { { 2, 4 }, { 3, 9 } } - )); + Arguments args = arguments( + 42, + 99, + "enigma", + null, + new int[] { 1, 2, 3 }, + new String[] { "foo", "bar" }, + new Integer[][] { { 2, 4 }, { 3, 9 } } + ); // @formatter:on + + assertEquals("42, 99, enigma, null, [1, 2, 3], [foo, bar], [[2, 4], [3, 9]]", format(formatter, 1, args)); } @Test @@ -116,7 +135,7 @@ void formatsCompleteArgumentsListWithNames() { var testMethod = ParameterizedTestCases.getMethod("parameterizedTest", int.class, String.class, Object[].class); var formatter = formatter(ARGUMENTS_WITH_NAMES_PLACEHOLDER, "enigma", testMethod); - var formattedName = formatter.format(1, 42, "enigma", new Object[] { "foo", 1 }); + var formattedName = format(formatter, 1, arguments(42, "enigma", new Object[] { "foo", 1 })); assertEquals("someNumber=42, someString=enigma, someArray=[foo, 1]", formattedName); } @@ -125,7 +144,7 @@ void formatsCompleteArgumentsListWithoutNamesForAggregators() { var testMethod = ParameterizedTestCases.getMethod("parameterizedTestWithAggregator", int.class, String.class); var formatter = formatter(ARGUMENTS_WITH_NAMES_PLACEHOLDER, "enigma", testMethod); - var formattedName = formatter.format(1, 42, "foo", "bar"); + var formattedName = format(formatter, 1, arguments(42, "foo", "bar")); assertEquals("someNumber=42, foo, bar", formattedName); } @@ -134,9 +153,9 @@ void formatsCompleteArgumentsListWithArrays() { var formatter = formatter(ARGUMENTS_PLACEHOLDER, "enigma"); // Explicit test for https://github.com/junit-team/junit5/issues/814 - assertEquals("[foo, bar]", formatter.format(1, (Object) new String[] { "foo", "bar" })); + assertEquals("[foo, bar]", format(formatter, 1, arguments((Object) new String[] { "foo", "bar" }))); - assertEquals("[foo, bar], 42, true", formatter.format(1, new String[] { "foo", "bar" }, 42, true)); + assertEquals("[foo, bar], 42, true", format(formatter, 1, arguments(new String[] { "foo", "bar" }, 42, true))); } @Test @@ -144,16 +163,16 @@ void formatsEverythingUsingCustomPattern() { var pattern = DISPLAY_NAME_PLACEHOLDER + " " + INDEX_PLACEHOLDER + " :: " + ARGUMENTS_PLACEHOLDER + " :: {1}"; var formatter = formatter(pattern, "enigma"); - assertEquals("enigma 1 :: foo, bar :: bar", formatter.format(1, "foo", "bar")); - assertEquals("enigma 2 :: foo, 42 :: 42", formatter.format(2, "foo", 42)); + assertEquals("enigma 1 :: foo, bar :: bar", format(formatter, 1, arguments("foo", "bar"))); + assertEquals("enigma 2 :: foo, 42 :: 42", format(formatter, 2, arguments("foo", 42))); } @Test void formatDoesNotAlterArgumentsArray() { - var formatter = formatter(ARGUMENTS_PLACEHOLDER, "enigma"); Object[] actual = { 1, "two", Byte.valueOf("-128"), new Integer[][] { { 2, 4 }, { 3, 9 } } }; + var formatter = formatter(ARGUMENTS_PLACEHOLDER, "enigma"); var expected = Arrays.copyOf(actual, actual.length); - assertEquals("1, two, -128, [[2, 4], [3, 9]]", formatter.format(1, actual)); + assertEquals("1, two, -128, [[2, 4], [3, 9]]", format(formatter, 1, arguments(actual))); assertArrayEquals(expected, actual); } @@ -162,14 +181,12 @@ void formatDoesNotRaiseAnArrayStoreException() { var formatter = formatter("{0} -> {1}", "enigma"); Object[] arguments = new Number[] { 1, 2 }; - assertEquals("1 -> 2", formatter.format(1, arguments)); + assertEquals("1 -> 2", format(formatter, 1, arguments(arguments))); } @Test void throwsReadableExceptionForInvalidPattern() { - var formatter = formatter("{index", "enigma"); - - var exception = assertThrows(JUnitException.class, () -> formatter.format(1)); + var exception = assertThrows(JUnitException.class, () -> formatter("{index", "enigma")); assertNotNull(exception.getCause()); assertEquals(IllegalArgumentException.class, exception.getCause().getClass()); } @@ -178,7 +195,7 @@ void throwsReadableExceptionForInvalidPattern() { void formattingDoesNotFailIfArgumentToStringImplementationReturnsNull() { var formatter = formatter(ARGUMENTS_PLACEHOLDER, "enigma"); - var formattedName = formatter.format(1, new ToStringReturnsNull(), "foo"); + var formattedName = format(formatter, 1, arguments(new ToStringReturnsNull(), "foo")); assertThat(formattedName).isEqualTo("null, foo"); } @@ -187,24 +204,28 @@ void formattingDoesNotFailIfArgumentToStringImplementationReturnsNull() { void formattingDoesNotFailIfArgumentToStringImplementationThrowsAnException() { var formatter = formatter(ARGUMENTS_PLACEHOLDER, "enigma"); - var formattedName = formatter.format(1, new ToStringThrowsException(), "foo"); + var formattedName = format(formatter, 1, arguments(new ToStringThrowsException(), "foo")); assertThat(formattedName).startsWith(ToStringThrowsException.class.getName() + "@"); assertThat(formattedName).endsWith("foo"); } @ParameterizedTest(name = "{0}") - @CsvSource(delimiter = '|', value = { "US | 42.23 is positive on 2019 Jan 13 at 12:34:56", - "DE | 42,23 is positive on 13.01.2019 at 12:34:56" }) + @CsvSource(delimiter = '|', textBlock = """ + US | 42.23 is positive on 2019 Jan 13 at 12:34:56 + DE | 42,23 is positive on 13.01.2019 at 12:34:56 + """) void customFormattingExpressionsAreSupported(Locale locale, String expectedValue) { var pattern = "[{index}] {1,number,#.##} is {1,choice,0 format(formatter, 1, arguments())) + .havingCause() + .isExactlyInstanceOf(ExtensionConfigurationException.class) + .withMessage("When the display name pattern for a @ParameterizedTest contains %s, " + + "the arguments must be supplied as an ArgumentSet.", ARGUMENT_SET_NAME_PLACEHOLDER); + // @formatter:on + } + + @Test + void defaultDisplayName() { + var formatter = formatter(DEFAULT_DISPLAY_NAME, "IGNORED"); + + var formattedName = format(formatter, 42, argumentSet("Fruits", "apple", "banana")); + + assertThat(formattedName).isEqualTo("[42] Fruits"); + } + + @Test + void argumentSetNameAndArgumentsPlaceholders() { + var pattern = ARGUMENT_SET_NAME_PLACEHOLDER + " :: " + ARGUMENTS_PLACEHOLDER; + var formatter = formatter(pattern, "IGNORED"); + + var formattedName = format(formatter, -1, argumentSet("Fruits", "apple", "banana")); + + assertThat(formattedName).isEqualTo("Fruits :: apple, banana"); + } + + @Test + void mixedTypesOfArgumentsImplementationsAndCustomDisplayNamePattern() { + var pattern = "[%s] %s :: %s".formatted(INDEX_PLACEHOLDER, DISPLAY_NAME_PLACEHOLDER, + ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER); + var testMethod = ParameterizedTestCases.getMethod("processFruits", String.class, String.class); + var formatter = formatter(pattern, "Mixed Arguments Types", testMethod); + + var name1 = format(formatter, 1, argumentSet("Fruits", "apple", "banana")); + var name2 = format(formatter, 2, arguments("apple", "banana")); + + assertThat(name1).isEqualTo("[1] Mixed Arguments Types :: Fruits"); + assertThat(name2).isEqualTo("[2] Mixed Arguments Types :: fruit1=apple, fruit2=banana"); + } + + } + + // ------------------------------------------------------------------------- + private static ParameterizedTestNameFormatter formatter(String pattern, String displayName) { - return new ParameterizedTestNameFormatter(pattern, displayName, mock(), 512); + return formatter(pattern, displayName, 512); } - private static ParameterizedTestNameFormatter formatter(String pattern, int argumentMaxLength) { - return new ParameterizedTestNameFormatter(pattern, "display name", mock(), argumentMaxLength); + private static ParameterizedTestNameFormatter formatter(String pattern, String displayName, int argumentMaxLength) { + return new ParameterizedTestNameFormatter(pattern, displayName, mock(), argumentMaxLength); } private static ParameterizedTestNameFormatter formatter(String pattern, String displayName, Method method) { @@ -259,7 +334,9 @@ private static ParameterizedTestNameFormatter formatter(String pattern, String d 512); } - // ------------------------------------------------------------------- + private static String format(ParameterizedTestNameFormatter formatter, int invocationIndex, Arguments arguments) { + return formatter.format(invocationIndex, arguments, arguments.get()); + } private static class ToStringReturnsNull { @@ -292,6 +369,10 @@ void parameterizedTestWithAggregator(int someNumber, @AggregateWith(CustomAggregator.class) String someAggregatedString) { } + @SuppressWarnings("unused") + void processFruits(String fruit1, String fruit2) { + } + private static class CustomAggregator implements ArgumentsAggregator { @Override public Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) { diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestSuite.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestSuite.java similarity index 95% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestSuite.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestSuite.java index dd3a68eb74a5..3d16c471d0d2 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/ParameterizedTestSuite.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/aggregator/AggregatorIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/aggregator/AggregatorIntegrationTests.java similarity index 99% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/aggregator/AggregatorIntegrationTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/aggregator/AggregatorIntegrationTests.java index 3e63bb310416..a6601c9ec05e 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/aggregator/AggregatorIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/aggregator/AggregatorIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessorTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessorTests.java similarity index 99% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessorTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessorTests.java index 10abef0847d7..1e9b95bb0079 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessorTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/DefaultArgumentConverterTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/converter/DefaultArgumentConverterTests.java similarity index 80% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/DefaultArgumentConverterTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/converter/DefaultArgumentConverterTests.java index 90d8fb062792..6e473e8d1965 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/DefaultArgumentConverterTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/converter/DefaultArgumentConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -47,6 +47,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.junit.platform.commons.test.TestClassLoader; import org.junit.platform.commons.util.ReflectionUtils; @@ -61,11 +63,13 @@ class DefaultArgumentConverterTests { void isAwareOfNull() { assertConverts(null, Object.class, null); assertConverts(null, String.class, null); + assertConverts(null, Boolean.class, null); } @Test void isAwareOfWrapperTypesForPrimitiveTypes() { assertConverts(true, boolean.class, true); + assertConverts(false, boolean.class, false); assertConverts((byte) 1, byte.class, (byte) 1); assertConverts('o', char.class, 'o'); assertConverts((short) 1, short.class, (short) 1); @@ -91,6 +95,7 @@ void isAwareOfWideningConversions() { @Test void convertsStringsToPrimitiveTypes() { assertConverts("true", boolean.class, true); + assertConverts("false", boolean.class, false); assertConverts("o", char.class, 'o'); assertConverts("1", byte.class, (byte) 1); assertConverts("1_0", byte.class, (byte) 10); @@ -106,19 +111,75 @@ void convertsStringsToPrimitiveTypes() { assertConverts("42.2_3", double.class, 42.23); } + @Test + void convertsStringsToPrimitiveWrapperTypes() { + assertConverts("true", Boolean.class, true); + assertConverts("false", Boolean.class, false); + assertConverts("o", Character.class, 'o'); + assertConverts("1", Byte.class, (byte) 1); + assertConverts("1_0", Byte.class, (byte) 10); + assertConverts("1", Short.class, (short) 1); + assertConverts("1_2", Short.class, (short) 12); + assertConverts("42", Integer.class, 42); + assertConverts("700_050_000", Integer.class, 700_050_000); + assertConverts("42", Long.class, 42L); + assertConverts("4_2", Long.class, 42L); + assertConverts("42.23", Float.class, 42.23f); + assertConverts("42.2_3", Float.class, 42.23f); + assertConverts("42.23", Double.class, 42.23); + assertConverts("42.2_3", Double.class, 42.23); + } + + @ParameterizedTest(name = "[{index}] {0}") + @ValueSource(classes = { char.class, boolean.class, short.class, byte.class, int.class, long.class, float.class, + double.class }) + void throwsExceptionForNullToPrimitiveTypeConversion(Class type) { + assertThatExceptionOfType(ArgumentConversionException.class) // + .isThrownBy(() -> convert(null, type)) // + .withMessage("Cannot convert null to primitive value of type " + type.getCanonicalName()); + } + + @ParameterizedTest(name = "[{index}] {0}") + @ValueSource(classes = { Boolean.class, Character.class, Short.class, Byte.class, Integer.class, Long.class, + Float.class, Double.class }) + void throwsExceptionWhenConvertingTheWordNullToPrimitiveWrapperType(Class type) { + assertThatExceptionOfType(ArgumentConversionException.class) // + .isThrownBy(() -> convert("null", type)) // + .withMessage("Failed to convert String \"null\" to type " + type.getCanonicalName()); + assertThatExceptionOfType(ArgumentConversionException.class) // + .isThrownBy(() -> convert("NULL", type)) // + .withMessage("Failed to convert String \"NULL\" to type " + type.getCanonicalName()); + } + @Test void throwsExceptionOnInvalidStringForPrimitiveTypes() { assertThatExceptionOfType(ArgumentConversionException.class) // .isThrownBy(() -> convert("ab", char.class)) // .withMessage("Failed to convert String \"ab\" to type char") // .havingCause() // + .havingCause() // .withMessage("String must have length of 1: ab"); assertThatExceptionOfType(ArgumentConversionException.class) // .isThrownBy(() -> convert("tru", boolean.class)) // .withMessage("Failed to convert String \"tru\" to type boolean") // .havingCause() // + .havingCause() // .withMessage("String must be 'true' or 'false' (ignoring case): tru"); + + assertThatExceptionOfType(ArgumentConversionException.class) // + .isThrownBy(() -> convert("null", boolean.class)) // + .withMessage("Failed to convert String \"null\" to type boolean") // + .havingCause() // + .havingCause() // + .withMessage("String must be 'true' or 'false' (ignoring case): null"); + + assertThatExceptionOfType(ArgumentConversionException.class) // + .isThrownBy(() -> convert("NULL", boolean.class)) // + .withMessage("Failed to convert String \"NULL\" to type boolean") // + .havingCause() // + .havingCause() // + .withMessage("String must be 'true' or 'false' (ignoring case): NULL"); } @Test @@ -281,6 +342,7 @@ void convertsStringToCurrency() { } @Test + @SuppressWarnings("deprecation") void convertsStringToLocale() { assertConverts("en", Locale.class, Locale.ENGLISH); assertConverts("en_us", Locale.class, new Locale(Locale.US.toString())); diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverterTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverterTests.java similarity index 98% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverterTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverterTests.java index 5e3a54e2d271..3983886cd476 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverterTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/TypedArgumentConverterTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/converter/TypedArgumentConverterTests.java similarity index 98% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/TypedArgumentConverterTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/converter/TypedArgumentConverterTests.java index c32a0313968e..0881c45231cd 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/TypedArgumentConverterTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/converter/TypedArgumentConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProviderTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProviderTests.java similarity index 72% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProviderTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProviderTests.java index fe37ddc7e557..af6e1eec06bd 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProviderTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,7 @@ package org.junit.jupiter.params.provider; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.params.provider.MockCsvAnnotationBuilder.csvSource; import static org.mockito.ArgumentMatchers.eq; @@ -30,7 +31,7 @@ class AnnotationBasedArgumentsProviderTests { private final AnnotationBasedArgumentsProvider annotationBasedArgumentsProvider = new AnnotationBasedArgumentsProvider<>() { @Override protected Stream provideArguments(ExtensionContext context, CsvSource annotation) { - return Stream.empty(); + return Stream.of(Arguments.of(annotation)); } }; @@ -54,4 +55,21 @@ void shouldInvokeTemplateMethodWithTheAnnotationProvidedToAccept() { verify(spiedProvider, atMostOnce()).provideArguments(eq(extensionContext), eq(annotation)); } + @Test + @DisplayName("should invoke the provideArguments template method for every accepted annotation") + void shouldInvokeTemplateMethodForEachAnnotationProvided() { + var extensionContext = mock(ExtensionContext.class); + var foo = csvSource("foo"); + var bar = csvSource("bar"); + + annotationBasedArgumentsProvider.accept(foo); + annotationBasedArgumentsProvider.accept(bar); + + var arguments = annotationBasedArgumentsProvider.provideArguments(extensionContext).toList(); + + assertThat(arguments).hasSize(2); + assertThat(arguments.getFirst().get()[0]).isEqualTo(foo); + assertThat(arguments.get(1).get()[0]).isEqualTo(bar); + } + } diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java similarity index 96% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java index 48958cf40eb2..3a56f283a2da 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/CsvArgumentsProviderTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/CsvArgumentsProviderTests.java similarity index 90% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/CsvArgumentsProviderTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/provider/CsvArgumentsProviderTests.java index 767768ce6bac..4aec498e9eca 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/CsvArgumentsProviderTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/CsvArgumentsProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -18,6 +18,7 @@ import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.PreconditionViolationException; @@ -40,7 +41,7 @@ void throwsExceptionIfNeitherValueNorTextBlockIsDeclared() { var annotation = csvSource().build(); assertThatExceptionOfType(PreconditionViolationException.class)// - .isThrownBy(() -> provideArguments(annotation))// + .isThrownBy(() -> provideArguments(annotation).findAny())// .withMessage("@CsvSource must be declared with either `value` or `textBlock` but not both"); } @@ -52,7 +53,7 @@ void throwsExceptionIfValueAndTextBlockAreDeclared() { """).build(); assertThatExceptionOfType(PreconditionViolationException.class)// - .isThrownBy(() -> provideArguments(annotation))// + .isThrownBy(() -> provideArguments(annotation).findAny())// .withMessage("@CsvSource must be declared with either `value` or `textBlock` but not both"); } @@ -223,7 +224,7 @@ void throwsExceptionIfBothDelimitersAreSimultaneouslySet() { var annotation = csvSource().delimiter('|').delimiterString("~~~").build(); assertThatExceptionOfType(PreconditionViolationException.class)// - .isThrownBy(() -> provideArguments(annotation))// + .isThrownBy(() -> provideArguments(annotation).findAny())// .withMessageStartingWith("The delimiter and delimiterString attributes cannot be set simultaneously in")// .withMessageContaining("CsvSource"); } @@ -248,11 +249,12 @@ void customEmptyValueAndDefaultNullValue() { @Test void customNullValues() { - var annotation = csvSource().nullValues("N/A", "NIL").lines("apple, , NIL, '', N/A, banana").build(); + var annotation = csvSource().nullValues("N/A", "NIL", "null")// + .lines("apple, , NIL, '', N/A, banana, null").build(); var arguments = provideArguments(annotation); - assertThat(arguments).containsExactly(array("apple", null, null, "", null, "banana")); + assertThat(arguments).containsExactly(array("apple", null, null, "", null, "banana", null)); } @Test @@ -269,7 +271,7 @@ void throwsExceptionIfSourceExceedsMaxCharsPerColumnConfig() { var annotation = csvSource().lines("413").maxCharsPerColumn(2).build(); assertThatExceptionOfType(CsvParsingException.class)// - .isThrownBy(() -> provideArguments(annotation))// + .isThrownBy(() -> provideArguments(annotation).findAny())// .withMessageStartingWith("Failed to parse CSV input configured via Mock for CsvSource")// .withRootCauseInstanceOf(ArrayIndexOutOfBoundsException.class); } @@ -288,27 +290,28 @@ void throwsExceptionWhenSourceExceedsDefaultMaxCharsPerColumnConfig() { var annotation = csvSource().lines("0".repeat(4097)).delimiter(';').build(); assertThatExceptionOfType(CsvParsingException.class)// - .isThrownBy(() -> provideArguments(annotation))// + .isThrownBy(() -> provideArguments(annotation).findAny())// .withMessageStartingWith("Failed to parse CSV input configured via Mock for CsvSource")// .withRootCauseInstanceOf(ArrayIndexOutOfBoundsException.class); } @Test void providesArgumentsForExceedsSourceWithCustomMaxCharsPerColumnConfig() { - var annotation = csvSource().lines("0".repeat(4097)).delimiter(';').maxCharsPerColumn(4097).build(); + var annotation = csvSource().lines("0".repeat(4097)).maxCharsPerColumn(4097).build(); var arguments = provideArguments(annotation); assertThat(arguments.toArray()).hasSize(1); } - @Test - void throwsExceptionWhenMaxCharsPerColumnIsNotPositiveNumber() { - var annotation = csvSource().lines("41").delimiter(';').maxCharsPerColumn(-1).build(); + @ParameterizedTest + @ValueSource(ints = { Integer.MIN_VALUE, -2, 0 }) + void throwsExceptionWhenMaxCharsPerColumnIsNotPositiveNumberOrMinusOne(int maxCharsPerColumn) { + var annotation = csvSource().lines("41").maxCharsPerColumn(maxCharsPerColumn).build(); assertThatExceptionOfType(PreconditionViolationException.class)// - .isThrownBy(() -> provideArguments(annotation))// - .withMessageStartingWith("maxCharsPerColumn must be a positive number: -1"); + .isThrownBy(() -> provideArguments(annotation).findAny())// + .withMessageStartingWith("maxCharsPerColumn must be a positive number or -1: " + maxCharsPerColumn); } @Test @@ -371,7 +374,7 @@ void throwsExceptionIfColumnCountExceedsHeaderCount() { """).build(); assertThatExceptionOfType(PreconditionViolationException.class)// - .isThrownBy(() -> provideArguments(annotation))// + .isThrownBy(() -> provideArguments(annotation).findAny())// .withMessage( "The number of columns (3) exceeds the number of supplied headers (2) in CSV record: [banana, 2, BOOM!]"); } diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/CsvFileArgumentsProviderTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/CsvFileArgumentsProviderTests.java similarity index 84% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/CsvFileArgumentsProviderTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/provider/CsvFileArgumentsProviderTests.java index 1933e502656f..f43ea0a2d01f 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/CsvFileArgumentsProviderTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/CsvFileArgumentsProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -19,7 +19,6 @@ import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; @@ -30,6 +29,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvFileArgumentsProvider.InputStreamProvider; import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.PreconditionViolationException; @@ -98,7 +98,8 @@ void throwsExceptionIfBothDelimitersAreSimultaneouslySet() { .delimiterString(";")// .build(); - var exception = assertThrows(PreconditionViolationException.class, () -> provideArguments(annotation, "foo")); + var exception = assertThrows(PreconditionViolationException.class, + () -> provideArguments(annotation, "foo").findAny()); assertThat(exception)// .hasMessageStartingWith("The delimiter and delimiterString attributes cannot be set simultaneously in")// @@ -157,7 +158,7 @@ public void close() { void readsFromSingleClasspathResource() { var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/single-column.csv")// + .resources("single-column.csv")// .build(); var arguments = provideArguments(new CsvFileArgumentsProvider(), annotation); @@ -167,7 +168,7 @@ void readsFromSingleClasspathResource() { @Test void readsFromSingleFileWithAbsolutePath(@TempDir Path tempDir) throws Exception { - var csvFile = writeClasspathResourceToFile("/single-column.csv", tempDir.resolve("single-column.csv")); + var csvFile = writeClasspathResourceToFile("single-column.csv", tempDir.resolve("single-column.csv")); var annotation = csvFileSource()// .encoding("ISO-8859-1")// .files(csvFile.toAbsolutePath().toString())// @@ -180,10 +181,10 @@ void readsFromSingleFileWithAbsolutePath(@TempDir Path tempDir) throws Exception @Test void readsFromClasspathResourcesAndFiles(@TempDir Path tempDir) throws Exception { - var csvFile = writeClasspathResourceToFile("/single-column.csv", tempDir.resolve("single-column.csv")); + var csvFile = writeClasspathResourceToFile("single-column.csv", tempDir.resolve("single-column.csv")); var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/single-column.csv")// + .resources("single-column.csv")// .files(csvFile.toAbsolutePath().toString())// .build(); @@ -194,7 +195,7 @@ void readsFromClasspathResourcesAndFiles(@TempDir Path tempDir) throws Exception @Test void readsFromSingleFileWithRelativePath() throws Exception { - var csvFile = writeClasspathResourceToFile("/single-column.csv", Path.of("single-column.csv")); + var csvFile = writeClasspathResourceToFile("single-column.csv", Path.of("single-column.csv")); try { var annotation = csvFileSource()// .encoding("ISO-8859-1")// @@ -214,7 +215,7 @@ void readsFromSingleFileWithRelativePath() throws Exception { void readsFromSingleClasspathResourceWithCustomEmptyValue() { var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/single-column.csv")// + .resources("single-column.csv")// .emptyValue("EMPTY")// .build(); @@ -227,7 +228,7 @@ void readsFromSingleClasspathResourceWithCustomEmptyValue() { void readsFromMultipleClasspathResources() { var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/single-column.csv", "/single-column.csv")// + .resources("single-column.csv", "single-column.csv")// .build(); var arguments = provideArguments(new CsvFileArgumentsProvider(), annotation); @@ -239,7 +240,7 @@ void readsFromMultipleClasspathResources() { void readsFromSingleClasspathResourceWithHeaders() { var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/single-column.csv")// + .resources("single-column.csv")// .numLinesToSkip(1)// .build(); @@ -252,7 +253,7 @@ void readsFromSingleClasspathResourceWithHeaders() { void readsFromSingleClasspathResourceWithMoreHeadersThanLines() { var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/single-column.csv")// + .resources("single-column.csv")// .numLinesToSkip(10)// .build(); @@ -265,7 +266,7 @@ void readsFromSingleClasspathResourceWithMoreHeadersThanLines() { void readsFromMultipleClasspathResourcesWithHeaders() { var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/single-column.csv", "/single-column.csv")// + .resources("single-column.csv", "single-column.csv")// .numLinesToSkip(1)// .build(); @@ -279,7 +280,7 @@ void readsFromMultipleClasspathResourcesWithHeaders() { void supportsCsvHeadersInDisplayNames() { var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/single-column.csv")// + .resources("single-column.csv")// .useHeadersInDisplayName(true)// .build(); @@ -369,7 +370,7 @@ void throwsExceptionForInvalidCharset() { @Test void throwsExceptionForInvalidCsvFormat() { var annotation = csvFileSource()// - .resources("/broken.csv")// + .resources("broken.csv")// .build(); var exception = assertThrows(CsvParsingException.class, @@ -396,10 +397,10 @@ void emptyValueIsAnEmptyWithCustomNullValueString() { @Test void readsLineFromDefaultMaxCharsFileWithDefaultConfig(@TempDir Path tempDir) throws Exception { - var csvFile = writeClasspathResourceToFile("/default-max-chars.csv", tempDir.resolve("default-max-chars.csv")); + var csvFile = writeClasspathResourceToFile("default-max-chars.csv", tempDir.resolve("default-max-chars.csv")); var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/default-max-chars.csv")// + .resources("default-max-chars.csv")// .files(csvFile.toAbsolutePath().toString())// .build(); @@ -409,12 +410,12 @@ void readsLineFromDefaultMaxCharsFileWithDefaultConfig(@TempDir Path tempDir) th } @Test - void readsLineFromExceedsMaxCharsFileWithCustomConfig(@TempDir Path tempDir) throws java.io.IOException { - var csvFile = writeClasspathResourceToFile("/exceeds-default-max-chars.csv", + void readsLineFromExceedsMaxCharsFileWithCustomExplicitConfig(@TempDir Path tempDir) throws Exception { + var csvFile = writeClasspathResourceToFile("exceeds-default-max-chars.csv", tempDir.resolve("exceeds-default-max-chars.csv")); var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/exceeds-default-max-chars.csv")// + .resources("exceeds-default-max-chars.csv")// .maxCharsPerColumn(4097)// .files(csvFile.toAbsolutePath().toString())// .build(); @@ -425,29 +426,54 @@ void readsLineFromExceedsMaxCharsFileWithCustomConfig(@TempDir Path tempDir) thr } @Test - void throwsExceptionWhenMaxCharsPerColumnIsNotPositiveNumber(@TempDir Path tempDir) throws java.io.IOException { - var csvFile = writeClasspathResourceToFile("/exceeds-default-max-chars.csv", + void readsLineFromExceedsMaxCharsFileWithCustomUnlimitedConfig(@TempDir Path tempDir) throws Exception { + var csvFile = tempDir.resolve("test.csv"); + try (var out = Files.newBufferedWriter(csvFile)) { + var chunks = 10; + var chunk = "a".repeat(8192); + for (long i = 0; i < chunks; i++) { + out.write(chunk); + } + } + + var annotation = csvFileSource()// + .encoding("ISO-8859-1")// + .maxCharsPerColumn(-1)// + .files(csvFile.toAbsolutePath().toString())// + .build(); + + var arguments = provideArguments(new CsvFileArgumentsProvider(), annotation); + + assertThat(arguments).hasSize(1); + } + + @ParameterizedTest + @ValueSource(ints = { Integer.MIN_VALUE, -2, 0 }) + void throwsExceptionWhenMaxCharsPerColumnIsNotPositiveNumberOrMinusOne(int maxCharsPerColumn, @TempDir Path tempDir) + throws Exception { + var csvFile = writeClasspathResourceToFile("exceeds-default-max-chars.csv", tempDir.resolve("exceeds-default-max-chars.csv")); var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/exceeds-default-max-chars.csv")// - .maxCharsPerColumn(-1).files(csvFile.toAbsolutePath().toString())// + .resources("exceeds-default-max-chars.csv")// + .maxCharsPerColumn(maxCharsPerColumn)// + .files(csvFile.toAbsolutePath().toString())// .build(); var exception = assertThrows(PreconditionViolationException.class, // - () -> provideArguments(new CsvFileArgumentsProvider(), annotation)); + () -> provideArguments(new CsvFileArgumentsProvider(), annotation).findAny()); assertThat(exception)// - .hasMessageStartingWith("maxCharsPerColumn must be a positive number: -1"); + .hasMessageStartingWith("maxCharsPerColumn must be a positive number or -1: " + maxCharsPerColumn); } @Test - void throwsExceptionForExceedsMaxCharsFileWithDefaultConfig(@TempDir Path tempDir) throws java.io.IOException { - var csvFile = writeClasspathResourceToFile("/exceeds-default-max-chars.csv", + void throwsExceptionForExceedsMaxCharsFileWithDefaultConfig(@TempDir Path tempDir) throws Exception { + var csvFile = writeClasspathResourceToFile("exceeds-default-max-chars.csv", tempDir.resolve("exceeds-default-max-chars.csv")); var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/exceeds-default-max-chars.csv")// + .resources("exceeds-default-max-chars.csv")// .files(csvFile.toAbsolutePath().toString())// .build(); @@ -460,12 +486,12 @@ void throwsExceptionForExceedsMaxCharsFileWithDefaultConfig(@TempDir Path tempDi } @Test - void ignoresLeadingAndTrailingSpaces(@TempDir Path tempDir) throws IOException { - var csvFile = writeClasspathResourceToFile("/leading-trailing-spaces.csv", + void ignoresLeadingAndTrailingSpaces(@TempDir Path tempDir) throws Exception { + var csvFile = writeClasspathResourceToFile("leading-trailing-spaces.csv", tempDir.resolve("leading-trailing-spaces.csv")); var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/leading-trailing-spaces.csv")// + .resources("leading-trailing-spaces.csv")// .files(csvFile.toAbsolutePath().toString())// .ignoreLeadingAndTrailingWhitespace(true)// .build(); @@ -476,12 +502,12 @@ void ignoresLeadingAndTrailingSpaces(@TempDir Path tempDir) throws IOException { } @Test - void trimsLeadingAndTrailingSpaces(@TempDir Path tempDir) throws IOException { - var csvFile = writeClasspathResourceToFile("/leading-trailing-spaces.csv", + void trimsLeadingAndTrailingSpaces(@TempDir Path tempDir) throws Exception { + var csvFile = writeClasspathResourceToFile("leading-trailing-spaces.csv", tempDir.resolve("leading-trailing-spaces.csv")); var annotation = csvFileSource()// .encoding("ISO-8859-1")// - .resources("/leading-trailing-spaces.csv")// + .resources("leading-trailing-spaces.csv")// .files(csvFile.toAbsolutePath().toString())// .delimiter(',')// .ignoreLeadingAndTrailingWhitespace(false)// @@ -526,7 +552,7 @@ private static T[] array(T... elements) { return elements; } - private static Path writeClasspathResourceToFile(String name, Path target) throws IOException { + private static Path writeClasspathResourceToFile(String name, Path target) throws Exception { try (var in = CsvFileArgumentsProviderTests.class.getResourceAsStream(name)) { Files.copy(in, target); } diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/EnumArgumentsProviderTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/EnumArgumentsProviderTests.java similarity index 95% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/EnumArgumentsProviderTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/provider/EnumArgumentsProviderTests.java index eb265d3e8ffc..6a5312085776 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/EnumArgumentsProviderTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/EnumArgumentsProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -56,21 +56,21 @@ void provideAllEnumConstantsWithNamingAll() { @Test void duplicateConstantNameIsDetected() { Exception exception = assertThrows(PreconditionViolationException.class, - () -> provideArguments(EnumWithTwoConstants.class, "FOO", "BAR", "FOO")); + () -> provideArguments(EnumWithTwoConstants.class, "FOO", "BAR", "FOO").findAny()); assertThat(exception).hasMessageContaining("Duplicate enum constant name(s) found"); } @Test void invalidConstantNameIsDetected() { Exception exception = assertThrows(PreconditionViolationException.class, - () -> provideArguments(EnumWithTwoConstants.class, "FO0", "B4R")); + () -> provideArguments(EnumWithTwoConstants.class, "FO0", "B4R").findAny()); assertThat(exception).hasMessageContaining("Invalid enum constant name(s) in"); } @Test void invalidPatternIsDetected() { Exception exception = assertThrows(PreconditionViolationException.class, - () -> provideArguments(EnumWithTwoConstants.class, Mode.MATCH_ALL, "(", ")")); + () -> provideArguments(EnumWithTwoConstants.class, Mode.MATCH_ALL, "(", ")").findAny()); assertThat(exception).hasMessageContaining("Pattern compilation failed"); } @@ -90,7 +90,7 @@ void incorrectParameterTypeIsDetected() throws Exception { TestCase.class.getDeclaredMethod("methodWithIncorrectParameter", Object.class)); Exception exception = assertThrows(PreconditionViolationException.class, - () -> provideArguments(NullEnum.class)); + () -> provideArguments(NullEnum.class).findAny()); assertThat(exception).hasMessageStartingWith("First parameter must reference an Enum type"); } @@ -100,7 +100,7 @@ void methodsWithoutParametersAreDetected() throws Exception { TestCase.class.getDeclaredMethod("methodWithoutParameters")); Exception exception = assertThrows(PreconditionViolationException.class, - () -> provideArguments(NullEnum.class)); + () -> provideArguments(NullEnum.class).findAny()); assertThat(exception).hasMessageStartingWith("Test method must declare at least one parameter"); } diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/EnumSourceTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/EnumSourceTests.java similarity index 98% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/EnumSourceTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/provider/EnumSourceTests.java index f36a31407aca..819dd6bd56df 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/EnumSourceTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/EnumSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -107,7 +107,7 @@ void matchesNone() { } enum EnumWithThreeConstants { - FOO, BAR, BAZ; + FOO, BAR, BAZ } diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/FieldArgumentsProviderTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/FieldArgumentsProviderTests.java new file mode 100644 index 000000000000..3ab74b340d84 --- /dev/null +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/FieldArgumentsProviderTests.java @@ -0,0 +1,621 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params.provider; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.engine.extension.MutableExtensionRegistry.createRegistryWithDefaultExtensions; +import static org.junit.platform.commons.util.ReflectionUtils.findMethod; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.engine.execution.DefaultExecutableInvoker; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.platform.commons.JUnitException; +import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.commons.test.TestClassLoader; +import org.junit.platform.commons.util.ReflectionUtils; + +/** + * Unit tests for {@link FieldArgumentsProvider}. + * + * @since 5.11 + */ +class FieldArgumentsProviderTests { + + @Test + void providesArgumentsUsingStreamSupplier() { + var arguments = provideArguments("stringStreamSupplier"); + + assertThat(arguments).containsExactly(array("foo"), array("bar")); + } + + @Test + void providesArgumentsUsingIntStreamSupplier() { + var arguments = provideArguments("intStreamSupplier"); + + assertThat(arguments).containsExactly(array(1), array(2)); + } + + @Test + void providesArgumentsUsingLongStreamSupplier() { + var arguments = provideArguments("longStreamSupplier"); + + assertThat(arguments).containsExactly(array(1L), array(2L)); + } + + @Test + void providesArgumentsUsingDoubleStreamSupplier() { + var arguments = provideArguments("doubleStreamSupplier"); + + assertThat(arguments).containsExactly(array(1.2), array(3.4)); + } + + @Test + void providesArgumentsUsingStreamSupplierOfIntArrays() { + var arguments = provideArguments("intArrayStreamSupplier"); + + assertThat(arguments).containsExactly( // + new Object[] { new int[] { 1, 2 } }, // + new Object[] { new int[] { 3, 4 } } // + ); + } + + @Test + void providesArgumentsUsingStreamSupplierOfTwoDimensionalIntArrays() { + var arguments = provideArguments("twoDimensionalIntArrayStreamSupplier"); + + assertThat(arguments).containsExactly( // + array((Object) new int[][] { { 1, 2 }, { 2, 3 } }), // + array((Object) new int[][] { { 4, 5 }, { 5, 6 } }) // + ); + } + + @Test + void providesArgumentsUsingStreamSupplierOfObjectArrays() { + var arguments = provideArguments("objectArrayStreamSupplier"); + + assertThat(arguments).containsExactly(array("foo", 42), array("bar", 23)); + } + + @Test + void providesArgumentsUsingStreamSupplierOfTwoDimensionalObjectArrays() { + var arguments = provideArguments("twoDimensionalObjectArrayStreamSupplier"); + + assertThat(arguments).containsExactly( // + array((Object) array(array("a", 1), array("b", 2))), // + array((Object) array(array("c", 3), array("d", 4))) // + ); + } + + @Test + void providesArgumentsUsingStreamSupplierOfArguments() { + var arguments = provideArguments("argumentsStreamSupplier"); + + assertThat(arguments).containsExactly(array("foo", 42), array("bar", 23)); + } + + @Test + void providesArgumentsUsingIterable() { + var arguments = provideArguments("stringIterable"); + + assertThat(arguments).containsExactly(array("foo"), array("bar")); + } + + @Test + void providesArgumentsUsingMultipleFields() { + var arguments = provideArguments("stringStreamSupplier", "stringIterable"); + + assertThat(arguments).containsExactly(array("foo"), array("bar"), array("foo"), array("bar")); + } + + @Test + void providesArgumentsUsingIterableOfObjectArrays() { + var arguments = provideArguments("objectArrayIterable"); + + assertThat(arguments).containsExactly(array("foo", 42), array("bar", 23)); + } + + @Test + void providesArgumentsUsingListOfStrings() { + var arguments = provideArguments("stringList"); + + assertThat(arguments).containsExactly(array("foo"), array("bar")); + } + + @Test + void providesArgumentsUsingListOfObjectArrays() { + var arguments = provideArguments("objectArrayList"); + + assertThat(arguments).containsExactly(array("foo", 42), array("bar", 23)); + } + + @Test + void providesArgumentsFromNonStaticFieldWhenStaticIsNotRequired() { + var lifecyclePerClass = true; + var arguments = provideArguments(NonStaticTestCase.class, lifecyclePerClass, "nonStaticStringStreamSupplier"); + + assertThat(arguments).containsExactly(array("foo"), array("bar")); + } + + @Test + void providesArgumentsUsingDefaultFieldName() { + var testClass = DefaultFieldNameTestCase.class; + var methodName = "testDefaultFieldName"; + var testMethod = findMethod(testClass, methodName, String.class).get(); + + var arguments = provideArguments(testClass, testMethod, false, new String[0]); + + assertThat(arguments).containsExactly(array("foo"), array("bar")); + } + + @Test + void providesArgumentsUsingExternalField() { + var arguments = provideArguments(ExternalFields.class.getName() + "#strings"); + + assertThat(arguments).containsExactly(array("string1"), array("string2")); + } + + @Test + void providesArgumentsUsingExternalFieldInTypeFromDifferentClassLoader() throws Exception { + try (var testClassLoader = TestClassLoader.forClasses(TestCase.class, ExternalFields.class)) { + var testClass = testClassLoader.loadClass(TestCase.class.getName()); + var fullyQualifiedFieldName = ExternalFields.class.getName() + "#strings"; + + assertThat(testClass.getClassLoader()).isSameAs(testClassLoader); + + var arguments = provideArguments(testClass, false, fullyQualifiedFieldName); + assertThat(arguments).containsExactly(array("string1"), array("string2")); + + var field = FieldArgumentsProvider.findField(testClass, fullyQualifiedFieldName); + assertThat(field).isNotNull(); + assertThat(field.getName()).isEqualTo("strings"); + + var declaringClass = field.getDeclaringClass(); + assertThat(declaringClass.getName()).isEqualTo(ExternalFields.class.getName()); + assertThat(declaringClass).isNotEqualTo(ExternalFields.class); + assertThat(declaringClass.getClassLoader()).isSameAs(testClassLoader); + } + } + + @Test + void providesArgumentsUsingExternalFieldFromStaticNestedClass() { + var arguments = provideArguments(ExternalFields.Nested.class.getName() + "#strings"); + + assertThat(arguments).containsExactly(array("nested string1"), array("nested string2")); + } + + @Test + void providesArgumentsUsingExternalAndInternalFieldsCombined() { + var arguments = provideArguments("stringStreamSupplier", ExternalFields.class.getName() + "#strings"); + + assertThat(arguments).containsExactly(array("foo"), array("bar"), array("string1"), array("string2")); + } + + @Nested + class PrimitiveArrays { + + @Test + void providesArgumentsUsingBooleanArray() { + var arguments = provideArguments("booleanArray"); + + assertThat(arguments).containsExactly(array(Boolean.TRUE), array(Boolean.FALSE)); + } + + @Test + void providesArgumentsUsingByteArray() { + var arguments = provideArguments("byteArray"); + + assertThat(arguments).containsExactly(array((byte) 1), array(Byte.MIN_VALUE)); + } + + @Test + void providesArgumentsUsingCharArray() { + var arguments = provideArguments("charArray"); + + assertThat(arguments).containsExactly(array((char) 1), array(Character.MIN_VALUE)); + } + + @Test + void providesArgumentsUsingDoubleArray() { + var arguments = provideArguments("doubleArray"); + + assertThat(arguments).containsExactly(array(1d), array(Double.MIN_VALUE)); + } + + @Test + void providesArgumentsUsingFloatArray() { + var arguments = provideArguments("floatArray"); + + assertThat(arguments).containsExactly(array(1f), array(Float.MIN_VALUE)); + } + + @Test + void providesArgumentsUsingIntArray() { + var arguments = provideArguments("intArray"); + + assertThat(arguments).containsExactly(array(47), array(Integer.MIN_VALUE)); + } + + @Test + void providesArgumentsUsingLongArray() { + var arguments = provideArguments("longArray"); + + assertThat(arguments).containsExactly(array(47L), array(Long.MIN_VALUE)); + } + + @Test + void providesArgumentsUsingShortArray() { + var arguments = provideArguments("shortArray"); + + assertThat(arguments).containsExactly(array((short) 47), array(Short.MIN_VALUE)); + } + + } + + @Nested + class ObjectArrays { + + @Test + void providesArgumentsUsingObjectArray() { + var arguments = provideArguments("objectArray"); + + assertThat(arguments).containsExactly(array(42), array("bar")); + } + + @Test + void providesArgumentsUsingStringArray() { + var arguments = provideArguments("stringArray"); + + assertThat(arguments).containsExactly(array("foo"), array("bar")); + } + + @Test + void providesArgumentsUsing2dStringArray() { + var arguments = provideArguments("twoDimensionalStringArray"); + + assertThat(arguments).containsExactly(array("foo", "bar"), array("baz", "qux")); + } + + @Test + void providesArgumentsUsing2dObjectArray() { + var arguments = provideArguments("twoDimensionalObjectArray"); + + assertThat(arguments).containsExactly(array("foo", 42), array("bar", 23)); + } + + } + + @Nested + class ErrorCases { + + @Test + void throwsExceptionWhenNonStaticLocalFieldIsReferencedWithLifecyclePerMethodSemantics() { + var lifecyclePerClass = false; + var exception = assertThrows(PreconditionViolationException.class, + () -> provideArguments(NonStaticTestCase.class, lifecyclePerClass, + "nonStaticStringStreamSupplier").toArray()); + + assertStaticIsRequired(exception); + } + + @Test + void throwsExceptionWhenNonStaticExternalFieldIsReferencedWithLifecyclePerMethodSemantics() { + var factoryClass = NonStaticTestCase.class.getName(); + var field = factoryClass + "#nonStaticStringStreamSupplier"; + var lifecyclePerClass = false; + var exception = assertThrows(PreconditionViolationException.class, + () -> provideArguments(TestCase.class, lifecyclePerClass, field).toArray()); + + assertStaticIsRequired(exception); + } + + @Test + void throwsExceptionWhenNonStaticExternalFieldIsReferencedWithLifecyclePerClassSemantics() { + var factoryClass = NonStaticTestCase.class.getName(); + var field = factoryClass + "#nonStaticStringStreamSupplier"; + boolean lifecyclePerClass = true; + var exception = assertThrows(PreconditionViolationException.class, + () -> provideArguments(TestCase.class, lifecyclePerClass, field).toArray()); + + assertStaticIsRequired(exception); + } + + private static void assertStaticIsRequired(PreconditionViolationException exception) { + assertThat(exception).hasMessageContainingAll("Field '", + "' must be static: local @FieldSource fields must be static ", + "unless the PER_CLASS @TestInstance lifecycle mode is used; ", + "external @FieldSource fields must always be static."); + } + + @ParameterizedTest + @ValueSource(strings = { "org.example.MyUtils", "org.example.MyUtils#", "#fieldName" }) + void throwsExceptionWhenFullyQualifiedFieldNameSyntaxIsInvalid(String fieldName) { + var exception = assertThrows(PreconditionViolationException.class, + () -> provideArguments(fieldName).toArray()); + + assertThat(exception.getMessage()).isEqualTo(""" + [%s] is not a valid fully qualified field name: \ + it must start with a fully qualified class name followed by a \ + '#' and then the field name.""", fieldName, TestCase.class.getName()); + } + + @Test + void throwsExceptionWhenClassForExternalFieldCannotBeLoaded() { + var exception = assertThrows(JUnitException.class, + () -> provideArguments("com.example.NonExistentClass#strings").toArray()); + + assertThat(exception.getMessage()).isEqualTo("Could not load class [com.example.NonExistentClass]"); + } + + @Test + void throwsExceptionWhenLocalFieldDoesNotExist() { + var exception = assertThrows(PreconditionViolationException.class, + () -> provideArguments("nonExistentField").toArray()); + + assertThat(exception.getMessage()).isEqualTo("Could not find field named [nonExistentField] in class [%s]", + TestCase.class.getName()); + } + + @ParameterizedTest + @ValueSource(strings = { "nonExistentField", "strings()" }) + void throwsExceptionWhenExternalFieldDoesNotExist(String fieldName) { + String factoryClass = ExternalFields.class.getName(); + + var exception = assertThrows(PreconditionViolationException.class, + () -> provideArguments(factoryClass + "#" + fieldName).toArray()); + + assertThat(exception.getMessage()).isEqualTo("Could not find field named [%s] in class [%s]", fieldName, + factoryClass); + } + + @Test + void throwsExceptionWhenLocalFieldHasNullValue() { + String field = "nullList"; + String factoryClass = TestCase.class.getName(); + + var exception = assertThrows(PreconditionViolationException.class, () -> provideArguments(field).toArray()); + + assertThat(exception.getMessage()).isEqualTo("The value of field [%s] in class [%s] must not be null", + field, factoryClass); + } + + @Test + void throwsExceptionWhenLocalFieldHasInvalidReturnType() { + String field = "object"; + String factoryClass = TestCase.class.getName(); + + var exception = assertThrows(PreconditionViolationException.class, () -> provideArguments(field).toArray()); + + assertThat(exception.getMessage()).isEqualTo( + "The value of field [%s] in class [%s] must be convertible to a Stream", field, factoryClass); + } + + @Test + void throwsExceptionWhenExternalFieldHasInvalidReturnType() { + String factoryClass = ExternalFields.class.getName(); + String fieldName = "object"; + String field = factoryClass + "#" + fieldName; + + var exception = assertThrows(PreconditionViolationException.class, + () -> provideArguments(TestCase.class, false, field).toArray()); + + assertThat(exception.getMessage()).isEqualTo( + "The value of field [%s] in class [%s] must be convertible to a Stream", fieldName, factoryClass); + } + + @ParameterizedTest + @ValueSource(strings = { "stream", "intStream", "longStream", "doubleStream" }) + void throwsExceptionWhenLocalFieldHasStreamReturnType(String field) { + String factoryClass = TestCase.class.getName(); + + var exception = assertThrows(PreconditionViolationException.class, () -> provideArguments(field).toArray()); + + assertThat(exception.getMessage()).isEqualTo("The value of field [%s] in class [%s] must not be a stream", + field, factoryClass); + } + + @Test + void throwsExceptionWhenLocalFieldHasIteratorReturnType() { + String field = "iterator"; + String factoryClass = TestCase.class.getName(); + + var exception = assertThrows(PreconditionViolationException.class, () -> provideArguments(field).toArray()); + + assertThat(exception.getMessage()).isEqualTo( + "The value of field [%s] in class [%s] must not be an Iterator", field, factoryClass); + } + + } + + // ------------------------------------------------------------------------- + + private static Object[] array(Object... objects) { + return objects; + } + + private static Stream provideArguments(String... fieldNames) { + return provideArguments(TestCase.class, false, fieldNames); + } + + private static Stream provideArguments(Class testClass, boolean allowNonStaticMethod, + String... fieldNames) { + + // Ensure we have a non-null test method, even if it's not a real test method. + // If this throws an exception, make sure that the supplied test class defines a "void test()" method. + Method testMethod = ReflectionUtils.findMethod(testClass, "test").get(); + return provideArguments(testClass, testMethod, allowNonStaticMethod, fieldNames); + } + + private static Stream provideArguments(Class testClass, Method testMethod, + boolean allowNonStaticMethod, String... fieldNames) { + + var extensionRegistry = createRegistryWithDefaultExtensions(mock()); + var fieldSource = mock(FieldSource.class); + + when(fieldSource.value()).thenReturn(fieldNames); + + var extensionContext = mock(ExtensionContext.class); + when(extensionContext.getTestClass()).thenReturn(Optional.of(testClass)); + when(extensionContext.getTestMethod()).thenReturn(Optional.of(testMethod)); + when(extensionContext.getExecutableInvoker()).thenReturn( + new DefaultExecutableInvoker(extensionContext, extensionRegistry)); + + doCallRealMethod().when(extensionContext).getRequiredTestMethod(); + doCallRealMethod().when(extensionContext).getRequiredTestClass(); + + var testInstance = allowNonStaticMethod ? ReflectionUtils.newInstance(testClass) : null; + when(extensionContext.getTestInstance()).thenReturn(Optional.ofNullable(testInstance)); + + var lifeCycle = allowNonStaticMethod ? Lifecycle.PER_CLASS : Lifecycle.PER_METHOD; + when(extensionContext.getTestInstanceLifecycle()).thenReturn(Optional.of(lifeCycle)); + + var provider = new FieldArgumentsProvider(); + provider.accept(fieldSource); + return provider.provideArguments(extensionContext).map(Arguments::get); + } + + // ------------------------------------------------------------------------- + + static class DefaultFieldNameTestCase { + + // Test + void testDefaultFieldName(String param) { + } + + // Field + static List testDefaultFieldName = List.of("foo", "bar"); + } + + static class TestCase { + + void test() { + } + + // --- Invalid --------------------------------------------------------- + + static List nullList = null; + + static Object object = -1; + + static Stream stream = Stream.of("foo", "bar"); + + static DoubleStream doubleStream = DoubleStream.of(1.2, 3.4); + + static IntStream intStream = IntStream.of(1, 2); + + static LongStream longStream = LongStream.of(1L, 2L); + + static Iterator iterator = List.of("foo", "bar").iterator(); + + // --- Stream Supplier ------------------------------------------------- + + static Supplier> stringStreamSupplier = () -> Stream.of("foo", "bar"); + + static Supplier doubleStreamSupplier = () -> DoubleStream.of(1.2, 3.4); + + static Supplier longStreamSupplier = () -> LongStream.of(1L, 2L); + + static Supplier intStreamSupplier = () -> IntStream.of(1, 2); + + static Supplier> intArrayStreamSupplier = // + () -> Stream.of(new int[] { 1, 2 }, new int[] { 3, 4 }); + + static Supplier> twoDimensionalIntArrayStreamSupplier = // + () -> Stream.of(new int[][] { { 1, 2 }, { 2, 3 } }, new int[][] { { 4, 5 }, { 5, 6 } }); + + static Supplier> objectArrayStreamSupplier = // + () -> Stream.of(new Object[] { "foo", 42 }, new Object[] { "bar", 23 }); + + static Supplier> twoDimensionalObjectArrayStreamSupplier = // + () -> Stream.of(new Object[][] { { "a", 1 }, { "b", 2 } }, new Object[][] { { "c", 3 }, { "d", 4 } }); + + static Supplier> argumentsStreamSupplier = // + () -> objectArrayStreamSupplier.get().map(Arguments::of); + + // --- Collection / Iterable ------------------------------------------- + + static List stringList = List.of("foo", "bar"); + + static List objectArrayList = List.of(array("foo", 42), array("bar", 23)); + + static Iterable stringIterable = stringList::iterator; + + static Iterable objectArrayIterable = objectArrayList::iterator; + + // --- Array of primitives --------------------------------------------- + + static boolean[] booleanArray = new boolean[] { true, false }; + + static byte[] byteArray = new byte[] { (byte) 1, Byte.MIN_VALUE }; + + static char[] charArray = new char[] { (char) 1, Character.MIN_VALUE }; + + static double[] doubleArray = new double[] { 1d, Double.MIN_VALUE }; + + static float[] floatArray = new float[] { 1f, Float.MIN_VALUE }; + + static int[] intArray = new int[] { 47, Integer.MIN_VALUE }; + + static long[] longArray = new long[] { 47L, Long.MIN_VALUE }; + + static short[] shortArray = new short[] { (short) 47, Short.MIN_VALUE }; + + // --- Array of objects ------------------------------------------------ + + static Object[] objectArray = new Object[] { 42, "bar" }; + + static String[] stringArray = new String[] { "foo", "bar" }; + + static String[][] twoDimensionalStringArray = new String[][] { { "foo", "bar" }, { "baz", "qux" } }; + + static Object[][] twoDimensionalObjectArray = new Object[][] { { "foo", 42 }, { "bar", 23 } }; + + } + + // This test case mimics @TestInstance(Lifecycle.PER_CLASS) + static class NonStaticTestCase { + + void test() { + } + + Supplier> nonStaticStringStreamSupplier = () -> Stream.of("foo", "bar"); + } + + static class ExternalFields { + + static Object object = -1; + + static List strings = List.of("string1", "string2"); + + static class Nested { + + static List strings = List.of("nested string1", "nested string2"); + + } + } + +} diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/MethodArgumentsProviderTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/MethodArgumentsProviderTests.java similarity index 99% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/MethodArgumentsProviderTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/provider/MethodArgumentsProviderTests.java index 08959e1a8824..54287492112e 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/MethodArgumentsProviderTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/MethodArgumentsProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/MockCsvAnnotationBuilder.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/MockCsvAnnotationBuilder.java similarity index 99% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/MockCsvAnnotationBuilder.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/provider/MockCsvAnnotationBuilder.java index 629709cbb17b..c3b3c10648bb 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/MockCsvAnnotationBuilder.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/MockCsvAnnotationBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/ValueArgumentsProviderTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ValueArgumentsProviderTests.java similarity index 98% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/ValueArgumentsProviderTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ValueArgumentsProviderTests.java index 213178fa805a..dea71ff0f727 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/ValueArgumentsProviderTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ValueArgumentsProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -29,7 +29,7 @@ class ValueArgumentsProviderTests { void multipleInputsAreNotAllowed() { var exception = assertThrows(PreconditionViolationException.class, () -> provideArguments(new short[1], new byte[0], new int[1], new long[0], new float[0], new double[0], - new char[0], new boolean[0], new String[0], new Class[0])); + new char[0], new boolean[0], new String[0], new Class[0]).findAny()); assertThat(exception).hasMessageContaining( "Exactly one type of input must be provided in the @ValueSource annotation, but there were 2"); @@ -39,7 +39,7 @@ void multipleInputsAreNotAllowed() { void onlyEmptyInputsAreNotAllowed() { var exception = assertThrows(PreconditionViolationException.class, () -> provideArguments(new short[0], new byte[0], new int[0], new long[0], new float[0], new double[0], - new char[0], new boolean[0], new String[0], new Class[0])); + new char[0], new boolean[0], new String[0], new Class[0]).findAny()); assertThat(exception).hasMessageContaining( "Exactly one type of input must be provided in the @ValueSource annotation, but there were 0"); diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/support/AnnotationConsumerInitializerTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/support/AnnotationConsumerInitializerTests.java similarity index 83% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/support/AnnotationConsumerInitializerTests.java rename to jupiter-tests/src/test/java/org/junit/jupiter/params/support/AnnotationConsumerInitializerTests.java index 18dce72c2f71..b60da429e03e 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/support/AnnotationConsumerInitializerTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/support/AnnotationConsumerInitializerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,10 +13,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.params.support.AnnotationConsumerInitializer.initialize; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; @@ -52,9 +58,11 @@ void shouldInitializeAnnotationBasedArgumentsProvider() throws NoSuchMethodExcep var method = SubjectClass.class.getDeclaredMethod("foo"); var initialisedAnnotationConsumer = initialize(method, instance); - initialisedAnnotationConsumer.provideArguments(mock()); + initialisedAnnotationConsumer.provideArguments(mock()).findAny(); - assertThat(initialisedAnnotationConsumer.annotation) // + assertThat(initialisedAnnotationConsumer.annotations) // + .hasSize(1) // + .element(0) // .isInstanceOfSatisfying(CsvSource.class, // source -> assertThat(source.value()).containsExactly("a", "b")); } @@ -93,13 +101,23 @@ void shouldThrowExceptionWhenParameterIsNotAnnotated() throws NoSuchMethodExcept assertThatThrownBy(() -> initialize(parameter, instance)).isInstanceOf(JUnitException.class); } + @Test + void shouldInitializeForEachAnnotations() throws NoSuchMethodException { + var instance = spy(new SomeAnnotationBasedArgumentsProvider()); + var method = SubjectClass.class.getDeclaredMethod("repeatableAnnotation", String.class); + + initialize(method, instance); + + verify(instance, times(2)).accept(any(CsvSource.class)); + } + private static class SomeAnnotationBasedArgumentsProvider extends AnnotationBasedArgumentsProvider { - CsvSource annotation; + List annotations = new ArrayList<>(); @Override protected Stream provideArguments(ExtensionContext context, CsvSource annotation) { - this.annotation = annotation; + annotations.add(annotation); return Stream.empty(); } } @@ -138,6 +156,11 @@ void bar(@JavaTimeConversionPattern("pattern") LocalDate date) { void noAnnotation(String param) { } + + @CsvSource("a") + @CsvSource("b") + void repeatableAnnotation(String param) { + } } } diff --git a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/api/KotlinAssertTimeoutAssertionsTests.kt b/jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinAssertTimeoutAssertionsTests.kt similarity index 64% rename from junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/api/KotlinAssertTimeoutAssertionsTests.kt rename to jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinAssertTimeoutAssertionsTests.kt index 9170c22c9e7d..b85229eb0ded 100644 --- a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/api/KotlinAssertTimeoutAssertionsTests.kt +++ b/jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinAssertTimeoutAssertionsTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -26,7 +26,6 @@ import java.util.concurrent.atomic.AtomicBoolean * @since 5.5 */ internal class KotlinAssertTimeoutAssertionsTests { - // --- executable ---------------------------------------------------------- @Test @@ -40,43 +39,48 @@ internal class KotlinAssertTimeoutAssertionsTests { @Test fun assertTimeoutForExecutableThatThrowsAnException() { - val exception = assertThrows { - assertTimeout(ofMillis(500)) { - throw RuntimeException("not this time") + val exception = + assertThrows { + assertTimeout(ofMillis(500)) { + throw RuntimeException("not this time") + } } - } assertMessageEquals(exception, "not this time") } @Test fun assertTimeoutForExecutableThatThrowsAnAssertionFailedError() { - val exception = assertThrows { - assertTimeout(ofMillis(500)) { fail("enigma") } - } + val exception = + assertThrows { + assertTimeout(ofMillis(500)) { fail("enigma") } + } assertMessageEquals(exception, "enigma") } @Test fun assertTimeoutForExecutableThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeout(ofMillis(10)) { this.nap() } - } + val error = + assertThrows { + assertTimeout(ofMillis(10)) { this.nap() } + } assertMessageStartsWith(error, "execution exceeded timeout of 10 ms by") } @Test fun assertTimeoutWithMessageForExecutableThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeout(ofMillis(10), "Tempus Fugit") { this.nap() } - } + val error = + assertThrows { + assertTimeout(ofMillis(10), "Tempus Fugit") { this.nap() } + } assertMessageStartsWith(error, "Tempus Fugit ==> execution exceeded timeout of 10 ms by") } @Test fun assertTimeoutWithMessageSupplierForExecutableThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeout(ofMillis(10), { "Tempus" + " " + "Fugit" }) { this.nap() } - } + val error = + assertThrows { + assertTimeout(ofMillis(10), { "Tempus" + " " + "Fugit" }) { this.nap() } + } assertMessageStartsWith(error, "Tempus Fugit ==> execution exceeded timeout of 10 ms by") } @@ -85,10 +89,11 @@ internal class KotlinAssertTimeoutAssertionsTests { @Test fun assertTimeoutForSupplierThatCompletesBeforeTheTimeout() { changed.get().set(false) - val result = assertTimeout(ofMillis(500)) { - changed.get().set(true) - "Tempus Fugit" - } + val result = + assertTimeout(ofMillis(500)) { + changed.get().set(true) + "Tempus Fugit" + } assertTrue(changed.get().get(), "should have executed in the same thread") assertEquals("Tempus Fugit", result) assertEquals("Tempus Fugit", assertTimeout(ofMillis(500), "message") { "Tempus Fugit" }) @@ -97,51 +102,56 @@ internal class KotlinAssertTimeoutAssertionsTests { @Test fun assertTimeoutForSupplierThatThrowsAnException() { - val exception = assertThrows { - assertTimeout(ofMillis(500)) { - ExceptionUtils.throwAsUncheckedException(RuntimeException("not this time")) + val exception = + assertThrows { + assertTimeout(ofMillis(500)) { + ExceptionUtils.throwAsUncheckedException(RuntimeException("not this time")) + } } - } assertMessageEquals(exception, "not this time") } @Test fun assertTimeoutForSupplierThatThrowsAnAssertionFailedError() { - val exception = assertThrows { - assertTimeout(ofMillis(500)) { - fail("enigma") + val exception = + assertThrows { + assertTimeout(ofMillis(500)) { + fail("enigma") + } } - } assertMessageEquals(exception, "enigma") } @Test fun assertTimeoutForSupplierThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeout(ofMillis(10)) { - nap() + val error = + assertThrows { + assertTimeout(ofMillis(10)) { + nap() + } } - } assertMessageStartsWith(error, "execution exceeded timeout of 10 ms by") } @Test fun assertTimeoutWithMessageForSupplierThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeout(ofMillis(10), "Tempus Fugit") { - nap() + val error = + assertThrows { + assertTimeout(ofMillis(10), "Tempus Fugit") { + nap() + } } - } assertMessageStartsWith(error, "Tempus Fugit ==> execution exceeded timeout of 10 ms by") } @Test fun assertTimeoutWithMessageSupplierForSupplierThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeout(ofMillis(10), { "Tempus" + " " + "Fugit" }) { - nap() + val error = + assertThrows { + assertTimeout(ofMillis(10), { "Tempus" + " " + "Fugit" }) { + nap() + } } - } assertMessageStartsWith(error, "Tempus Fugit ==> execution exceeded timeout of 10 ms by") } @@ -158,41 +168,46 @@ internal class KotlinAssertTimeoutAssertionsTests { @Test fun assertTimeoutPreemptivelyForExecutableThatThrowsAnException() { - val exception = assertThrows { - assertTimeoutPreemptively(ofMillis(500)) { throw RuntimeException("not this time") } - } + val exception = + assertThrows { + assertTimeoutPreemptively(ofMillis(500)) { throw RuntimeException("not this time") } + } assertMessageEquals(exception, "not this time") } @Test fun assertTimeoutPreemptivelyForExecutableThatThrowsAnAssertionFailedError() { - val exception = assertThrows { - assertTimeoutPreemptively(ofMillis(500)) { fail("enigma") } - } + val exception = + assertThrows { + assertTimeoutPreemptively(ofMillis(500)) { fail("enigma") } + } assertMessageEquals(exception, "enigma") } @Test fun assertTimeoutPreemptivelyForExecutableThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeoutPreemptively(ofMillis(10)) { waitForInterrupt() } - } + val error = + assertThrows { + assertTimeoutPreemptively(ofMillis(10)) { waitForInterrupt() } + } assertMessageEquals(error, "execution timed out after 10 ms") } @Test fun assertTimeoutPreemptivelyWithMessageForExecutableThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeoutPreemptively(ofMillis(10), "Tempus Fugit") { waitForInterrupt() } - } + val error = + assertThrows { + assertTimeoutPreemptively(ofMillis(10), "Tempus Fugit") { waitForInterrupt() } + } assertMessageEquals(error, "Tempus Fugit ==> execution timed out after 10 ms") } @Test fun assertTimeoutPreemptivelyWithMessageSupplierForExecutableThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeoutPreemptively(ofMillis(10), { "Tempus" + " " + "Fugit" }) { waitForInterrupt() } - } + val error = + assertThrows { + assertTimeoutPreemptively(ofMillis(10), { "Tempus" + " " + "Fugit" }) { waitForInterrupt() } + } assertMessageEquals(error, "Tempus Fugit ==> execution timed out after 10 ms") } @@ -206,10 +221,11 @@ internal class KotlinAssertTimeoutAssertionsTests { @Test fun assertTimeoutPreemptivelyForSupplierThatCompletesBeforeTheTimeout() { changed.get().set(false) - val result = assertTimeoutPreemptively(ofMillis(500)) { - changed.get().set(true) - "Tempus Fugit" - } + val result = + assertTimeoutPreemptively(ofMillis(500)) { + changed.get().set(true) + "Tempus Fugit" + } assertFalse(changed.get().get(), "should have executed in a different thread") assertEquals("Tempus Fugit", result) assertEquals("Tempus Fugit", assertTimeoutPreemptively(ofMillis(500), "message") { "Tempus Fugit" }) @@ -218,51 +234,56 @@ internal class KotlinAssertTimeoutAssertionsTests { @Test fun assertTimeoutPreemptivelyForSupplierThatThrowsAnException() { - val exception = assertThrows { - assertTimeoutPreemptively(ofMillis(500)) { - ExceptionUtils.throwAsUncheckedException(RuntimeException("not this time")) + val exception = + assertThrows { + assertTimeoutPreemptively(ofMillis(500)) { + ExceptionUtils.throwAsUncheckedException(RuntimeException("not this time")) + } } - } assertMessageEquals(exception, "not this time") } @Test fun assertTimeoutPreemptivelyForSupplierThatThrowsAnAssertionFailedError() { - val exception = assertThrows { - assertTimeoutPreemptively(ofMillis(500)) { - fail("enigma") + val exception = + assertThrows { + assertTimeoutPreemptively(ofMillis(500)) { + fail("enigma") + } } - } assertMessageEquals(exception, "enigma") } @Test fun assertTimeoutPreemptivelyForSupplierThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeoutPreemptively(ofMillis(10)) { - waitForInterrupt() + val error = + assertThrows { + assertTimeoutPreemptively(ofMillis(10)) { + waitForInterrupt() + } } - } assertMessageEquals(error, "execution timed out after 10 ms") } @Test fun assertTimeoutPreemptivelyWithMessageForSupplierThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeoutPreemptively(ofMillis(10), "Tempus Fugit") { - waitForInterrupt() + val error = + assertThrows { + assertTimeoutPreemptively(ofMillis(10), "Tempus Fugit") { + waitForInterrupt() + } } - } assertMessageEquals(error, "Tempus Fugit ==> execution timed out after 10 ms") } @Test fun assertTimeoutPreemptivelyWithMessageSupplierForSupplierThatCompletesAfterTheTimeout() { - val error = assertThrows { - assertTimeoutPreemptively(ofMillis(10), { "Tempus" + " " + "Fugit" }) { - waitForInterrupt() + val error = + assertThrows { + assertTimeoutPreemptively(ofMillis(10), { "Tempus" + " " + "Fugit" }) { + waitForInterrupt() + } } - } assertMessageEquals(error, "Tempus Fugit ==> execution timed out after 10 ms") } @@ -270,11 +291,11 @@ internal class KotlinAssertTimeoutAssertionsTests { * Take a nap for 100 milliseconds. */ private fun nap() { - val start = System.currentTimeMillis() + val start = System.nanoTime() // workaround for imprecise clocks (yes, Windows, I'm talking about you) do { Thread.sleep(100) - } while (System.currentTimeMillis() - start < 100) + } while (System.nanoTime() - start < 100_000_000L) } private fun waitForInterrupt() { diff --git a/jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinAssertionsTests.kt b/jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinAssertionsTests.kt new file mode 100644 index 000000000000..79593e7b7a5a --- /dev/null +++ b/jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinAssertionsTests.kt @@ -0,0 +1,259 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ +package org.junit.jupiter.api + +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals +import org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith +import org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.DynamicContainer.dynamicContainer +import org.junit.jupiter.api.DynamicTest.dynamicTest +import org.opentest4j.AssertionFailedError +import org.opentest4j.MultipleFailuresError +import java.util.stream.Stream +import kotlin.reflect.KClass + +/** + * Unit tests for JUnit Jupiter [org.junit.jupiter.api] top-level assertion functions. + */ +class KotlinAssertionsTests { + // Bonus: no null check tests as these get handled by the compiler! + + @Test + fun `assertAll with functions that do not throw exceptions`() { + assertAll(Stream.of({ assertTrue(true) }, { assertFalse(false) })) + assertAll("heading", Stream.of({ assertTrue(true) }, { assertFalse(false) })) + assertAll(setOf({ assertTrue(true) }, { assertFalse(false) })) + assertAll("heading", setOf({ assertTrue(true) }, { assertFalse(false) })) + assertAll({ assertTrue(true) }, { assertFalse(false) }) + assertAll("heading", { assertTrue(true) }, { assertFalse(false) }) + } + + @Test + fun `assertAll with functions that throw AssertionErrors`() { + val multipleFailuresError = + assertThrows { + assertAll( + { assertFalse(true) }, + { assertFalse(true) } + ) + } + assertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError::class, AssertionFailedError::class) + } + + @Test + fun `assertThrows and fail`() { + assertThrows { fail("message") } + assertThrows { fail("message", AssertionError()) } + assertThrows { fail("message", null) } + assertThrows("should fail") { fail({ "message" }) } + assertThrows({ "should fail" }) { fail(AssertionError()) } + assertThrows({ "should fail" }) { fail(null as Throwable?) } + } + + @Test + fun `expected context exception testing`() = + runBlocking { + assertThrows("Should fail async") { + suspend { fail("Should fail async") }() + } + } + + @TestFactory + fun assertDoesNotThrow(): Stream = + Stream.of( + dynamicContainer( + "succeeds when no exception thrown", + Stream.of( + dynamicTest("for no arguments variant") { + val actual = assertDoesNotThrow { 1 } + assertEquals(1, actual) + }, + dynamicTest("for no arguments variant (suspended)") { + runBlocking { + val actual = assertDoesNotThrow { suspend { 1 }() } + assertEquals(1, actual) + } + }, + dynamicTest("for message variant") { + val actual = assertDoesNotThrow("message") { 2 } + assertEquals(2, actual) + }, + dynamicTest("for message variant (suspended)") { + runBlocking { + val actual = assertDoesNotThrow("message") { suspend { 2 }() } + assertEquals(2, actual) + } + }, + dynamicTest("for message supplier variant") { + val actual = assertDoesNotThrow({ "message" }) { 3 } + assertEquals(3, actual) + }, + dynamicTest("for message supplier variant (suspended)") { + runBlocking { + val actual = assertDoesNotThrow({ "message" }) { suspend { 3 }() } + assertEquals(3, actual) + } + } + ) + ), + dynamicContainer( + "fails when an exception is thrown", + Stream.of( + dynamicTest("for no arguments variant") { + val exception = + assertThrows { + assertDoesNotThrow { + fail("fail") + } + } + assertMessageEquals( + exception, + "Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" + ) + }, + dynamicTest("for no arguments variant (suspended)") { + runBlocking { + val exception = + assertThrows { + assertDoesNotThrow { + suspend { fail("fail") }() + } + } + assertMessageEquals( + exception, + "Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" + ) + } + }, + dynamicTest("for message variant") { + val exception = + assertThrows { + assertDoesNotThrow("Does not throw") { + fail("fail") + } + } + assertMessageEquals( + exception, + "Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" + ) + }, + dynamicTest("for message variant (suspended)") { + runBlocking { + val exception = + assertThrows { + assertDoesNotThrow("Does not throw") { + suspend { fail("fail") }() + } + } + assertMessageEquals( + exception, + "Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" + ) + } + }, + dynamicTest("for message supplier variant") { + val exception = + assertThrows { + assertDoesNotThrow({ "Does not throw" }) { + fail("fail") + } + } + assertMessageEquals( + exception, + "Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" + ) + }, + dynamicTest("for message supplier variant (suspended)") { + runBlocking { + val exception = + assertThrows { + assertDoesNotThrow({ "Does not throw" }) { + suspend { fail("fail") }() + } + } + assertMessageEquals( + exception, + "Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail" + ) + } + } + ) + ) + ) + + @Test + fun `assertAll with stream of functions that throw AssertionErrors`() { + val multipleFailuresError = + assertThrows("Should have thrown multiple errors") { + assertAll(Stream.of({ assertFalse(true) }, { assertFalse(true) })) + } + assertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError::class, AssertionFailedError::class) + } + + @Test + fun `assertAll with collection of functions that throw AssertionErrors`() { + val multipleFailuresError = + assertThrows("Should have thrown multiple errors") { + assertAll(setOf({ assertFalse(true) }, { assertFalse(true) })) + } + assertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError::class, AssertionFailedError::class) + } + + @Test + fun `assertThrows with function that does not throw an exception`() { + val assertionMessage = "This will not throw an exception" + val error = + assertThrows("assertThrows did not throw the correct exception") { + assertThrows(assertionMessage) { } + // This should never execute: + expectAssertionFailedError() + } + assertMessageStartsWith(error, assertionMessage) + } + + @Test + fun assertInstanceOf() { + assertInstanceOf(listOf("whatever")) + assertInstanceOf(listOf("whatever"), "No random access") + assertInstanceOf(listOf("whatever")) { "No random access" } + } + + @Test + fun `assertInstanceOf fails wrong type value`() { + val result = + assertThrows { + assertInstanceOf(StringBuilder(), "Should be a String") + } + assertMessageStartsWith(result, "Should be a String") + } + + @Test + fun `assertInstanceOf fails null value`() { + val result = + assertThrows { + assertInstanceOf(null, "Should be a String") + } + assertMessageStartsWith(result, "Should be a String") + } + + companion object { + fun assertExpectedExceptionTypes( + multipleFailuresError: MultipleFailuresError, + vararg exceptionTypes: KClass + ) = AssertAllAssertionsTests.assertExpectedExceptionTypes( + multipleFailuresError, + *exceptionTypes.map { it.java }.toTypedArray() + ) + } +} diff --git a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/api/KotlinFailAssertionsTests.kt b/jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinFailAssertionsTests.kt similarity index 55% rename from junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/api/KotlinFailAssertionsTests.kt rename to jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinFailAssertionsTests.kt index 00023d0a5723..2b1311232187 100644 --- a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/api/KotlinFailAssertionsTests.kt +++ b/jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinFailAssertionsTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -17,38 +17,41 @@ import org.opentest4j.AssertionFailedError import java.util.stream.Stream class KotlinFailAssertionsTests { - @Test fun `fail with string`() { val message = "test" - val ex = assertThrows { - fail(message) - } + val ex = + assertThrows { + fail(message) + } assertMessageEquals(ex, message) } @Test fun `fail with message supplier`() { val message = "test" - val ex = assertThrows { - fail { message } - } + val ex = + assertThrows { + fail { message } + } assertMessageEquals(ex, message) } @Test fun `fail with null string`() { - val ex = assertThrows { - fail(null as String?) - } + val ex = + assertThrows { + fail(null as String?) + } assertEmptyMessage(ex) } @Test fun `fail with null message supplier`() { - val ex = assertThrows { - fail(null as (() -> String)?) - } + val ex = + assertThrows { + fail(null as (() -> String)?) + } assertEmptyMessage(ex) } @@ -56,9 +59,10 @@ class KotlinFailAssertionsTests { fun `fail with string and throwable`() { val message = "message" val throwableCause = "cause" - val ex = assertThrows { - fail(message, Throwable(throwableCause)) - } + val ex = + assertThrows { + fail(message, Throwable(throwableCause)) + } assertMessageEquals(ex, message) val cause = ex.cause assertMessageContains(cause, throwableCause) @@ -67,9 +71,10 @@ class KotlinFailAssertionsTests { @Test fun `fail with throwable`() { val throwableCause = "cause" - val ex = assertThrows { - fail(Throwable(throwableCause)) - } + val ex = + assertThrows { + fail(Throwable(throwableCause)) + } assertEmptyMessage(ex) val cause = ex.cause assertMessageContains(cause, throwableCause) @@ -78,9 +83,10 @@ class KotlinFailAssertionsTests { @Test fun `fail with string and null throwable`() { val message = "message" - val ex = assertThrows { - fail(message, null) - } + val ex = + assertThrows { + fail(message, null) + } assertMessageEquals(ex, message) if (ex.cause != null) { throw AssertionError("Cause should have been null") @@ -90,9 +96,10 @@ class KotlinFailAssertionsTests { @Test fun `fail with null string and throwable`() { val throwableCause = "cause" - val ex = assertThrows { - fail(null, Throwable(throwableCause)) - } + val ex = + assertThrows { + fail(null, Throwable(throwableCause)) + } assertEmptyMessage(ex) val cause = ex.cause assertMessageContains(cause, throwableCause) @@ -100,23 +107,26 @@ class KotlinFailAssertionsTests { @Test fun `fail usable as a stream expression`() { - val count = Stream.empty() - .peek { _ -> fail("peek should never be called") } - .filter { _ -> fail("filter should never be called", Throwable("cause")) } - .map { _ -> fail(Throwable("map should never be called")) } - .sorted { _, _ -> fail { "sorted should never be called" } } - .count() + val count = + Stream + .empty() + .peek { _ -> fail("peek should never be called") } + .filter { _ -> fail("filter should never be called", Throwable("cause")) } + .map { _ -> fail(Throwable("map should never be called")) } + .sorted { _, _ -> fail { "sorted should never be called" } } + .count() assertEquals(0L, count) } @Test fun `fail usable as a sequence expression`() { - val count = emptyList() - .asSequence() - .onEach { _ -> fail("peek should never be called") } - .filter { _ -> fail("filter should never be called", Throwable("cause")) } - .map { _ -> fail(Throwable("map should never be called")) } - .count() + val count = + emptyList() + .asSequence() + .onEach { _ -> fail("peek should never be called") } + .filter { _ -> fail("filter should never be called", Throwable("cause")) } + .map { _ -> fail(Throwable("map should never be called")) } + .count() assertEquals(0, count) } } diff --git a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/engine/kotlin/ArbitraryNamingKotlinTestCase.kt b/jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/ArbitraryNamingKotlinTestCase.kt similarity index 93% rename from junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/engine/kotlin/ArbitraryNamingKotlinTestCase.kt rename to jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/ArbitraryNamingKotlinTestCase.kt index 1b919a9f2ae0..2a3737f56f0b 100644 --- a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/engine/kotlin/ArbitraryNamingKotlinTestCase.kt +++ b/jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/ArbitraryNamingKotlinTestCase.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerClassKotlinTestCase.kt b/jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerClassKotlinTestCase.kt similarity index 90% rename from junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerClassKotlinTestCase.kt rename to jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerClassKotlinTestCase.kt index f5c4e5b88dbd..ef87340692f6 100644 --- a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerClassKotlinTestCase.kt +++ b/jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerClassKotlinTestCase.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -19,7 +19,6 @@ import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS @TestInstance(PER_CLASS) class InstancePerClassKotlinTestCase { - companion object { @JvmField val TEST_INSTANCES: MutableMap> = HashMap() @@ -56,7 +55,8 @@ class InstancePerClassKotlinTestCase { } private fun increment(name: String) { - TEST_INSTANCES.computeIfAbsent(this, { _ -> HashMap() }) + TEST_INSTANCES + .computeIfAbsent(this, { _ -> HashMap() }) .compute(name, { _, oldValue -> (oldValue ?: 0) + 1 }) } } diff --git a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerMethodKotlinTestCase.kt b/jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerMethodKotlinTestCase.kt similarity index 83% rename from junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerMethodKotlinTestCase.kt rename to jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerMethodKotlinTestCase.kt index b5845f9ae0fb..463a39ad9473 100644 --- a/junit-jupiter-engine/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerMethodKotlinTestCase.kt +++ b/jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerMethodKotlinTestCase.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -16,7 +16,6 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class InstancePerMethodKotlinTestCase { - companion object { @JvmField val TEST_INSTANCES: MutableMap> = LinkedHashMap() @@ -33,8 +32,12 @@ class InstancePerMethodKotlinTestCase { increment(this, "afterAll") } - private fun increment(instance: Any, name: String) { - TEST_INSTANCES.computeIfAbsent(instance, { _ -> LinkedHashMap() }) + private fun increment( + instance: Any, + name: String + ) { + TEST_INSTANCES + .computeIfAbsent(instance, { _ -> LinkedHashMap() }) .compute(name, { _, oldValue -> (oldValue ?: 0) + 1 }) } } diff --git a/junit-jupiter-params/src/test/kotlin/ParameterizedTestNameFormatterIntegrationTests.kt b/jupiter-tests/src/test/kotlin/org/junit/jupiter/params/ParameterizedTestNameFormatterIntegrationTests.kt similarity index 76% rename from junit-jupiter-params/src/test/kotlin/ParameterizedTestNameFormatterIntegrationTests.kt rename to jupiter-tests/src/test/kotlin/org/junit/jupiter/params/ParameterizedTestNameFormatterIntegrationTests.kt index 829f98aefabc..a5dc306c1bb2 100644 --- a/junit-jupiter-params/src/test/kotlin/ParameterizedTestNameFormatterIntegrationTests.kt +++ b/jupiter-tests/src/test/kotlin/org/junit/jupiter/params/ParameterizedTestNameFormatterIntegrationTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -7,16 +7,19 @@ * * https://www.eclipse.org/legal/epl-v20.html */ +package org.junit.jupiter.params + import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.TestInfo -import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource class ParameterizedTestNameFormatterIntegrationTests { - @ValueSource(strings = ["foo", "bar"]) @ParameterizedTest - fun defaultDisplayName(param: String, info: TestInfo) { + fun defaultDisplayName( + param: String, + info: TestInfo + ) { if (param.equals("foo")) { assertEquals("[1] foo", info.displayName) } else { @@ -26,7 +29,10 @@ class ParameterizedTestNameFormatterIntegrationTests { @ValueSource(strings = ["foo", "bar"]) @ParameterizedTest(name = "{0}") - fun `1st argument`(param: String, info: TestInfo) { + fun `1st argument`( + param: String, + info: TestInfo + ) { if (param.equals("foo")) { assertEquals("foo", info.displayName) } else { @@ -36,13 +42,19 @@ class ParameterizedTestNameFormatterIntegrationTests { @ValueSource(strings = ["foo", "bar"]) @ParameterizedTest(name = "{displayName}") - fun `it's an {enigma} '{0}'`(@Suppress("UNUSED_PARAMETER") param: String, info: TestInfo) { + fun `it's an {enigma} '{0}'`( + @Suppress("UNUSED_PARAMETER") param: String, + info: TestInfo + ) { assertEquals("it's an {enigma} '{0}'(String, TestInfo)", info.displayName) } @ValueSource(strings = ["foo", "bar"]) @ParameterizedTest(name = "{displayName} - {0}") - fun `displayName and 1st 'argument'`(param: String, info: TestInfo) { + fun `displayName and 1st 'argument'`( + param: String, + info: TestInfo + ) { if (param.equals("foo")) { assertEquals("displayName and 1st 'argument'(String, TestInfo) - foo", info.displayName) } else { @@ -52,7 +64,10 @@ class ParameterizedTestNameFormatterIntegrationTests { @ValueSource(strings = ["foo", "bar"]) @ParameterizedTest(name = "{0} - {displayName}") - fun `1st 'argument' and displayName`(param: String, info: TestInfo) { + fun `1st 'argument' and displayName`( + param: String, + info: TestInfo + ) { if (param.equals("foo")) { assertEquals("foo - 1st 'argument' and displayName(String, TestInfo)", info.displayName) } else { diff --git a/junit-jupiter-params/src/test/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessorKotlinTests.kt b/jupiter-tests/src/test/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessorKotlinTests.kt similarity index 79% rename from junit-jupiter-params/src/test/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessorKotlinTests.kt rename to jupiter-tests/src/test/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessorKotlinTests.kt index 3c50a0d173fd..c3b97696cc5c 100644 --- a/junit-jupiter-params/src/test/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessorKotlinTests.kt +++ b/jupiter-tests/src/test/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessorKotlinTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -23,7 +23,6 @@ import java.lang.reflect.Method * Unit tests for using [ArgumentsAccessor] from Kotlin. */ class ArgumentsAccessorKotlinTests { - @Test fun `get() with reified type and index`() { assertEquals(1, defaultArgumentsAccessor(1, 1).get(0)) @@ -32,9 +31,10 @@ class ArgumentsAccessorKotlinTests { @Test fun `get() with reified type and index for incompatible type`() { - val exception = assertThrows { - defaultArgumentsAccessor(1, Integer.valueOf(1)).get(0) - } + val exception = + assertThrows { + defaultArgumentsAccessor(1, Integer.valueOf(1)).get(0) + } assertThat(exception).hasMessage( "Argument at index [0] with value [1] and type [java.lang.Integer] could not be converted or cast to type [java.lang.Character]." @@ -53,14 +53,15 @@ class ArgumentsAccessorKotlinTests { assertEquals('A', defaultArgumentsAccessor(1, 'A').get(0, Character::class.java)) } - fun defaultArgumentsAccessor(invocationIndex: Int, vararg arguments: Any): DefaultArgumentsAccessor { - return DefaultArgumentsAccessor(parameterContext(), invocationIndex, *arguments) - } + fun defaultArgumentsAccessor( + invocationIndex: Int, + vararg arguments: Any + ): DefaultArgumentsAccessor = DefaultArgumentsAccessor(parameterContext(), invocationIndex, *arguments) fun parameterContext(): ParameterContext { val declaringExecutable: Method = ReflectionUtils.findMethod(DefaultArgumentsAccessorTests::class.java, "foo").get() val parameterContext: ParameterContext = mock() - `when`(parameterContext.getDeclaringExecutable()).thenReturn(declaringExecutable) + `when`(parameterContext.declaringExecutable).thenReturn(declaringExecutable) return parameterContext } diff --git a/junit-jupiter-params/src/test/kotlin/org/junit/jupiter/params/aggregator/DisplayNameTests.kt b/jupiter-tests/src/test/kotlin/org/junit/jupiter/params/aggregator/DisplayNameTests.kt similarity index 66% rename from junit-jupiter-params/src/test/kotlin/org/junit/jupiter/params/aggregator/DisplayNameTests.kt rename to jupiter-tests/src/test/kotlin/org/junit/jupiter/params/aggregator/DisplayNameTests.kt index 96b717ad5044..d12ac782b0f6 100644 --- a/junit-jupiter-params/src/test/kotlin/org/junit/jupiter/params/aggregator/DisplayNameTests.kt +++ b/jupiter-tests/src/test/kotlin/org/junit/jupiter/params/aggregator/DisplayNameTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -19,17 +19,22 @@ import org.junit.jupiter.params.provider.MethodSource // https://github.com/junit-team/junit5/issues/1836 object DisplayNameTests { @JvmStatic - fun data() = arrayOf( - arrayOf("A", 1), - arrayOf("B", 2), - arrayOf("C", 3), - arrayOf("", 4), // empty is okay - arrayOf(null, 5) // null was the problem - ) + fun data() = + arrayOf( + arrayOf("A", 1), + arrayOf("B", 2), + arrayOf("C", 3), + arrayOf("", 4), // empty is okay + arrayOf(null, 5) // null was the problem + ) @ParameterizedTest @MethodSource("data") - fun test(char: String?, number: Int, info: TestInfo) { + fun test( + char: String?, + number: Int, + info: TestInfo + ) { assertEquals("[$number] $char, $number", info.displayName) } } diff --git a/junit-jupiter-engine/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/jupiter-tests/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension similarity index 100% rename from junit-jupiter-engine/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension rename to jupiter-tests/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension diff --git a/junit-jupiter-engine/src/test/resources/jupiter-testjar.jar b/jupiter-tests/src/test/resources/jupiter-testjar.jar similarity index 100% rename from junit-jupiter-engine/src/test/resources/jupiter-testjar.jar rename to jupiter-tests/src/test/resources/jupiter-testjar.jar diff --git a/junit-jupiter-engine/src/test/resources/log4j2-test.xml b/jupiter-tests/src/test/resources/log4j2-test.xml similarity index 100% rename from junit-jupiter-engine/src/test/resources/log4j2-test.xml rename to jupiter-tests/src/test/resources/log4j2-test.xml diff --git a/junit-jupiter-params/src/test/resources/broken.csv b/jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/broken.csv similarity index 100% rename from junit-jupiter-params/src/test/resources/broken.csv rename to jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/broken.csv diff --git a/junit-jupiter-params/src/test/resources/default-max-chars.csv b/jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/default-max-chars.csv similarity index 100% rename from junit-jupiter-params/src/test/resources/default-max-chars.csv rename to jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/default-max-chars.csv diff --git a/junit-jupiter-params/src/test/resources/exceeds-default-max-chars.csv b/jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/exceeds-default-max-chars.csv similarity index 100% rename from junit-jupiter-params/src/test/resources/exceeds-default-max-chars.csv rename to jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/exceeds-default-max-chars.csv diff --git a/junit-jupiter-params/src/test/resources/leading-trailing-spaces.csv b/jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/leading-trailing-spaces.csv similarity index 100% rename from junit-jupiter-params/src/test/resources/leading-trailing-spaces.csv rename to jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/leading-trailing-spaces.csv diff --git a/junit-jupiter-params/src/test/resources/single-column.csv b/jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/single-column.csv similarity index 100% rename from junit-jupiter-params/src/test/resources/single-column.csv rename to jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/single-column.csv diff --git a/junit-jupiter-params/src/test/resources/org/junit/jupiter/params/two-column-with-headers.csv b/jupiter-tests/src/test/resources/org/junit/jupiter/params/two-column-with-headers.csv similarity index 100% rename from junit-jupiter-params/src/test/resources/org/junit/jupiter/params/two-column-with-headers.csv rename to jupiter-tests/src/test/resources/org/junit/jupiter/params/two-column-with-headers.csv diff --git a/junit-jupiter-params/src/test/resources/org/junit/jupiter/params/two-column.csv b/jupiter-tests/src/test/resources/org/junit/jupiter/params/two-column.csv similarity index 100% rename from junit-jupiter-params/src/test/resources/org/junit/jupiter/params/two-column.csv rename to jupiter-tests/src/test/resources/org/junit/jupiter/params/two-column.csv diff --git a/platform-tests/platform-tests.gradle.kts b/platform-tests/platform-tests.gradle.kts index f32a2852349d..183203351e94 100644 --- a/platform-tests/platform-tests.gradle.kts +++ b/platform-tests/platform-tests.gradle.kts @@ -6,7 +6,7 @@ plugins { id("junitbuild.java-library-conventions") id("junitbuild.junit4-compatibility") id("junitbuild.testing-conventions") - alias(libs.plugins.jmh) + id("junitbuild.jmh-conventions") } dependencies { @@ -26,6 +26,7 @@ dependencies { testImplementation(testFixtures(projects.junitPlatformEngine)) testImplementation(testFixtures(projects.junitPlatformLauncher)) testImplementation(projects.junitJupiterEngine) + testImplementation(testFixtures(projects.junitJupiterEngine)) testImplementation(libs.apiguardian) testImplementation(libs.jfrunit) { exclude(group = "org.junit.vintage") @@ -43,15 +44,11 @@ dependencies { } // --- https://openjdk.java.net/projects/code-tools/jmh/ ----------------------- - jmh(libs.jmh.core) jmh(projects.junitJupiterApi) jmh(libs.junit4) - jmhAnnotationProcessor(libs.jmh.generator.annprocess) } jmh { - jmhVersion = libs.versions.jmh - duplicateClassesStrategy = DuplicatesStrategy.WARN fork = 1 warmupIterations = 1 @@ -64,10 +61,12 @@ tasks { excludeTags("exclude") } jvmArgs("-Xmx1g") - distribution { - // Retry in a new JVM on Windows to improve chances of successful retries when - // cached resources are used (e.g. in ClasspathScannerTests) - retryInSameJvm = !OperatingSystem.current().isWindows + develocity { + testDistribution { + // Retry in a new JVM on Windows to improve chances of successful retries when + // cached resources are used (e.g. in ClasspathScannerTests) + retryInSameJvm = !OperatingSystem.current().isWindows + } } } test { @@ -80,19 +79,16 @@ tasks { includeTags("junit4") } } - checkstyleJmh { // use same style rules as defined for tests - config = resources.text.fromFile(checkstyle.configDirectory.file("checkstyleTest.xml")) - } } eclipse { classpath { - plusConfigurations.add(projects.junitPlatformConsole.dependencyProject.configurations["shadowed"]) + plusConfigurations.add(projects.junitPlatformConsole.dependencyProject.configurations["shadowedClasspath"]) } } idea { module { - scopes["PROVIDED"]!!["plus"]!!.add(projects.junitPlatformConsole.dependencyProject.configurations["shadowed"]) + scopes["PROVIDED"]!!["plus"]!!.add(projects.junitPlatformConsole.dependencyProject.configurations["shadowedClasspath"]) } } diff --git a/platform-tests/src/jmh/java/org/junit/jupiter/jmh/AssertionBenchmarks.java b/platform-tests/src/jmh/java/org/junit/jupiter/jmh/AssertionBenchmarks.java index 97136ecdb9a4..6b0940f6de80 100644 --- a/platform-tests/src/jmh/java/org/junit/jupiter/jmh/AssertionBenchmarks.java +++ b/platform-tests/src/jmh/java/org/junit/jupiter/jmh/AssertionBenchmarks.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/DefaultPackageTestCase.java b/platform-tests/src/test/java/DefaultPackageTestCase.java index 8018c935f497..5acabf2e975d 100644 --- a/platform-tests/src/test/java/DefaultPackageTestCase.java +++ b/platform-tests/src/test/java/DefaultPackageTestCase.java @@ -1,6 +1,6 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/jupiter/api/condition/OSTests.java b/platform-tests/src/test/java/org/junit/jupiter/api/condition/OSTests.java index 714869b603d7..971e6ab9e035 100644 --- a/platform-tests/src/test/java/org/junit/jupiter/api/condition/OSTests.java +++ b/platform-tests/src/test/java/org/junit/jupiter/api/condition/OSTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/jupiter/extensions/Heavyweight.java b/platform-tests/src/test/java/org/junit/jupiter/extensions/Heavyweight.java index dd2df97c8510..208b0a5cac27 100644 --- a/platform-tests/src/test/java/org/junit/jupiter/extensions/Heavyweight.java +++ b/platform-tests/src/test/java/org/junit/jupiter/extensions/Heavyweight.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/jupiter/extensions/HeavyweightTests.java b/platform-tests/src/test/java/org/junit/jupiter/extensions/HeavyweightTests.java index 2d316392c079..5f0aaf1911be 100644 --- a/platform-tests/src/test/java/org/junit/jupiter/extensions/HeavyweightTests.java +++ b/platform-tests/src/test/java/org/junit/jupiter/extensions/HeavyweightTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/AbstractEqualsAndHashCodeTests.java b/platform-tests/src/test/java/org/junit/platform/AbstractEqualsAndHashCodeTests.java index 688bb079e780..d5693baaea2f 100644 --- a/platform-tests/src/test/java/org/junit/platform/AbstractEqualsAndHashCodeTests.java +++ b/platform-tests/src/test/java/org/junit/platform/AbstractEqualsAndHashCodeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/JUnitPlatformTestSuite.java b/platform-tests/src/test/java/org/junit/platform/JUnitPlatformTestSuite.java index 48633071c35a..456fcf9a5117 100644 --- a/platform-tests/src/test/java/org/junit/platform/JUnitPlatformTestSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/JUnitPlatformTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java b/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java index d8a612bef2bc..1babd73c2766 100644 --- a/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java +++ b/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/TestEngineTests.java b/platform-tests/src/test/java/org/junit/platform/TestEngineTests.java index fc3e8f13df41..a5ad5f3ccbdb 100644 --- a/platform-tests/src/test/java/org/junit/platform/TestEngineTests.java +++ b/platform-tests/src/test/java/org/junit/platform/TestEngineTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/annotation/TestableAnnotationTests.java b/platform-tests/src/test/java/org/junit/platform/commons/annotation/TestableAnnotationTests.java index 611a30bf6cff..894550f22604 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/annotation/TestableAnnotationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/annotation/TestableAnnotationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/function/TryTests.java b/platform-tests/src/test/java/org/junit/platform/commons/function/TryTests.java index 52d466ffe15a..54716dc3fad5 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/function/TryTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/function/TryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/support/AnnotationSupportTests.java b/platform-tests/src/test/java/org/junit/platform/commons/support/AnnotationSupportTests.java index 8675d0384d17..33d7d0caeaa1 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/support/AnnotationSupportTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/AnnotationSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/support/ClassSupportTests.java b/platform-tests/src/test/java/org/junit/platform/commons/support/ClassSupportTests.java index 05352fa97e15..ebde2ce33e10 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/support/ClassSupportTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/ClassSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/support/ModifierSupportTests.java b/platform-tests/src/test/java/org/junit/platform/commons/support/ModifierSupportTests.java index 9bc39a1a9f09..81fc84a10325 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/support/ModifierSupportTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/ModifierSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/support/PreconditionAssertions.java b/platform-tests/src/test/java/org/junit/platform/commons/support/PreconditionAssertions.java index bd7a3fe17638..3a8060479a10 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/support/PreconditionAssertions.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/PreconditionAssertions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java b/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java index 0006a795fa07..adb986154b79 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -19,6 +19,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.net.URI; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -36,6 +37,7 @@ class ReflectionSupportTests { private static final Predicate> allTypes = type -> true; + private static final Predicate allResources = type -> true; private static final Predicate allNames = name -> true; private static final Predicate allMethods = name -> true; private static final Predicate allFields = name -> true; @@ -94,7 +96,7 @@ void tryToLoadClassWithExplicitClassLoaderDelegates() { */ @Test void tryToLoadClassWithExplicitClassLoaderPreconditions() { - ClassLoader cl = getClass().getClassLoader(); + var cl = getClass().getClassLoader(); assertPreconditionViolationExceptionForString("Class name", () -> ReflectionSupport.tryToLoadClass(null, cl)); assertPreconditionViolationExceptionForString("Class name", () -> ReflectionSupport.tryToLoadClass("", cl)); @@ -110,11 +112,7 @@ List findAllClassesInClasspathRootDelegates() throws Throwable { paths.addAll(ReflectionUtils.getAllClasspathRootDirectories()); for (var path : paths) { var root = path.toUri(); - var displayName = root.getPath(); - if (displayName.length() > 42) { - displayName = "..." + displayName.substring(displayName.length() - 42); - } - tests.add(DynamicTest.dynamicTest(displayName, + tests.add(DynamicTest.dynamicTest(createDisplayName(root), () -> assertEquals(ReflectionUtils.findAllClassesInClasspathRoot(root, allTypes, allNames), ReflectionSupport.findAllClassesInClasspathRoot(root, allTypes, allNames)))); } @@ -132,6 +130,66 @@ void findAllClassesInClasspathRootPreconditions() { () -> ReflectionSupport.findAllClassesInClasspathRoot(path, allTypes, null)); } + /** + * @since 1.11 + */ + @TestFactory + List findAllResourcesInClasspathRootDelegates() throws Throwable { + List tests = new ArrayList<>(); + List paths = new ArrayList<>(); + paths.add(Path.of(".").toRealPath()); + paths.addAll(ReflectionUtils.getAllClasspathRootDirectories()); + for (var path : paths) { + var root = path.toUri(); + tests.add(DynamicTest.dynamicTest(createDisplayName(root), + () -> assertEquals(ReflectionUtils.findAllResourcesInClasspathRoot(root, allResources), + ReflectionSupport.findAllResourcesInClasspathRoot(root, allResources)))); + } + return tests; + } + + /** + * @since 1.11 + */ + @Test + void findAllResourcesInClasspathRootPreconditions() { + var path = Path.of(".").toUri(); + assertPreconditionViolationException("root", + () -> ReflectionSupport.findAllResourcesInClasspathRoot(null, allResources)); + assertPreconditionViolationException("resourceFilter", + () -> ReflectionSupport.findAllResourcesInClasspathRoot(path, null)); + } + + /** + * @since 1.11 + */ + @TestFactory + List streamAllResourcesInClasspathRootDelegates() throws Throwable { + List tests = new ArrayList<>(); + List paths = new ArrayList<>(); + paths.add(Path.of(".").toRealPath()); + paths.addAll(ReflectionUtils.getAllClasspathRootDirectories()); + for (var path : paths) { + var root = path.toUri(); + tests.add(DynamicTest.dynamicTest(createDisplayName(root), + () -> assertEquals(ReflectionUtils.streamAllResourcesInClasspathRoot(root, allResources).toList(), + ReflectionSupport.streamAllResourcesInClasspathRoot(root, allResources).toList()))); + } + return tests; + } + + /** + * @since 1.11 + */ + @Test + void streamAllResourcesInClasspathRootPreconditions() { + var path = Path.of(".").toUri(); + assertPreconditionViolationException("root", + () -> ReflectionSupport.streamAllResourcesInClasspathRoot(null, allResources)); + assertPreconditionViolationException("resourceFilter", + () -> ReflectionSupport.streamAllResourcesInClasspathRoot(path, null)); + } + @Test void findAllClassesInPackageDelegates() { assertNotEquals(0, ReflectionSupport.findAllClassesInPackage("org.junit", allTypes, allNames).size()); @@ -149,6 +207,50 @@ void findAllClassesInPackagePreconditions() { () -> ReflectionSupport.findAllClassesInPackage("org.junit", allTypes, null)); } + /** + * @since 1.11 + */ + @Test + void findAllResourcesInPackageDelegates() { + assertNotEquals(0, ReflectionSupport.findAllResourcesInPackage("org.junit", allResources).size()); + + assertEquals(ReflectionUtils.findAllResourcesInPackage("org.junit", allResources), + ReflectionSupport.findAllResourcesInPackage("org.junit", allResources)); + } + + /** + * @since 1.11 + */ + @Test + void findAllResourcesInPackagePreconditions() { + assertPreconditionViolationExceptionForString("basePackageName", + () -> ReflectionSupport.findAllResourcesInPackage(null, allResources)); + assertPreconditionViolationException("resourceFilter", + () -> ReflectionSupport.findAllResourcesInPackage("org.junit", null)); + } + + /** + * @since 1.11 + */ + @Test + void streamAllResourcesInPackageDelegates() { + assertNotEquals(0, ReflectionSupport.streamAllResourcesInPackage("org.junit", allResources).count()); + + assertEquals(ReflectionUtils.streamAllResourcesInPackage("org.junit", allResources).toList(), + ReflectionSupport.streamAllResourcesInPackage("org.junit", allResources).toList()); + } + + /** + * @since 1.11 + */ + @Test + void streamAllResourcesInPackagePreconditions() { + assertPreconditionViolationExceptionForString("basePackageName", + () -> ReflectionSupport.streamAllResourcesInPackage(null, allResources)); + assertPreconditionViolationException("resourceFilter", + () -> ReflectionSupport.streamAllResourcesInPackage("org.junit", null)); + } + @Test void findAllClassesInModuleDelegates() { assertEquals(ReflectionUtils.findAllClassesInModule("org.junit.platform.commons", allTypes, allNames), @@ -166,6 +268,48 @@ void findAllClassesInModulePreconditions() { () -> ReflectionSupport.findAllClassesInModule("org.junit.platform.commons", allTypes, null)); } + /** + * @since 1.11 + */ + @Test + void findAllResourcesInModuleDelegates() { + assertEquals(ReflectionUtils.findAllResourcesInModule("org.junit.platform.commons", allResources), + ReflectionSupport.findAllResourcesInModule("org.junit.platform.commons", allResources)); + } + + /** + * @since 1.11 + */ + @Test + void findAllResourcesInModulePreconditions() { + var exception = assertThrows(PreconditionViolationException.class, + () -> ReflectionSupport.findAllResourcesInModule(null, allResources)); + assertEquals("Module name must not be null or empty", exception.getMessage()); + assertPreconditionViolationException("Resource filter", + () -> ReflectionSupport.findAllResourcesInModule("org.junit.platform.commons", null)); + } + + /** + * @since 1.11 + */ + @Test + void streamAllResourcesInModuleDelegates() { + assertEquals(ReflectionUtils.streamAllResourcesInModule("org.junit.platform.commons", allResources).toList(), + ReflectionSupport.streamAllResourcesInModule("org.junit.platform.commons", allResources).toList()); + } + + /** + * @since 1.11 + */ + @Test + void streamAllResourcesInModulePreconditions() { + var exception = assertThrows(PreconditionViolationException.class, + () -> ReflectionSupport.streamAllResourcesInModule(null, allResources)); + assertEquals("Module name must not be null or empty", exception.getMessage()); + assertPreconditionViolationException("Resource filter", + () -> ReflectionSupport.streamAllResourcesInModule("org.junit.platform.commons", null)); + } + @Test void newInstanceDelegates() { assertEquals(ReflectionUtils.newInstance(String.class, "foo"), @@ -318,11 +462,21 @@ void findNestedClassesPreconditions() { () -> ReflectionSupport.findNestedClasses(ClassWithNestedClasses.class, null)); } + private static String createDisplayName(URI root) { + var displayName = root.getPath(); + if (displayName.length() > 42) { + displayName = "..." + displayName.substring(displayName.length() - 42); + } + return displayName; + } + static class ClassWithNestedClasses { + @SuppressWarnings({ "InnerClassMayBeStatic", "unused" }) class Nested1 { } + @SuppressWarnings("unused") static class Nested2 { } diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/FallbackStringToObjectConverterTests.java b/platform-tests/src/test/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverterTests.java similarity index 89% rename from junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/FallbackStringToObjectConverterTests.java rename to platform-tests/src/test/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverterTests.java index 1d2cd21a6fdf..9994bba3858c 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/FallbackStringToObjectConverterTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.jupiter.params.converter; +package org.junit.platform.commons.support.conversion; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.platform.commons.util.ReflectionUtils.findMethod; @@ -19,15 +19,15 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.converter.FallbackStringToObjectConverter.IsFactoryConstructor; -import org.junit.jupiter.params.converter.FallbackStringToObjectConverter.IsFactoryMethod; +import org.junit.platform.commons.support.conversion.FallbackStringToObjectConverter.IsFactoryConstructor; +import org.junit.platform.commons.support.conversion.FallbackStringToObjectConverter.IsFactoryMethod; import org.junit.platform.commons.util.ReflectionUtils; /** * Unit tests for {@link FallbackStringToObjectConverter}, {@link IsFactoryMethod}, * and {@link IsFactoryConstructor}. * - * @since 5.1 + * @since 1.11 (originally since JUnit Jupiter 5.1) */ class FallbackStringToObjectConverterTests { @@ -87,13 +87,13 @@ void convertsStringToNewspaperViaConstructorIgnoringMultipleFactoryMethods() thr @Test @DisplayName("Cannot convert String to Diary because Diary has neither a static factory method nor a factory constructor") void cannotConvertStringToDiary() { - assertThat(converter.canConvert(Diary.class)).isFalse(); + assertThat(converter.canConvertTo(Diary.class)).isFalse(); } @Test @DisplayName("Cannot convert String to Magazine because Magazine has multiple static factory methods") void cannotConvertStringToMagazine() { - assertThat(converter.canConvert(Magazine.class)).isFalse(); + assertThat(converter.canConvertTo(Magazine.class)).isFalse(); } // ------------------------------------------------------------------------- @@ -120,7 +120,7 @@ private static Method magazineMethod(String methodName) { } private static void assertConverts(String input, Class targetType, Object expectedOutput) throws Exception { - assertThat(converter.canConvert(targetType)).isTrue(); + assertThat(converter.canConvertTo(targetType)).isTrue(); var result = converter.convert(input, targetType); @@ -161,10 +161,9 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - if (!(obj instanceof Book)) { + if (!(obj instanceof Book that)) { return false; } - var that = (Book) obj; return Objects.equals(this.title, that.title); } @@ -183,10 +182,9 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - if (!(obj instanceof Journal)) { + if (!(obj instanceof Journal that)) { return false; } - var that = (Journal) obj; return Objects.equals(this.title, that.title); } @@ -213,10 +211,9 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - if (!(obj instanceof Newspaper)) { + if (!(obj instanceof Newspaper that)) { return false; } - var that = (Newspaper) obj; return Objects.equals(this.title, that.title); } diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/AnnotationUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/AnnotationUtilsTests.java index 478491014be1..7360a9505eee 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/AnnotationUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/AnnotationUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -36,13 +36,22 @@ import java.lang.annotation.Target; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.List; import java.util.Optional; import java.util.function.Predicate; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.commons.util.pkg1.ClassLevelDir; +import org.junit.platform.commons.util.pkg1.InstanceLevelDir; +import org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateBeforeMethod; +import org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateTempDirField; +import org.junit.platform.commons.util.pkg1.subpkg.SubclassWithNonStaticPackagePrivateBeforeMethod; +import org.junit.platform.commons.util.pkg1.subpkg.SubclassWithNonStaticPackagePrivateTempDirField; /** * Unit tests for {@link AnnotationUtils}. @@ -380,6 +389,28 @@ void findAnnotatedMethodsForAnnotationUsedInClassAndSuperclassHierarchyDown() th assertThat(methods.subList(1, 3)).containsOnly(method1, method3); } + /** + * @see https://github.com/junit-team/junit5/issues/3553 + */ + @Test + void findAnnotatedMethodsDoesNotAllowInstanceMethodToHideStaticMethod() throws Exception { + final String BEFORE = "before"; + Class superclass = SuperclassWithStaticPackagePrivateBeforeMethod.class; + Method beforeAllMethod = superclass.getDeclaredMethod(BEFORE); + Class subclass = SubclassWithNonStaticPackagePrivateBeforeMethod.class; + Method beforeEachMethod = subclass.getDeclaredMethod(BEFORE); + + // Prerequisite + var methods = findAnnotatedMethods(superclass, BeforeAll.class, TOP_DOWN); + assertThat(methods).containsExactly(beforeAllMethod); + + // Actual use cases for this test + methods = findAnnotatedMethods(subclass, BeforeAll.class, TOP_DOWN); + assertThat(methods).containsExactly(beforeAllMethod); + methods = findAnnotatedMethods(subclass, BeforeEach.class, TOP_DOWN); + assertThat(methods).containsExactly(beforeEachMethod); + } + @Test void findAnnotatedMethodsForAnnotationUsedInInterface() throws Exception { var interfaceMethod = InterfaceWithAnnotatedDefaultMethod.class.getDeclaredMethod("interfaceMethod"); @@ -460,21 +491,58 @@ void findAnnotatedFieldsForAnnotationUsedInInterface() throws Exception { } @Test - void findAnnotatedFieldsForShadowedFields() throws Exception { - Class clazz = ClassWithShadowedAnnotatedFields.class; - var interfaceField = clazz.getDeclaredField("interfaceField"); - var superField = clazz.getDeclaredField("superField"); - var field1 = clazz.getDeclaredField("field1"); - var field2 = clazz.getDeclaredField("field2"); - var field3 = clazz.getDeclaredField("field3"); + void findAnnotatedFieldsFindsAllFieldsInTypeHierarchy() { + assertThat(findShadowingAnnotatedFields(Annotation1.class))// + .containsExactly("super", "foo", "baz", "super-shadow", "foo-shadow", "baz-shadow"); + assertThat(findShadowingAnnotatedFields(Annotation2.class))// + .containsExactly("bar", "baz", "bar-shadow", "baz-shadow"); + assertThat(findShadowingAnnotatedFields(Annotation3.class))// + .containsExactly("interface", "interface-shadow"); + } - assertThat(findShadowingAnnotatedFields(Annotation1.class)).containsExactly(superField, field1, field3); - assertThat(findShadowingAnnotatedFields(Annotation2.class)).containsExactly(field2, field3); - assertThat(findShadowingAnnotatedFields(Annotation3.class)).containsExactly(interfaceField); + @Test + void findAnnotatedFieldsForShadowedFieldsInLegacyMode() { + try { + ReflectionUtils.useLegacySearchSemantics = true; + + assertThat(findShadowingAnnotatedFields(Annotation1.class))// + .containsExactly("super-shadow", "foo-shadow", "baz-shadow"); + assertThat(findShadowingAnnotatedFields(Annotation2.class))// + .containsExactly("bar-shadow", "baz-shadow"); + assertThat(findShadowingAnnotatedFields(Annotation3.class))// + .containsExactly("interface-shadow"); + } + finally { + ReflectionUtils.useLegacySearchSemantics = false; + } + } + + private List findShadowingAnnotatedFields(Class annotationType) { + var fields = findAnnotatedFields(ClassWithShadowedAnnotatedFields.class, annotationType, isStringField); + var values = ReflectionUtils.readFieldValues(fields, new ClassWithShadowedAnnotatedFields()); + return values.stream().map(String::valueOf).toList(); } - private List findShadowingAnnotatedFields(Class annotationType) { - return findAnnotatedFields(ClassWithShadowedAnnotatedFields.class, annotationType, isStringField); + /** + * @see https://github.com/junit-team/junit5/issues/3553 + */ + @Test + void findAnnotatedFieldsDoesNotAllowInstanceFieldToHideStaticField() throws Exception { + final String TEMP_DIR = "tempDir"; + Class superclass = SuperclassWithStaticPackagePrivateTempDirField.class; + Field staticField = superclass.getDeclaredField(TEMP_DIR); + Class subclass = SubclassWithNonStaticPackagePrivateTempDirField.class; + Field nonStaticField = subclass.getDeclaredField(TEMP_DIR); + + // Prerequisite + var fields = findAnnotatedFields(superclass, ClassLevelDir.class, field -> true); + assertThat(fields).containsExactly(staticField); + + // Actual use cases for this test + fields = findAnnotatedFields(subclass, ClassLevelDir.class, field -> true); + assertThat(fields).containsExactly(staticField); + fields = findAnnotatedFields(subclass, InstanceLevelDir.class, field -> true); + assertThat(fields).containsExactly(nonStaticField); } // === findPublicAnnotatedFields() ========================================= diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ClassLoaderUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ClassLoaderUtilsTests.java index b3a01d5314ce..a80cd18242ea 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ClassLoaderUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ClassLoaderUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -108,8 +108,7 @@ void getLocationFromNullFails() { @Test void getLocationFromVariousObjectsArePresent() { - assertTrue(ClassLoaderUtils.getLocation(void.class).isPresent()); - assertTrue(ClassLoaderUtils.getLocation(byte.class).isPresent()); + assertTrue(ClassLoaderUtils.getLocation(getClass()).isPresent()); assertTrue(ClassLoaderUtils.getLocation(this).isPresent()); assertTrue(ClassLoaderUtils.getLocation("").isPresent()); assertTrue(ClassLoaderUtils.getLocation(0).isPresent()); diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ClassNamePatternFilterUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ClassNamePatternFilterUtilsTests.java index 214daefbba6b..1d68086db38b 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ClassNamePatternFilterUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ClassNamePatternFilterUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ClassUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ClassUtilsTests.java index 015e60b40cb3..8a683324b0e4 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ClassUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ClassUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ClasspathScannerTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ClasspathScannerTests.java index f68256284906..7cd2f31456b4 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ClasspathScannerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ClasspathScannerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,7 @@ package org.junit.platform.commons.util; +import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; @@ -19,10 +20,13 @@ import static org.junit.platform.commons.test.ConcurrencyTestingUtils.executeConcurrently; import java.io.IOException; +import java.io.InputStream; import java.lang.module.ModuleFinder; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystemNotFoundException; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -45,6 +49,7 @@ import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.function.Try; import org.junit.platform.commons.logging.LogRecordListener; +import org.junit.platform.commons.support.Resource; /** * Unit tests for {@link ClasspathScanner}. @@ -55,6 +60,7 @@ class ClasspathScannerTests { private static final ClassFilter allClasses = ClassFilter.of(type -> true); + private static final Predicate allResources = type -> true; private final List> loadedClasses = new ArrayList<>(); @@ -76,7 +82,7 @@ void scanForClassesInClasspathRootWhenMalformedClassnameInternalErrorOccursWithN }; assertClassesScannedWhenExceptionIsThrown(malformedClassNameSimulationFilter); - assertDebugMessageLogged(listener, "Failed to load java.lang.Class for path .+ during classpath scanning."); + assertDebugMessageLogged(listener, "Failed to load .+ during classpath scanning."); } @Test @@ -104,7 +110,7 @@ void scanForClassesInClasspathRootWhenOtherInternalErrorOccurs(LogRecordListener }; assertClassesScannedWhenExceptionIsThrown(otherInternalErrorSimulationFilter); - assertDebugMessageLogged(listener, "Failed to load java.lang.Class for path .+ during classpath scanning."); + assertDebugMessageLogged(listener, "Failed to load .+ during classpath scanning."); } @Test @@ -117,7 +123,7 @@ void scanForClassesInClasspathRootWhenGenericRuntimeExceptionOccurs(LogRecordLis }; assertClassesScannedWhenExceptionIsThrown(runtimeExceptionSimulationFilter); - assertDebugMessageLogged(listener, "Failed to load java.lang.Class for path .+ during classpath scanning."); + assertDebugMessageLogged(listener, "Failed to load .+ during classpath scanning."); } private void assertClassesScannedWhenExceptionIsThrown(Predicate> filter) throws Exception { @@ -126,6 +132,24 @@ private void assertClassesScannedWhenExceptionIsThrown(Predicate> filte assertThat(classes).hasSizeGreaterThanOrEqualTo(150); } + @Test + void scanForResourcesInClasspathRootWhenGenericRuntimeExceptionOccurs(LogRecordListener listener) throws Exception { + Predicate runtimeExceptionSimulationFilter = resource -> { + if (resource.getName().equals("org/junit/platform/commons/other-example.resource")) { + throw new RuntimeException("a generic exception"); + } + return true; + }; + + assertResourcesScannedWhenExceptionIsThrown(runtimeExceptionSimulationFilter); + assertDebugMessageLogged(listener, "Failed to load .+ during classpath scanning."); + } + + private void assertResourcesScannedWhenExceptionIsThrown(Predicate filter) { + var resources = this.classpathScanner.scanForResourcesInClasspathRoot(getTestClasspathResourceRoot(), filter); + assertThat(resources).hasSizeGreaterThanOrEqualTo(150); + } + private void assertDebugMessageLogged(LogRecordListener listener, String regex) { // @formatter:off assertThat(listener.stream(ClasspathScanner.class, Level.FINE) @@ -162,18 +186,103 @@ void scanForClassesInClasspathRootWithinJarWithSpacesInPath() throws Exception { private void scanForClassesInClasspathRootWithinJarFile(String resourceName) throws Exception { var jarfile = getClass().getResource(resourceName); - try (var classLoader = new URLClassLoader(new URL[] { jarfile })) { + try (var classLoader = new URLClassLoader(new URL[] { jarfile }, null)) { var classpathScanner = new ClasspathScanner(() -> classLoader, ReflectionUtils::tryToLoadClass); var classes = classpathScanner.scanForClassesInClasspathRoot(jarfile.toURI(), allClasses); - var classNames = classes.stream().map(Class::getName).collect(Collectors.toList()); - assertThat(classNames).hasSize(3) // - .contains("org.junit.platform.jartest.notincluded.NotIncluded", + assertThat(classes).extracting(Class::getName) // + .containsExactlyInAnyOrder("org.junit.platform.jartest.notincluded.NotIncluded", "org.junit.platform.jartest.included.recursive.RecursivelyIncluded", "org.junit.platform.jartest.included.Included"); } } + @Test + void scanForResourcesInClasspathRootWithinJarFile() throws Exception { + scanForResourcesInClasspathRootWithinJarFile("/jartest.jar"); + } + + @Test + void scanForResourcesInClasspathRootWithinJarWithSpacesInPath() throws Exception { + scanForResourcesInClasspathRootWithinJarFile("/folder with spaces/jar test with spaces.jar"); + } + + private void scanForResourcesInClasspathRootWithinJarFile(String resourceName) throws Exception { + var jarfile = getClass().getResource(resourceName); + + try (var classLoader = new URLClassLoader(new URL[] { jarfile }, null)) { + var classpathScanner = new ClasspathScanner(() -> classLoader, ReflectionUtils::tryToLoadClass); + + var resources = classpathScanner.scanForResourcesInClasspathRoot(jarfile.toURI(), allResources); + assertThat(resources).extracting(Resource::getName) // + .containsExactlyInAnyOrder("org/junit/platform/jartest/notincluded/not-included.resource", + "org/junit/platform/jartest/included/included.resource", + "org/junit/platform/jartest/included/recursive/recursively-included.resource", + "META-INF/MANIFEST.MF"); + } + } + + @Test + void scanForResourcesInShadowedClassPathRoot() throws Exception { + var jarFile = getClass().getResource("/jartest.jar"); + var shadowedJarFile = getClass().getResource("/jartest-shadowed.jar"); + + try (var classLoader = new URLClassLoader(new URL[] { jarFile, shadowedJarFile }, null)) { + var classpathScanner = new ClasspathScanner(() -> classLoader, ReflectionUtils::tryToLoadClass); + + var resources = classpathScanner.scanForResourcesInClasspathRoot(shadowedJarFile.toURI(), allResources); + assertThat(resources).extracting(Resource::getName).containsExactlyInAnyOrder( + "org/junit/platform/jartest/included/unique.resource", // + "org/junit/platform/jartest/included/included.resource", // + "org/junit/platform/jartest/included/recursive/recursively-included.resource", // + "META-INF/MANIFEST.MF"); + + assertThat(resources).extracting(Resource::getUri) // + .map(ClasspathScannerTests::jarFileAndEntry) // + .containsExactlyInAnyOrder( + // This resource only exists in the shadowed jar file + "jartest-shadowed.jar!/org/junit/platform/jartest/included/unique.resource", + // These resources exist in both the jar and shadowed jar file. + // They must be discovered in the shadowed jar as we're searching in that classpath root. + "jartest-shadowed.jar!/org/junit/platform/jartest/included/included.resource", + "jartest-shadowed.jar!/org/junit/platform/jartest/included/recursive/recursively-included.resource", + "jartest-shadowed.jar!/META-INF/MANIFEST.MF"); + } + } + + @Test + void scanForResourcesInPackageWithDuplicateResources() throws Exception { + var jarFile = getClass().getResource("/jartest.jar"); + var shadowedJarFile = getClass().getResource("/jartest-shadowed.jar"); + + try (var classLoader = new URLClassLoader(new URL[] { jarFile, shadowedJarFile }, null)) { + var classpathScanner = new ClasspathScanner(() -> classLoader, ReflectionUtils::tryToLoadClass); + + var resources = classpathScanner.scanForResourcesInPackage("org.junit.platform.jartest.included", + allResources); + + assertThat(resources).extracting(Resource::getUri) // + .map(ClasspathScannerTests::jarFileAndEntry) // + .containsExactlyInAnyOrder( + // This resource only exists in the shadowed jar file + "jartest-shadowed.jar!/org/junit/platform/jartest/included/unique.resource", + // These resources exist in both the jar and shadowed jar file. + "jartest.jar!/org/junit/platform/jartest/included/included.resource", + "jartest-shadowed.jar!/org/junit/platform/jartest/included/included.resource", + "jartest.jar!/org/junit/platform/jartest/included/recursive/recursively-included.resource", + "jartest-shadowed.jar!/org/junit/platform/jartest/included/recursive/recursively-included.resource"); + } + } + + private static String jarFileAndEntry(URI uri) { + var uriString = uri.toString(); + int lastJarUriSeparator = uriString.lastIndexOf("!/"); + var jarUri = uriString.substring(0, lastJarUriSeparator); + var jarEntry = uriString.substring(lastJarUriSeparator + 1); + var fileName = jarUri.substring(jarUri.lastIndexOf("/") + 1); + return fileName + "!" + jarEntry; + } + @Test void scanForClassesInPackage() { var classes = classpathScanner.scanForClassesInPackage("org.junit.platform.commons", allClasses); @@ -182,6 +291,14 @@ void scanForClassesInPackage() { assertTrue(classes.contains(MemberClassToBeFound.class)); } + @Test + void scanForResourcesInPackage() { + var resources = classpathScanner.scanForResourcesInPackage("org.junit.platform.commons", allResources); + assertThat(resources).extracting(Resource::getUri).containsExactlyInAnyOrder( + uriOf("/org/junit/platform/commons/example.resource"), + uriOf("/org/junit/platform/commons/other-example.resource")); + } + @Test // #2500 void scanForClassesInPackageWithinModulesSharingNamePrefix(@TempDir Path temp) throws Exception { @@ -239,12 +356,35 @@ void findAllClassesInPackageWithinJarFileConcurrently() throws Exception { assertThrows(FileSystemNotFoundException.class, () -> FileSystems.getFileSystem(jarUri), "FileSystem should be closed"); - results.forEach(classes -> { - assertThat(classes).hasSize(2); - var classNames = classes.stream().map(Class::getSimpleName).toList(); - assertTrue(classNames.contains("Included")); - assertTrue(classNames.contains("RecursivelyIncluded")); - }); + results.forEach(classes -> assertThat(classes) // + .hasSize(2) // + .extracting(Class::getSimpleName) // + .containsExactlyInAnyOrder("Included", "RecursivelyIncluded")); + } + } + + @Test + void findAllResourcesInPackageWithinJarFileConcurrently() throws Exception { + var jarFile = getClass().getResource("/jartest.jar"); + var jarUri = URI.create("jar:" + jarFile); + + try (var classLoader = new URLClassLoader(new URL[] { jarFile })) { + var classpathScanner = new ClasspathScanner(() -> classLoader, ReflectionUtils::tryToLoadClass); + + var results = executeConcurrently(10, + () -> classpathScanner.scanForResourcesInPackage("org.junit.platform.jartest.included", allResources)); + + assertThrows(FileSystemNotFoundException.class, () -> FileSystems.getFileSystem(jarUri), + "FileSystem should be closed"); + + // @formatter:off + results.forEach(resources -> assertThat(resources) + .hasSize(2) + .extracting(Resource::getName).containsExactlyInAnyOrder( + "org/junit/platform/jartest/included/included.resource", + "org/junit/platform/jartest/included/recursive/recursively-included.resource" + )); + // @formatter:on } } @@ -258,6 +398,16 @@ void scanForClassesInDefaultPackage() { assertTrue(classes.stream().anyMatch(clazz -> "DefaultPackageTestCase".equals(clazz.getName()))); } + @Test + void scanForResourcesInDefaultPackage() { + Predicate resourceFilter = this::inDefaultPackage; + var resources = classpathScanner.scanForResourcesInPackage("", resourceFilter); + + assertThat(resources).as("number of resources found in default package").isNotEmpty(); + assertTrue(resources.stream().allMatch(this::inDefaultPackage)); + assertTrue(resources.stream().anyMatch(resource -> "default-package.resource".equals(resource.getName()))); + } + @Test void scanForClassesInPackageWithFilter() { var thisClassOnly = ClassFilter.of(clazz -> clazz == ClasspathScannerTests.class); @@ -265,18 +415,54 @@ void scanForClassesInPackageWithFilter() { assertSame(ClasspathScannerTests.class, classes.get(0)); } + @Test + void scanForResourcesInPackageWithFilter() { + Predicate thisResourceOnly = resource -> "org/junit/platform/commons/example.resource".equals( + resource.getName()); + var resources = classpathScanner.scanForResourcesInPackage("org.junit.platform.commons", thisResourceOnly); + assertThat(resources).extracting(Resource::getName).containsExactly( + "org/junit/platform/commons/example.resource"); + } + + @Test + void resourcesCanBeRead() throws IOException { + Predicate thisResourceOnly = resource -> "org/junit/platform/commons/example.resource".equals( + resource.getName()); + var resources = classpathScanner.scanForResourcesInPackage("org.junit.platform.commons", thisResourceOnly); + Resource resource = resources.get(0); + + assertThat(resource.getName()).isEqualTo("org/junit/platform/commons/example.resource"); + assertThat(resource.getUri()).isEqualTo(uriOf("/org/junit/platform/commons/example.resource")); + try (InputStream is = resource.getInputStream()) { + String contents = new String(is.readAllBytes(), StandardCharsets.UTF_8); + assertThat(contents).isEqualTo("This file was unintentionally left blank.\n"); + } + } + @Test void scanForClassesInPackageForNullBasePackage() { assertThrows(PreconditionViolationException.class, () -> classpathScanner.scanForClassesInPackage(null, allClasses)); } + @Test + void scanForResourcesInPackageForNullBasePackage() { + assertThrows(PreconditionViolationException.class, + () -> classpathScanner.scanForResourcesInPackage(null, allResources)); + } + @Test void scanForClassesInPackageForWhitespaceBasePackage() { assertThrows(PreconditionViolationException.class, () -> classpathScanner.scanForClassesInPackage(" ", allClasses)); } + @Test + void scanForResourcesInPackageForWhitespaceBasePackage() { + assertThrows(PreconditionViolationException.class, + () -> classpathScanner.scanForResourcesInPackage(" ", allResources)); + } + @Test void scanForClassesInPackageForNullClassFilter() { assertThrows(PreconditionViolationException.class, @@ -290,6 +476,13 @@ void scanForClassesInPackageWhenIOExceptionOccurs() { assertThat(classes).isEmpty(); } + @Test + void scanForResourcesInPackageWhenIOExceptionOccurs() { + var scanner = new ClasspathScanner(ThrowingClassLoader::new, ReflectionUtils::tryToLoadClass); + var classes = scanner.scanForResourcesInPackage("org.junit.platform.commons", allResources); + assertThat(classes).isEmpty(); + } + @Test void scanForClassesInPackageOnlyLoadsClassesThatAreIncludedByTheClassNameFilter() { Predicate classNameFilter = name -> ClasspathScannerTests.class.getName().equals(name); @@ -340,6 +533,10 @@ private boolean inDefaultPackage(Class clazz) { return pkg == null || "".equals(clazz.getPackage().getName()); } + private boolean inDefaultPackage(Resource resource) { + return !resource.getName().contains("/"); + } + @Test void findAllClassesInClasspathRootWithFilter() throws Exception { var root = getTestClasspathRoot(); @@ -377,11 +574,28 @@ void onlyLoadsClassesInClasspathRootThatAreIncludedByTheClassNameFilter() throws assertThat(loadedClasses).containsExactly(ClasspathScannerTests.class); } + private static URI uriOf(String name) { + var resource = ClasspathScannerTests.class.getResource(name); + try { + return requireNonNull(resource).toURI(); + } + catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + private URI getTestClasspathRoot() throws Exception { var location = getClass().getProtectionDomain().getCodeSource().getLocation(); return location.toURI(); } + private URI getTestClasspathResourceRoot() { + // Gradle puts classes and resources in different roots. + var defaultPackageResource = "/default-package.resource"; + var resourceUri = getClass().getResource(defaultPackageResource).toString(); + return URI.create(resourceUri.substring(0, resourceUri.length() - defaultPackageResource.length())); + } + class MemberClassToBeFound { } diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/CloseablePathTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/CloseablePathTests.java new file mode 100644 index 000000000000..512eefeb41ec --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/CloseablePathTests.java @@ -0,0 +1,130 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.platform.commons.test.ConcurrencyTestingUtils.executeConcurrently; +import static org.junit.platform.commons.util.CloseablePath.JAR_URI_SCHEME; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystems; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.util.CloseablePath.FileSystemProvider; +import org.junit.platform.engine.support.hierarchical.OpenTest4JAwareThrowableCollector; + +class CloseablePathTests { + + URI uri; + URI jarUri; + + List paths = new ArrayList<>(); + + @BeforeEach + void createUris() throws Exception { + uri = getClass().getResource("/jartest.jar").toURI(); + jarUri = URI.create(JAR_URI_SCHEME + ':' + uri); + } + + @AfterEach + void closeAllPaths() { + closeAll(paths); + } + + @Test + void parsesJarUri() throws Exception { + FileSystemProvider fileSystemProvider = mock(); + + FileSystem fileSystem = mock(); + when(fileSystemProvider.newFileSystem(any())).thenReturn(fileSystem); + + URI jarFileWithEntry = URI.create("jar:file:/example.jar!/com/example/Example.class"); + CloseablePath.create(jarFileWithEntry, fileSystemProvider).close(); + + URI jarFileUri = URI.create("jar:file:/example.jar"); + verify(fileSystemProvider).newFileSystem(jarFileUri); + verifyNoMoreInteractions(fileSystemProvider); + } + + @Test + void parsesRecursiveJarUri() throws Exception { + FileSystemProvider fileSystemProvider = mock(); + + FileSystem fileSystem = mock(); + when(fileSystemProvider.newFileSystem(any())).thenReturn(fileSystem); + + URI jarNestedFileWithEntry = URI.create( + "jar:nested:file:/example.jar!/BOOT-INF/classes!/com/example/Example.class"); + CloseablePath.create(jarNestedFileWithEntry, fileSystemProvider).close(); + + URI jarNestedFile = URI.create("jar:nested:file:/example.jar!/BOOT-INF/classes"); + verify(fileSystemProvider).newFileSystem(jarNestedFile); + verifyNoMoreInteractions(fileSystemProvider); + } + + @Test + void createsAndClosesJarFileSystemOnceWhenCalledConcurrently() throws Exception { + var numThreads = 50; + + FileSystemProvider fileSystemProvider = mock(); + when(fileSystemProvider.newFileSystem(any())) // + .thenAnswer(invocation -> FileSystems.newFileSystem((URI) invocation.getArgument(0), Map.of())); + + paths = executeConcurrently(numThreads, () -> CloseablePath.create(uri, fileSystemProvider)); + verify(fileSystemProvider, only()).newFileSystem(jarUri); + + // Close all but the first path + closeAll(paths.subList(1, numThreads)); + assertDoesNotThrow(() -> FileSystems.getFileSystem(jarUri), "FileSystem should still be open"); + + // Close last remaining path + paths.get(0).close(); + assertThrows(FileSystemNotFoundException.class, () -> FileSystems.getFileSystem(jarUri), + "FileSystem should have been closed"); + } + + @Test + @SuppressWarnings("resource") + void closingIsIdempotent() throws Exception { + var path1 = CloseablePath.create(uri); + paths.add(path1); + var path2 = CloseablePath.create(uri); + paths.add(path2); + + path1.close(); + path1.close(); + assertDoesNotThrow(() -> FileSystems.getFileSystem(jarUri), "FileSystem should still be open"); + + path2.close(); + assertThrows(FileSystemNotFoundException.class, () -> FileSystems.getFileSystem(jarUri), + "FileSystem should have been closed"); + } + + private static void closeAll(List paths) { + var throwableCollector = new OpenTest4JAwareThrowableCollector(); + paths.forEach(closeablePath -> throwableCollector.execute(closeablePath::close)); + throwableCollector.assertEmpty(); + } +} diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/CollectionUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/CollectionUtilsTests.java index 63563e8071ce..ddc08e339f93 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/CollectionUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/CollectionUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -21,6 +21,7 @@ import java.lang.reflect.Array; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -32,6 +33,7 @@ import java.util.stream.Stream; import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.extension.ParameterContext; @@ -52,256 +54,306 @@ */ class CollectionUtilsTests { - @Test - void getOnlyElementWithNullCollection() { - var exception = assertThrows(PreconditionViolationException.class, () -> CollectionUtils.getOnlyElement(null)); - assertEquals("collection must not be null", exception.getMessage()); - } + @Nested + class OnlyElement { - @Test - void getOnlyElementWithEmptyCollection() { - var exception = assertThrows(PreconditionViolationException.class, - () -> CollectionUtils.getOnlyElement(Set.of())); - assertEquals("collection must contain exactly one element: []", exception.getMessage()); - } + @Test + void nullCollection() { + var exception = assertThrows(PreconditionViolationException.class, + () -> CollectionUtils.getOnlyElement(null)); + assertEquals("collection must not be null", exception.getMessage()); + } - @Test - void getOnlyElementWithSingleElementCollection() { - var expected = new Object(); - var actual = CollectionUtils.getOnlyElement(Set.of(expected)); - assertSame(expected, actual); - } + @Test + void emptyCollection() { + var exception = assertThrows(PreconditionViolationException.class, + () -> CollectionUtils.getOnlyElement(Set.of())); + assertEquals("collection must contain exactly one element: []", exception.getMessage()); + } - @Test - void getOnlyElementWithMultiElementCollection() { - var exception = assertThrows(PreconditionViolationException.class, - () -> CollectionUtils.getOnlyElement(List.of("foo", "bar"))); - assertEquals("collection must contain exactly one element: [foo, bar]", exception.getMessage()); - } + @Test + void singleElementCollection() { + var expected = new Object(); + var actual = CollectionUtils.getOnlyElement(Set.of(expected)); + assertSame(expected, actual); + } - @Test - void toUnmodifiableListThrowsOnMutation() { - var numbers = Stream.of(1).collect(toUnmodifiableList()); - assertThrows(UnsupportedOperationException.class, numbers::clear); + @Test + void multiElementCollection() { + var exception = assertThrows(PreconditionViolationException.class, + () -> CollectionUtils.getOnlyElement(List.of("foo", "bar"))); + assertEquals("collection must contain exactly one element: [foo, bar]", exception.getMessage()); + } } - @ParameterizedTest - @ValueSource(classes = { // - Stream.class, // - DoubleStream.class, // - IntStream.class, // - LongStream.class, // - Collection.class, // - Iterable.class, // - Iterator.class, // - Object[].class, // - String[].class, // - int[].class, // - double[].class, // - char[].class // - }) - void isConvertibleToStreamForSupportedTypes(Class type) { - assertThat(CollectionUtils.isConvertibleToStream(type)).isTrue(); - } + @Nested + class FirstElement { - @ParameterizedTest - @MethodSource("objectsConvertibleToStreams") - void isConvertibleToStreamForSupportedTypesFromObjects(Object object) { - assertThat(CollectionUtils.isConvertibleToStream(object.getClass())).isTrue(); - } + @Test + void nullCollection() { + var exception = assertThrows(PreconditionViolationException.class, + () -> CollectionUtils.getFirstElement(null)); + assertEquals("collection must not be null", exception.getMessage()); + } - static Stream objectsConvertibleToStreams() { - return Stream.of(// - Stream.of("cat", "dog"), // - DoubleStream.of(42.3), // - IntStream.of(99), // - LongStream.of(100000000), // - Set.of(1, 2, 3), // - Arguments.of((Object) new Object[] { 9, 8, 7 }), // - new int[] { 5, 10, 15 }// - ); - } + @Test + void emptyCollection() { + assertThat(CollectionUtils.getFirstElement(Set.of())).isEmpty(); + } - @ParameterizedTest - @ValueSource(classes = { // - void.class, // - Void.class, // - Object.class, // - Integer.class, // - String.class, // - int.class, // - boolean.class // - }) - void isConvertibleToStreamForUnsupportedTypes(Class type) { - assertThat(CollectionUtils.isConvertibleToStream(type)).isFalse(); - } + @Test + void singleElementCollection() { + var expected = new Object(); + assertThat(CollectionUtils.getFirstElement(Set.of(expected))).containsSame(expected); + } - @Test - void isConvertibleToStreamForNull() { - assertThat(CollectionUtils.isConvertibleToStream(null)).isFalse(); + @Test + void multiElementCollection() { + assertThat(CollectionUtils.getFirstElement(List.of("foo", "bar"))).contains("foo"); + } + + @Test + void collectionWithNullValues() { + assertThat(CollectionUtils.getFirstElement(Arrays.asList(new Object[1]))).isEmpty(); + } } - @Test - void toStreamWithNull() { - Exception exception = assertThrows(PreconditionViolationException.class, () -> CollectionUtils.toStream(null)); + @Nested + class UnmodifiableList { - assertThat(exception).hasMessage("Object must not be null"); + @Test + void throwsOnMutation() { + var numbers = Stream.of(1).collect(toUnmodifiableList()); + assertThrows(UnsupportedOperationException.class, numbers::clear); + } } - @Test - void toStreamWithUnsupportedObjectType() { - Exception exception = assertThrows(PreconditionViolationException.class, - () -> CollectionUtils.toStream("unknown")); + @Nested + class StreamConversion { + + @ParameterizedTest + @ValueSource(classes = { // + Stream.class, // + DoubleStream.class, // + IntStream.class, // + LongStream.class, // + Collection.class, // + Iterable.class, // + Iterator.class, // + Object[].class, // + String[].class, // + int[].class, // + double[].class, // + char[].class // + }) + void isConvertibleToStreamForSupportedTypes(Class type) { + assertThat(CollectionUtils.isConvertibleToStream(type)).isTrue(); + } - assertThat(exception).hasMessage("Cannot convert instance of java.lang.String into a Stream: unknown"); - } + @ParameterizedTest + @MethodSource("objectsConvertibleToStreams") + void isConvertibleToStreamForSupportedTypesFromObjects(Object object) { + assertThat(CollectionUtils.isConvertibleToStream(object.getClass())).isTrue(); + } - @Test - void toStreamWithExistingStream() { - var input = Stream.of("foo"); + static Stream objectsConvertibleToStreams() { + return Stream.of(// + Stream.of("cat", "dog"), // + DoubleStream.of(42.3), // + IntStream.of(99), // + LongStream.of(100000000), // + Set.of(1, 2, 3), // + Arguments.of((Object) new Object[] { 9, 8, 7 }), // + new int[] { 5, 10, 15 }// + ); + } - var result = CollectionUtils.toStream(input); + @ParameterizedTest + @ValueSource(classes = { // + void.class, // + Void.class, // + Object.class, // + Integer.class, // + String.class, // + int.class, // + boolean.class // + }) + void isConvertibleToStreamForUnsupportedTypes(Class type) { + assertThat(CollectionUtils.isConvertibleToStream(type)).isFalse(); + } - assertThat(result).isSameAs(input); - } + @Test + void isConvertibleToStreamForNull() { + assertThat(CollectionUtils.isConvertibleToStream(null)).isFalse(); + } - @Test - @SuppressWarnings("unchecked") - void toStreamWithDoubleStream() { - var input = DoubleStream.of(42.23); + @Test + void toStreamWithNull() { + Exception exception = assertThrows(PreconditionViolationException.class, + () -> CollectionUtils.toStream(null)); - var result = (Stream) CollectionUtils.toStream(input); + assertThat(exception).hasMessage("Object must not be null"); + } - assertThat(result).containsExactly(42.23); - } + @Test + void toStreamWithUnsupportedObjectType() { + Exception exception = assertThrows(PreconditionViolationException.class, + () -> CollectionUtils.toStream("unknown")); - @Test - @SuppressWarnings("unchecked") - void toStreamWithIntStream() { - var input = IntStream.of(23, 42); + assertThat(exception).hasMessage("Cannot convert instance of java.lang.String into a Stream: unknown"); + } - var result = (Stream) CollectionUtils.toStream(input); + @Test + void toStreamWithExistingStream() { + var input = Stream.of("foo"); - assertThat(result).containsExactly(23, 42); - } + var result = CollectionUtils.toStream(input); + + assertThat(result).isSameAs(input); + } - @Test - @SuppressWarnings("unchecked") - void toStreamWithLongStream() { - var input = LongStream.of(23L, 42L); + @Test + @SuppressWarnings("unchecked") + void toStreamWithDoubleStream() { + var input = DoubleStream.of(42.23); - var result = (Stream) CollectionUtils.toStream(input); + var result = (Stream) CollectionUtils.toStream(input); - assertThat(result).containsExactly(23L, 42L); - } + assertThat(result).containsExactly(42.23); + } - @Test - @SuppressWarnings({ "unchecked", "serial" }) - void toStreamWithCollection() { - var collectionStreamClosed = new AtomicBoolean(false); - Collection input = new ArrayList<>() { + @Test + @SuppressWarnings("unchecked") + void toStreamWithIntStream() { + var input = IntStream.of(23, 42); - { - add("foo"); - add("bar"); - } + var result = (Stream) CollectionUtils.toStream(input); - @Override - public Stream stream() { - return super.stream().onClose(() -> collectionStreamClosed.set(true)); - } - }; + assertThat(result).containsExactly(23, 42); + } - try (var stream = (Stream) CollectionUtils.toStream(input)) { - var result = stream.collect(toList()); - assertThat(result).containsExactly("foo", "bar"); + @Test + @SuppressWarnings("unchecked") + void toStreamWithLongStream() { + var input = LongStream.of(23L, 42L); + + var result = (Stream) CollectionUtils.toStream(input); + + assertThat(result).containsExactly(23L, 42L); } - assertThat(collectionStreamClosed.get()).describedAs("collectionStreamClosed").isTrue(); - } + @Test + @SuppressWarnings({ "unchecked", "serial" }) + void toStreamWithCollection() { + var collectionStreamClosed = new AtomicBoolean(false); + Collection input = new ArrayList<>() { + + { + add("foo"); + add("bar"); + } + + @Override + public Stream stream() { + return super.stream().onClose(() -> collectionStreamClosed.set(true)); + } + }; + + try (var stream = (Stream) CollectionUtils.toStream(input)) { + var result = stream.collect(toList()); + assertThat(result).containsExactly("foo", "bar"); + } - @Test - @SuppressWarnings("unchecked") - void toStreamWithIterable() { + assertThat(collectionStreamClosed.get()).describedAs("collectionStreamClosed").isTrue(); + } - Iterable input = () -> List.of("foo", "bar").iterator(); + @Test + @SuppressWarnings("unchecked") + void toStreamWithIterable() { - var result = (Stream) CollectionUtils.toStream(input); + Iterable input = () -> List.of("foo", "bar").iterator(); - assertThat(result).containsExactly("foo", "bar"); - } + var result = (Stream) CollectionUtils.toStream(input); - @Test - @SuppressWarnings("unchecked") - void toStreamWithIterator() { - var input = List.of("foo", "bar").iterator(); + assertThat(result).containsExactly("foo", "bar"); + } - var result = (Stream) CollectionUtils.toStream(input); + @Test + @SuppressWarnings("unchecked") + void toStreamWithIterator() { + var input = List.of("foo", "bar").iterator(); - assertThat(result).containsExactly("foo", "bar"); - } + var result = (Stream) CollectionUtils.toStream(input); - @Test - @SuppressWarnings("unchecked") - void toStreamWithArray() { - var result = (Stream) CollectionUtils.toStream(new String[] { "foo", "bar" }); + assertThat(result).containsExactly("foo", "bar"); + } - assertThat(result).containsExactly("foo", "bar"); - } + @Test + @SuppressWarnings("unchecked") + void toStreamWithArray() { + var result = (Stream) CollectionUtils.toStream(new String[] { "foo", "bar" }); - @TestFactory - Stream toStreamWithPrimitiveArrays() { - //@formatter:off - return Stream.of( - dynamicTest("boolean[]", - () -> toStreamWithPrimitiveArray(new boolean[] { true, false })), - dynamicTest("byte[]", - () -> toStreamWithPrimitiveArray(new byte[] { 0, Byte.MIN_VALUE, Byte.MAX_VALUE })), - dynamicTest("char[]", - () -> toStreamWithPrimitiveArray(new char[] { 0, Character.MIN_VALUE, Character.MAX_VALUE })), - dynamicTest("double[]", - () -> toStreamWithPrimitiveArray(new double[] { 0, Double.MIN_VALUE, Double.MAX_VALUE })), - dynamicTest("float[]", - () -> toStreamWithPrimitiveArray(new float[] { 0, Float.MIN_VALUE, Float.MAX_VALUE })), - dynamicTest("int[]", - () -> toStreamWithPrimitiveArray(new int[] { 0, Integer.MIN_VALUE, Integer.MAX_VALUE })), - dynamicTest("long[]", - () -> toStreamWithPrimitiveArray(new long[] { 0, Long.MIN_VALUE, Long.MAX_VALUE })), - dynamicTest("short[]", - () -> toStreamWithPrimitiveArray(new short[] { 0, Short.MIN_VALUE, Short.MAX_VALUE })) - ); - //@formatter:on - } + assertThat(result).containsExactly("foo", "bar"); + } - private void toStreamWithPrimitiveArray(Object primitiveArray) { - assertTrue(primitiveArray.getClass().isArray()); - assertTrue(primitiveArray.getClass().getComponentType().isPrimitive()); - var result = CollectionUtils.toStream(primitiveArray).toArray(); - for (var i = 0; i < result.length; i++) { - assertEquals(Array.get(primitiveArray, i), result[i]); + @TestFactory + Stream toStreamWithPrimitiveArrays() { + //@formatter:off + return Stream.of( + dynamicTest("boolean[]", + () -> toStreamWithPrimitiveArray(new boolean[] { true, false })), + dynamicTest("byte[]", + () -> toStreamWithPrimitiveArray(new byte[] { 0, Byte.MIN_VALUE, Byte.MAX_VALUE })), + dynamicTest("char[]", + () -> toStreamWithPrimitiveArray(new char[] { 0, Character.MIN_VALUE, Character.MAX_VALUE })), + dynamicTest("double[]", + () -> toStreamWithPrimitiveArray(new double[] { 0, Double.MIN_VALUE, Double.MAX_VALUE })), + dynamicTest("float[]", + () -> toStreamWithPrimitiveArray(new float[] { 0, Float.MIN_VALUE, Float.MAX_VALUE })), + dynamicTest("int[]", + () -> toStreamWithPrimitiveArray(new int[] { 0, Integer.MIN_VALUE, Integer.MAX_VALUE })), + dynamicTest("long[]", + () -> toStreamWithPrimitiveArray(new long[] { 0, Long.MIN_VALUE, Long.MAX_VALUE })), + dynamicTest("short[]", + () -> toStreamWithPrimitiveArray(new short[] { 0, Short.MIN_VALUE, Short.MAX_VALUE })) + ); + //@formatter:on + } + + private void toStreamWithPrimitiveArray(Object primitiveArray) { + assertTrue(primitiveArray.getClass().isArray()); + assertTrue(primitiveArray.getClass().getComponentType().isPrimitive()); + var result = CollectionUtils.toStream(primitiveArray).toArray(); + for (var i = 0; i < result.length; i++) { + assertEquals(Array.get(primitiveArray, i), result[i]); + } } } - @ParameterizedTest - @CsvSource(delimiter = '|', nullValues = "N/A", textBlock = """ - foo,bar,baz | baz,bar,foo - foo,bar | bar,foo - foo | foo - N/A | N/A - """) - void iteratesListElementsInReverseOrder(@ConvertWith(CommaSeparator.class) List input, - @ConvertWith(CommaSeparator.class) List expected) { - var result = new ArrayList<>(); + @Nested + class ReverseOrderIteration { - CollectionUtils.forEachInReverseOrder(input, result::add); + @ParameterizedTest + @CsvSource(delimiter = '|', nullValues = "N/A", textBlock = """ + foo,bar,baz | baz,bar,foo + foo,bar | bar,foo + foo | foo + N/A | N/A + """) + void iteratesListElementsInReverseOrder(@ConvertWith(CommaSeparator.class) List input, + @ConvertWith(CommaSeparator.class) List expected) { + var result = new ArrayList<>(); - assertEquals(expected, result); - } + CollectionUtils.forEachInReverseOrder(input, result::add); - private static class CommaSeparator implements ArgumentConverter { - @Override - public Object convert(Object source, ParameterContext context) throws ArgumentConversionException { - return source == null ? List.of() : List.of(((String) source).split(",")); + assertEquals(expected, result); + } + + private static class CommaSeparator implements ArgumentConverter { + @Override + public Object convert(Object source, ParameterContext context) throws ArgumentConversionException { + return source == null ? List.of() : List.of(((String) source).split(",")); + } } } } diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ExceptionUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ExceptionUtilsTests.java index edd11a17ded4..fcd55714f993 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ExceptionUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ExceptionUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/FunctionUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/FunctionUtilsTests.java index a543c6528482..dd7c44d6f770 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/FunctionUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/FunctionUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/LruCacheTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/LruCacheTests.java index 7d7d6168c2a3..917e7c864705 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/LruCacheTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/LruCacheTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ModuleUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ModuleUtilsTests.java index 5b8a42b547a9..c3800cf0c8c0 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ModuleUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ModuleUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/PackageUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/PackageUtilsTests.java index 009ba2c5ce16..397419bba54f 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/PackageUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/PackageUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/PreconditionsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/PreconditionsTests.java index b525437b19d9..5b97851f697e 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/PreconditionsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/PreconditionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java index d74b74ce7592..394c7f73aaa9 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -25,15 +25,24 @@ import static org.junit.platform.commons.function.Try.success; import static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP; import static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.TOP_DOWN; +import static org.junit.platform.commons.util.ReflectionUtils.findFields; import static org.junit.platform.commons.util.ReflectionUtils.findMethod; import static org.junit.platform.commons.util.ReflectionUtils.findMethods; import static org.junit.platform.commons.util.ReflectionUtils.invokeMethod; +import static org.junit.platform.commons.util.ReflectionUtils.isWideningConversion; import static org.junit.platform.commons.util.ReflectionUtils.readFieldValue; +import static org.junit.platform.commons.util.ReflectionUtils.readFieldValues; import static org.junit.platform.commons.util.ReflectionUtils.tryToReadFieldValue; +import java.io.Closeable; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -59,11 +68,12 @@ import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.logging.LogRecordListener; import org.junit.platform.commons.test.TestClassLoader; -import org.junit.platform.commons.util.ReflectionUtilsTests.ClassWithNestedClasses.Nested1; -import org.junit.platform.commons.util.ReflectionUtilsTests.ClassWithNestedClasses.Nested2; -import org.junit.platform.commons.util.ReflectionUtilsTests.ClassWithNestedClasses.Nested3; -import org.junit.platform.commons.util.ReflectionUtilsTests.Interface45.Nested5; -import org.junit.platform.commons.util.ReflectionUtilsTests.InterfaceWithNestedClass.Nested4; +import org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.ClassWithNestedClasses.Nested1; +import org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.ClassWithNestedClasses.Nested2; +import org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.ClassWithNestedClasses.Nested3; +import org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.Interface45.Nested5; +import org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.InterfaceWithNestedClass; +import org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.InterfaceWithNestedClass.Nested4; import org.junit.platform.commons.util.ReflectionUtilsTests.OuterClass.InnerClass; import org.junit.platform.commons.util.ReflectionUtilsTests.OuterClass.InnerClass.RecursiveInnerInnerClass; import org.junit.platform.commons.util.ReflectionUtilsTests.OuterClass.InnerSiblingClass; @@ -72,6 +82,10 @@ import org.junit.platform.commons.util.ReflectionUtilsTests.OuterClass.StaticNestedSiblingClass; import org.junit.platform.commons.util.ReflectionUtilsTests.OuterClassImplementingInterface.InnerClassImplementingInterface; import org.junit.platform.commons.util.classes.CustomType; +import org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateBeforeMethod; +import org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateTempDirField; +import org.junit.platform.commons.util.pkg1.subpkg.SubclassWithNonStaticPackagePrivateBeforeMethod; +import org.junit.platform.commons.util.pkg1.subpkg.SubclassWithNonStaticPackagePrivateTempDirField; /** * Unit tests for {@link ReflectionUtils}. @@ -86,205 +100,415 @@ class ReflectionUtilsTests { private static final Predicate methodContains4 = method -> method.getName().contains("4"); private static final Predicate methodContains5 = method -> method.getName().contains("5"); - @Test - void isPublic() throws Exception { - assertTrue(ReflectionUtils.isPublic(PublicClass.class)); - assertTrue(ReflectionUtils.isPublic(PublicClass.class.getMethod("publicMethod"))); - - assertFalse(ReflectionUtils.isPublic(PrivateClass.class)); - assertFalse(ReflectionUtils.isPublic(PrivateClass.class.getDeclaredMethod("privateMethod"))); - assertFalse(ReflectionUtils.isPublic(ProtectedClass.class)); - assertFalse(ReflectionUtils.isPublic(ProtectedClass.class.getDeclaredMethod("protectedMethod"))); - assertFalse(ReflectionUtils.isPublic(PackageVisibleClass.class)); - assertFalse(ReflectionUtils.isPublic(PackageVisibleClass.class.getDeclaredMethod("packageVisibleMethod"))); - } + @Nested + class MiscellaneousTests { - @Test - void isPrivate() throws Exception { - assertTrue(ReflectionUtils.isPrivate(PrivateClass.class)); - assertTrue(ReflectionUtils.isPrivate(PrivateClass.class.getDeclaredMethod("privateMethod"))); - - assertFalse(ReflectionUtils.isPrivate(PublicClass.class)); - assertFalse(ReflectionUtils.isPrivate(PublicClass.class.getMethod("publicMethod"))); - assertFalse(ReflectionUtils.isPrivate(ProtectedClass.class)); - assertFalse(ReflectionUtils.isPrivate(ProtectedClass.class.getDeclaredMethod("protectedMethod"))); - assertFalse(ReflectionUtils.isPrivate(PackageVisibleClass.class)); - assertFalse(ReflectionUtils.isPrivate(PackageVisibleClass.class.getDeclaredMethod("packageVisibleMethod"))); - } + @Test + void returnsPrimitiveVoid() throws Exception { + Class clazz = ClassWithVoidAndNonVoidMethods.class; + assertTrue(ReflectionUtils.returnsPrimitiveVoid(clazz.getDeclaredMethod("voidMethod"))); - @Test - void isNotPrivate() throws Exception { - assertTrue(ReflectionUtils.isNotPrivate(PublicClass.class)); - assertTrue(ReflectionUtils.isNotPrivate(PublicClass.class.getDeclaredMethod("publicMethod"))); - assertTrue(ReflectionUtils.isNotPrivate(ProtectedClass.class)); - assertTrue(ReflectionUtils.isNotPrivate(ProtectedClass.class.getDeclaredMethod("protectedMethod"))); - assertTrue(ReflectionUtils.isNotPrivate(PackageVisibleClass.class)); - assertTrue(ReflectionUtils.isNotPrivate(PackageVisibleClass.class.getDeclaredMethod("packageVisibleMethod"))); + assertFalse(ReflectionUtils.returnsPrimitiveVoid(clazz.getDeclaredMethod("methodReturningVoidReference"))); + assertFalse(ReflectionUtils.returnsPrimitiveVoid(clazz.getDeclaredMethod("methodReturningObject"))); + assertFalse(ReflectionUtils.returnsPrimitiveVoid(clazz.getDeclaredMethod("methodReturningPrimitive"))); + } - assertFalse(ReflectionUtils.isNotPrivate(PrivateClass.class.getDeclaredMethod("privateMethod"))); - } + @Test + void getAllAssignmentCompatibleClassesWithNullClass() { + assertThrows(PreconditionViolationException.class, + () -> ReflectionUtils.getAllAssignmentCompatibleClasses(null)); + } - @Test - void isAbstract() throws Exception { - assertTrue(ReflectionUtils.isAbstract(AbstractClass.class)); - assertTrue(ReflectionUtils.isAbstract(AbstractClass.class.getDeclaredMethod("abstractMethod"))); + @Test + void getAllAssignmentCompatibleClasses() { + var superclasses = ReflectionUtils.getAllAssignmentCompatibleClasses(B.class); + assertThat(superclasses).containsExactly(B.class, InterfaceC.class, InterfaceA.class, InterfaceB.class, + A.class, InterfaceD.class, Object.class); + assertTrue(superclasses.stream().allMatch(clazz -> clazz.isAssignableFrom(B.class))); + } - assertFalse(ReflectionUtils.isAbstract(PublicClass.class)); - assertFalse(ReflectionUtils.isAbstract(PublicClass.class.getDeclaredMethod("publicMethod"))); - } + @Test + void newInstance() { + // @formatter:off + assertThat(ReflectionUtils.newInstance(C.class, "one", "two")).isNotNull(); + assertThat(ReflectionUtils.newInstance(C.class)).isNotNull(); - @Test - void isStatic() throws Exception { - assertTrue(ReflectionUtils.isStatic(StaticClass.class)); - assertTrue(ReflectionUtils.isStatic(StaticClass.class.getDeclaredMethod("staticMethod"))); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.newInstance(C.class, "one", null)); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.newInstance(C.class, null, "two")); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.newInstance(C.class, null, null)); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.newInstance(C.class, ((Object[]) null))); - assertFalse(ReflectionUtils.isStatic(PublicClass.class)); - assertFalse(ReflectionUtils.isStatic(PublicClass.class.getDeclaredMethod("publicMethod"))); - } + var exception = assertThrows(RuntimeException.class, () -> ReflectionUtils.newInstance(Exploder.class)); + assertThat(exception).hasMessage("boom"); + // @formatter:on + } - @Test - void isNotStatic() throws Exception { - assertTrue(ReflectionUtils.isNotStatic(PublicClass.class)); - assertTrue(ReflectionUtils.isNotStatic(PublicClass.class.getDeclaredMethod("publicMethod"))); + @Test + void wideningConversion() { + // byte + assertTrue(isWideningConversion(byte.class, short.class)); + assertTrue(isWideningConversion(byte.class, int.class)); + assertTrue(isWideningConversion(byte.class, long.class)); + assertTrue(isWideningConversion(byte.class, float.class)); + assertTrue(isWideningConversion(byte.class, double.class)); + // Byte + assertTrue(isWideningConversion(Byte.class, short.class)); + assertTrue(isWideningConversion(Byte.class, int.class)); + assertTrue(isWideningConversion(Byte.class, long.class)); + assertTrue(isWideningConversion(Byte.class, float.class)); + assertTrue(isWideningConversion(Byte.class, double.class)); + + // short + assertTrue(isWideningConversion(short.class, int.class)); + assertTrue(isWideningConversion(short.class, long.class)); + assertTrue(isWideningConversion(short.class, float.class)); + assertTrue(isWideningConversion(short.class, double.class)); + // Short + assertTrue(isWideningConversion(Short.class, int.class)); + assertTrue(isWideningConversion(Short.class, long.class)); + assertTrue(isWideningConversion(Short.class, float.class)); + assertTrue(isWideningConversion(Short.class, double.class)); + + // char + assertTrue(isWideningConversion(char.class, int.class)); + assertTrue(isWideningConversion(char.class, long.class)); + assertTrue(isWideningConversion(char.class, float.class)); + assertTrue(isWideningConversion(char.class, double.class)); + // Character + assertTrue(isWideningConversion(Character.class, int.class)); + assertTrue(isWideningConversion(Character.class, long.class)); + assertTrue(isWideningConversion(Character.class, float.class)); + assertTrue(isWideningConversion(Character.class, double.class)); + + // int + assertTrue(isWideningConversion(int.class, long.class)); + assertTrue(isWideningConversion(int.class, float.class)); + assertTrue(isWideningConversion(int.class, double.class)); + // Integer + assertTrue(isWideningConversion(Integer.class, long.class)); + assertTrue(isWideningConversion(Integer.class, float.class)); + assertTrue(isWideningConversion(Integer.class, double.class)); + + // long + assertTrue(isWideningConversion(long.class, float.class)); + assertTrue(isWideningConversion(long.class, double.class)); + // Long + assertTrue(isWideningConversion(Long.class, float.class)); + assertTrue(isWideningConversion(Long.class, double.class)); + + // float + assertTrue(isWideningConversion(float.class, double.class)); + // Float + assertTrue(isWideningConversion(Float.class, double.class)); + + // double and Double --> nothing to test + + // Unsupported + assertFalse(isWideningConversion(int.class, byte.class)); // narrowing + assertFalse(isWideningConversion(float.class, int.class)); // narrowing + assertFalse(isWideningConversion(int.class, int.class)); // direct match + assertFalse(isWideningConversion(String.class, int.class)); // neither a primitive nor a wrapper + } - assertFalse(ReflectionUtils.isNotStatic(StaticClass.class)); - assertFalse(ReflectionUtils.isNotStatic(StaticClass.class.getDeclaredMethod("staticMethod"))); - } + @Test + void getAllClasspathRootDirectories(@TempDir Path tempDirectory) throws Exception { + var root1 = tempDirectory.resolve("root1").toAbsolutePath(); + var root2 = tempDirectory.resolve("root2").toAbsolutePath(); + var testClassPath = root1 + File.pathSeparator + root2; - @Test - void isFinal() throws Exception { - assertTrue(ReflectionUtils.isFinal(FinalClass.class)); - assertTrue(ReflectionUtils.isFinal(FinalClass.class.getDeclaredMethod("finalMethod"))); + var originalClassPath = System.setProperty("java.class.path", testClassPath); + try { + createDirectories(root1, root2); - assertFalse(ReflectionUtils.isFinal(PublicClass.class)); - assertFalse(ReflectionUtils.isFinal(PublicClass.class.getDeclaredMethod("publicMethod"))); - } + assertThat(ReflectionUtils.getAllClasspathRootDirectories()).containsOnly(root1, root2); + } + finally { + System.setProperty("java.class.path", originalClassPath); + } + } - @Test - void isNotFinal() throws Exception { - assertTrue(ReflectionUtils.isNotFinal(PublicClass.class)); - assertTrue(ReflectionUtils.isNotFinal(PublicClass.class.getDeclaredMethod("publicMethod"))); + private static void createDirectories(Path... paths) throws IOException { + for (var path : paths) { + Files.createDirectory(path); + } + } - assertFalse(ReflectionUtils.isNotFinal(FinalClass.class)); - assertFalse(ReflectionUtils.isNotFinal(FinalClass.class.getDeclaredMethod("finalMethod"))); - } + @Test + void getDeclaredConstructorPreconditions() { + // @formatter:off + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getDeclaredConstructor(null)); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getDeclaredConstructor(ClassWithTwoConstructors.class)); + // @formatter:on + } - @Test - void returnsVoid() throws Exception { - Class clazz = ClassWithVoidAndNonVoidMethods.class; - assertTrue(ReflectionUtils.returnsVoid(clazz.getDeclaredMethod("voidMethod"))); + @Test + void getDeclaredConstructor() { + Constructor constructor = ReflectionUtils.getDeclaredConstructor(getClass()); + assertNotNull(constructor); + assertEquals(getClass(), constructor.getDeclaringClass()); - assertFalse(ReflectionUtils.returnsVoid(clazz.getDeclaredMethod("methodReturningVoidReference"))); - assertFalse(ReflectionUtils.returnsVoid(clazz.getDeclaredMethod("methodReturningObject"))); - assertFalse(ReflectionUtils.returnsVoid(clazz.getDeclaredMethod("methodReturningPrimitive"))); - } + constructor = ReflectionUtils.getDeclaredConstructor(ClassWithOneCustomConstructor.class); + assertNotNull(constructor); + assertEquals(ClassWithOneCustomConstructor.class, constructor.getDeclaringClass()); + assertEquals(String.class, constructor.getParameterTypes()[0]); + } - @Test - void getAllAssignmentCompatibleClassesWithNullClass() { - assertThrows(PreconditionViolationException.class, - () -> ReflectionUtils.getAllAssignmentCompatibleClasses(null)); - } + @Test + void isGeneric() { + for (var method : Generic.class.getMethods()) { + assertTrue(ReflectionUtils.isGeneric(method)); + } + for (var method : NonGenericClass.class.getMethods()) { + assertFalse(ReflectionUtils.isGeneric(method)); + } + } - @Test - void getAllAssignmentCompatibleClasses() { - var superclasses = ReflectionUtils.getAllAssignmentCompatibleClasses(B.class); - assertThat(superclasses).containsExactly(B.class, InterfaceC.class, InterfaceA.class, InterfaceB.class, A.class, - InterfaceD.class, Object.class); - assertTrue(superclasses.stream().allMatch(clazz -> clazz.isAssignableFrom(B.class))); - } + /** + * @see #3684 + */ + @Test + void getInterfaceMethodIfPossible() throws Exception { + // "anonymous" because it's implemented as an anonymous class. + InputStream anonymousInputStream = InputStream.nullInputStream(); + Class targetType = anonymousInputStream.getClass(); + assertThat(targetType.isAnonymousClass()).isTrue(); - @Test - void newInstance() { - // @formatter:off - assertThat(ReflectionUtils.newInstance(C.class, "one", "two")).isNotNull(); - assertThat(ReflectionUtils.newInstance(C.class)).isNotNull(); + Method method = targetType.getMethod("close"); + assertThat(method).isNotNull(); + assertThat(method.getDeclaringClass()).isEqualTo(targetType); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.newInstance(C.class, "one", null)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.newInstance(C.class, null, "two")); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.newInstance(C.class, null, null)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.newInstance(C.class, ((Object[]) null))); + Method interfaceMethod = ReflectionUtils.getInterfaceMethodIfPossible(method, targetType); + assertThat(interfaceMethod).isNotNull().isNotEqualTo(method); + // InputStream implements Closeable directly, so we find the `close` method + // in Closeable instead of AutoCloseable. + assertThat(interfaceMethod.getDeclaringClass()).isEqualTo(Closeable.class); + } - var exception = assertThrows(RuntimeException.class, () -> ReflectionUtils.newInstance(Exploder.class)); - assertThat(exception).hasMessage("boom"); - // @formatter:on - } + static class ClassWithVoidAndNonVoidMethods { - @Test - @SuppressWarnings("deprecation") - void readFieldValueOfNonexistentStaticField() { - assertThat(readFieldValue(MyClass.class, "doesNotExist", null)).isNotPresent(); - assertThat(readFieldValue(MySubClass.class, "staticField", null)).isNotPresent(); - } + void voidMethod() { + } - @Test - void tryToReadFieldValueOfNonexistentStaticField() { - assertThrows(NoSuchFieldException.class, () -> tryToReadFieldValue(MyClass.class, "doesNotExist", null).get()); - assertThrows(NoSuchFieldException.class, - () -> tryToReadFieldValue(MySubClass.class, "staticField", null).get()); - } + Void methodReturningVoidReference() { + return null; + } - @Test - @SuppressWarnings("deprecation") - void readFieldValueOfNonexistentInstanceField() { - assertThat(readFieldValue(MyClass.class, "doesNotExist", new MyClass(42))).isNotPresent(); - assertThat(readFieldValue(MyClass.class, "doesNotExist", new MySubClass(42))).isNotPresent(); - } + String methodReturningObject() { + return ""; + } - @Test - void tryToReadFieldValueOfNonexistentInstanceField() { - assertThrows(NoSuchFieldException.class, - () -> tryToReadFieldValue(MyClass.class, "doesNotExist", new MyClass(42)).get()); - assertThrows(NoSuchFieldException.class, - () -> tryToReadFieldValue(MyClass.class, "doesNotExist", new MySubClass(42)).get()); - } + int methodReturningPrimitive() { + return 0; + } - @Test - @SuppressWarnings("deprecation") - void readFieldValueOfExistingStaticField() throws Exception { - assertThat(readFieldValue(MyClass.class, "staticField", null)).contains(42); + } - var field = MyClass.class.getDeclaredField("staticField"); - assertThat(readFieldValue(field)).contains(42); - assertThat(readFieldValue(field, null)).contains(42); - } + static class Exploder { - @Test - void tryToReadFieldValueOfExistingStaticField() throws Exception { - assertThat(tryToReadFieldValue(MyClass.class, "staticField", null).get()).isEqualTo(42); + Exploder() { + throw new RuntimeException("boom"); + } - var field = MyClass.class.getDeclaredField("staticField"); - assertThat(tryToReadFieldValue(field).get()).isEqualTo(42); - assertThat(tryToReadFieldValue(field, null).get()).isEqualTo(42); - } + } - @Test - @SuppressWarnings("deprecation") - void readFieldValueOfExistingInstanceField() throws Exception { - var instance = new MyClass(42); - assertThat(readFieldValue(MyClass.class, "instanceField", instance)).contains(42); + interface InterfaceA { + } - var field = MyClass.class.getDeclaredField("instanceField"); - assertThat(readFieldValue(field, instance)).contains(42); - } + interface InterfaceB { + } + + interface InterfaceC extends InterfaceA, InterfaceB { + } + + interface InterfaceD { + } + + static class A implements InterfaceA, InterfaceD { + } + + static class B extends A implements InterfaceC { + } + + static class C { + + C() { + } + + C(String a, String b) { + } + + } + + @SuppressWarnings("unused") + private static class ClassWithOneCustomConstructor { + + ClassWithOneCustomConstructor(String str) { + } + } + + @SuppressWarnings("unused") + private static class ClassWithTwoConstructors { + + ClassWithTwoConstructors() { + } + + ClassWithTwoConstructors(String str) { + } + } + + public class NonGenericClass { + + public void publicMethod() { + } + } - @Test - @SuppressWarnings("deprecation") - void attemptToReadFieldValueOfExistingInstanceFieldAsStaticField() throws Exception { - var field = MyClass.class.getDeclaredField("instanceField"); - Exception exception = assertThrows(PreconditionViolationException.class, () -> readFieldValue(field, null)); - assertThat(exception)// - .hasMessageStartingWith("Cannot read non-static field")// - .hasMessageEndingWith("on a null instance."); } - @Test - void tryToReadFieldValueOfExistingInstanceField() throws Exception { - var instance = new MyClass(42); - assertThat(tryToReadFieldValue(MyClass.class, "instanceField", instance).get()).isEqualTo(42); + @Nested + class ModifierTests { + + @Test + void isPublic() throws Exception { + assertTrue(ReflectionUtils.isPublic(PublicClass.class)); + assertTrue(ReflectionUtils.isPublic(PublicClass.class.getMethod("publicMethod"))); + + assertFalse(ReflectionUtils.isPublic(PrivateClass.class)); + assertFalse(ReflectionUtils.isPublic(PrivateClass.class.getDeclaredMethod("privateMethod"))); + assertFalse(ReflectionUtils.isPublic(ProtectedClass.class)); + assertFalse(ReflectionUtils.isPublic(ProtectedClass.class.getDeclaredMethod("protectedMethod"))); + assertFalse(ReflectionUtils.isPublic(PackageVisibleClass.class)); + assertFalse(ReflectionUtils.isPublic(PackageVisibleClass.class.getDeclaredMethod("packageVisibleMethod"))); + } + + @Test + void isPrivate() throws Exception { + assertTrue(ReflectionUtils.isPrivate(PrivateClass.class)); + assertTrue(ReflectionUtils.isPrivate(PrivateClass.class.getDeclaredMethod("privateMethod"))); + + assertFalse(ReflectionUtils.isPrivate(PublicClass.class)); + assertFalse(ReflectionUtils.isPrivate(PublicClass.class.getMethod("publicMethod"))); + assertFalse(ReflectionUtils.isPrivate(ProtectedClass.class)); + assertFalse(ReflectionUtils.isPrivate(ProtectedClass.class.getDeclaredMethod("protectedMethod"))); + assertFalse(ReflectionUtils.isPrivate(PackageVisibleClass.class)); + assertFalse(ReflectionUtils.isPrivate(PackageVisibleClass.class.getDeclaredMethod("packageVisibleMethod"))); + } + + @Test + void isNotPrivate() throws Exception { + assertTrue(ReflectionUtils.isNotPrivate(PublicClass.class)); + assertTrue(ReflectionUtils.isNotPrivate(PublicClass.class.getDeclaredMethod("publicMethod"))); + assertTrue(ReflectionUtils.isNotPrivate(ProtectedClass.class)); + assertTrue(ReflectionUtils.isNotPrivate(ProtectedClass.class.getDeclaredMethod("protectedMethod"))); + assertTrue(ReflectionUtils.isNotPrivate(PackageVisibleClass.class)); + assertTrue( + ReflectionUtils.isNotPrivate(PackageVisibleClass.class.getDeclaredMethod("packageVisibleMethod"))); + + assertFalse(ReflectionUtils.isNotPrivate(PrivateClass.class.getDeclaredMethod("privateMethod"))); + } + + @Test + void isAbstract() throws Exception { + assertTrue(ReflectionUtils.isAbstract(AbstractClass.class)); + assertTrue(ReflectionUtils.isAbstract(AbstractClass.class.getDeclaredMethod("abstractMethod"))); + + assertFalse(ReflectionUtils.isAbstract(PublicClass.class)); + assertFalse(ReflectionUtils.isAbstract(PublicClass.class.getDeclaredMethod("publicMethod"))); + } + + @Test + void isStatic() throws Exception { + assertTrue(ReflectionUtils.isStatic(StaticClass.class)); + assertTrue(ReflectionUtils.isStatic(StaticClass.class.getDeclaredMethod("staticMethod"))); + + assertFalse(ReflectionUtils.isStatic(PublicClass.class)); + assertFalse(ReflectionUtils.isStatic(PublicClass.class.getDeclaredMethod("publicMethod"))); + } + + @Test + void isNotStatic() throws Exception { + assertTrue(ReflectionUtils.isNotStatic(PublicClass.class)); + assertTrue(ReflectionUtils.isNotStatic(PublicClass.class.getDeclaredMethod("publicMethod"))); + + assertFalse(ReflectionUtils.isNotStatic(StaticClass.class)); + assertFalse(ReflectionUtils.isNotStatic(StaticClass.class.getDeclaredMethod("staticMethod"))); + } + + @Test + void isFinal() throws Exception { + assertTrue(ReflectionUtils.isFinal(FinalClass.class)); + assertTrue(ReflectionUtils.isFinal(FinalClass.class.getDeclaredMethod("finalMethod"))); + + assertFalse(ReflectionUtils.isFinal(PublicClass.class)); + assertFalse(ReflectionUtils.isFinal(PublicClass.class.getDeclaredMethod("publicMethod"))); + } + + @Test + void isNotFinal() throws Exception { + assertTrue(ReflectionUtils.isNotFinal(PublicClass.class)); + assertTrue(ReflectionUtils.isNotFinal(PublicClass.class.getDeclaredMethod("publicMethod"))); + + assertFalse(ReflectionUtils.isNotFinal(FinalClass.class)); + assertFalse(ReflectionUtils.isNotFinal(FinalClass.class.getDeclaredMethod("finalMethod"))); + } + + // Intentionally non-static + public class PublicClass { + + public void publicMethod() { + } + + public void method(String str, Integer num) { + } + + public void method(String[] strings, Integer[] nums) { + } + + public void method(boolean b, char c) { + } + + public void method(char[] characters, int[] nums) { + } + } + + private class PrivateClass { + + @SuppressWarnings("unused") + private void privateMethod() { + } + } + + protected class ProtectedClass { + + @SuppressWarnings("unused") + protected void protectedMethod() { + } + } + + class PackageVisibleClass { + + @SuppressWarnings("unused") + void packageVisibleMethod() { + } + } + + final class FinalClass { + + @SuppressWarnings("unused") + final void finalMethod() { + } + } + + abstract static class AbstractClass { + + abstract void abstractMethod(); + } + + static class StaticClass { + + static void staticMethod() { + } + } - var field = MyClass.class.getDeclaredField("instanceField"); - assertThat(tryToReadFieldValue(field, instance).get()).isEqualTo(42); - assertThrows(PreconditionViolationException.class, () -> tryToReadFieldValue(field, null).get()); } @Nested @@ -419,1362 +643,1456 @@ void isAssignableToForNullObjectAndPrimitive() { } - @Test - void wideningConversion() { - // byte - assertTrue(ReflectionUtils.isWideningConversion(byte.class, short.class)); - assertTrue(ReflectionUtils.isWideningConversion(byte.class, int.class)); - assertTrue(ReflectionUtils.isWideningConversion(byte.class, long.class)); - assertTrue(ReflectionUtils.isWideningConversion(byte.class, float.class)); - assertTrue(ReflectionUtils.isWideningConversion(byte.class, double.class)); - // Byte - assertTrue(ReflectionUtils.isWideningConversion(Byte.class, short.class)); - assertTrue(ReflectionUtils.isWideningConversion(Byte.class, int.class)); - assertTrue(ReflectionUtils.isWideningConversion(Byte.class, long.class)); - assertTrue(ReflectionUtils.isWideningConversion(Byte.class, float.class)); - assertTrue(ReflectionUtils.isWideningConversion(Byte.class, double.class)); - - // short - assertTrue(ReflectionUtils.isWideningConversion(short.class, int.class)); - assertTrue(ReflectionUtils.isWideningConversion(short.class, long.class)); - assertTrue(ReflectionUtils.isWideningConversion(short.class, float.class)); - assertTrue(ReflectionUtils.isWideningConversion(short.class, double.class)); - // Short - assertTrue(ReflectionUtils.isWideningConversion(Short.class, int.class)); - assertTrue(ReflectionUtils.isWideningConversion(Short.class, long.class)); - assertTrue(ReflectionUtils.isWideningConversion(Short.class, float.class)); - assertTrue(ReflectionUtils.isWideningConversion(Short.class, double.class)); - - // char - assertTrue(ReflectionUtils.isWideningConversion(char.class, int.class)); - assertTrue(ReflectionUtils.isWideningConversion(char.class, long.class)); - assertTrue(ReflectionUtils.isWideningConversion(char.class, float.class)); - assertTrue(ReflectionUtils.isWideningConversion(char.class, double.class)); - // Character - assertTrue(ReflectionUtils.isWideningConversion(Character.class, int.class)); - assertTrue(ReflectionUtils.isWideningConversion(Character.class, long.class)); - assertTrue(ReflectionUtils.isWideningConversion(Character.class, float.class)); - assertTrue(ReflectionUtils.isWideningConversion(Character.class, double.class)); - - // int - assertTrue(ReflectionUtils.isWideningConversion(int.class, long.class)); - assertTrue(ReflectionUtils.isWideningConversion(int.class, float.class)); - assertTrue(ReflectionUtils.isWideningConversion(int.class, double.class)); - // Integer - assertTrue(ReflectionUtils.isWideningConversion(Integer.class, long.class)); - assertTrue(ReflectionUtils.isWideningConversion(Integer.class, float.class)); - assertTrue(ReflectionUtils.isWideningConversion(Integer.class, double.class)); - - // long - assertTrue(ReflectionUtils.isWideningConversion(long.class, float.class)); - assertTrue(ReflectionUtils.isWideningConversion(long.class, double.class)); - // Long - assertTrue(ReflectionUtils.isWideningConversion(Long.class, float.class)); - assertTrue(ReflectionUtils.isWideningConversion(Long.class, double.class)); - - // float - assertTrue(ReflectionUtils.isWideningConversion(float.class, double.class)); - // Float - assertTrue(ReflectionUtils.isWideningConversion(Float.class, double.class)); - - // double and Double --> nothing to test - - // Unsupported - assertFalse(ReflectionUtils.isWideningConversion(int.class, byte.class)); // narrowing - assertFalse(ReflectionUtils.isWideningConversion(float.class, int.class)); // narrowing - assertFalse(ReflectionUtils.isWideningConversion(int.class, int.class)); // direct match - assertFalse(ReflectionUtils.isWideningConversion(String.class, int.class)); // neither a primitive nor a wrapper - } + @Nested + class MethodInvocationTests { - @Test - void invokeMethodPreconditions() { - // @formatter:off - assertThrows(PreconditionViolationException.class, () -> invokeMethod(null, new Object())); - assertThrows(PreconditionViolationException.class, () -> invokeMethod(Object.class.getMethod("hashCode"), null)); - // @formatter:on - } + @Test + void invokeMethodPreconditions() { + // @formatter:off + assertThrows(PreconditionViolationException.class, () -> invokeMethod(null, new Object())); + assertThrows(PreconditionViolationException.class, () -> invokeMethod(Object.class.getMethod("hashCode"), null)); + // @formatter:on + } - @Test - void invokePublicMethod() throws Exception { - var tracker = new InvocationTracker(); - invokeMethod(InvocationTracker.class.getDeclaredMethod("publicMethod"), tracker); - assertTrue(tracker.publicMethodInvoked); - } + @Test + void invokePublicMethod() throws Exception { + var tracker = new InvocationTracker(); + invokeMethod(InvocationTracker.class.getDeclaredMethod("publicMethod"), tracker); + assertTrue(tracker.publicMethodInvoked); + } - @Test - void invokePrivateMethod() throws Exception { - var tracker = new InvocationTracker(); - invokeMethod(InvocationTracker.class.getDeclaredMethod("privateMethod"), tracker); - assertTrue(tracker.privateMethodInvoked); - } + @Test + void invokePrivateMethod() throws Exception { + var tracker = new InvocationTracker(); + invokeMethod(InvocationTracker.class.getDeclaredMethod("privateMethod"), tracker); + assertTrue(tracker.privateMethodInvoked); + } - @Test - void invokePublicStaticMethod() throws Exception { - invokeMethod(InvocationTracker.class.getDeclaredMethod("publicStaticMethod"), null); - assertTrue(InvocationTracker.publicStaticMethodInvoked); - } + @Test + void invokePublicStaticMethod() throws Exception { + invokeMethod(InvocationTracker.class.getDeclaredMethod("publicStaticMethod"), null); + assertTrue(InvocationTracker.publicStaticMethodInvoked); + } - @Test - void invokePrivateStaticMethod() throws Exception { - invokeMethod(InvocationTracker.class.getDeclaredMethod("privateStaticMethod"), null); - assertTrue(InvocationTracker.privateStaticMethodInvoked); - } + @Test + void invokePrivateStaticMethod() throws Exception { + invokeMethod(InvocationTracker.class.getDeclaredMethod("privateStaticMethod"), null); + assertTrue(InvocationTracker.privateStaticMethodInvoked); + } - @Test - void tryToLoadClassPreconditions() { - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToLoadClass(null)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToLoadClass("")); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToLoadClass(" ")); + static class InvocationTracker { - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToLoadClass(null, null)); - assertThrows(PreconditionViolationException.class, - () -> ReflectionUtils.tryToLoadClass(getClass().getName(), null)); - } + static boolean publicStaticMethodInvoked; + static boolean privateStaticMethodInvoked; - @Test - @SuppressWarnings("deprecation") - void loadClassWhenClassNotFoundException() { - assertThat(ReflectionUtils.loadClass("foo.bar.EnigmaClassThatDoesNotExist")).isEmpty(); - } + boolean publicMethodInvoked; + boolean privateMethodInvoked; - @Test - void tryToLoadClassWhenClassNotFoundException() { - assertThrows(ClassNotFoundException.class, - () -> ReflectionUtils.tryToLoadClass("foo.bar.EnigmaClassThatDoesNotExist").get()); - } + public static void publicStaticMethod() { + publicStaticMethodInvoked = true; + } - @Test - void tryToLoadClassFailsWithinReasonableTimeForInsanelyLargeAndInvalidMultidimensionalPrimitiveArrayName() { - // Create a class name of the form int[][][]...[][][]X - String className = IntStream.rangeClosed(1, 20_000)// - .mapToObj(i -> "[]")// - .collect(joining("", "int", "X")); - - // The following should ideally fail in less than 50ms. So we just make - // sure it fails in less than 500ms in order to (hopefully) allow the - // test to pass on CI servers with limited resources. - assertTimeoutPreemptively(ofMillis(500), - () -> assertThrows(ClassNotFoundException.class, () -> ReflectionUtils.tryToLoadClass(className).get())); - } + @SuppressWarnings("unused") + private static void privateStaticMethod() { + privateStaticMethodInvoked = true; + } - @Test - @SuppressWarnings("deprecation") - void loadClass() { - var optional = ReflectionUtils.loadClass(Integer.class.getName()); - assertThat(optional).contains(Integer.class); - } + public void publicMethod() { + publicMethodInvoked = true; + } - @Test - void tryToLoadClass() { - assertThat(ReflectionUtils.tryToLoadClass(Integer.class.getName())).isEqualTo(success(Integer.class)); - } + @SuppressWarnings("unused") + private void privateMethod() { + privateMethodInvoked = true; + } + } - @Test - void tryToLoadClassTrimsClassName() { - assertThat(ReflectionUtils.tryToLoadClass(" " + Integer.class.getName() + "\t")).isEqualTo( - success(Integer.class)); } - @Test - void tryToLoadClassForPrimitive() { - assertThat(ReflectionUtils.tryToLoadClass(int.class.getName())).isEqualTo(success(int.class)); - } + @Nested + class ClassLoadingTests { - @Test - void tryToLoadClassForPrimitiveArray() { - assertThat(ReflectionUtils.tryToLoadClass(int[].class.getName())).isEqualTo(success(int[].class)); - } + @Test + void tryToLoadClassPreconditions() { + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToLoadClass(null)); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToLoadClass("")); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToLoadClass(" ")); - @Test - void tryToLoadClassForPrimitiveArrayUsingSourceCodeSyntax() { - assertThat(ReflectionUtils.tryToLoadClass("int[]")).isEqualTo(success(int[].class)); - } + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToLoadClass(null, null)); + assertThrows(PreconditionViolationException.class, + () -> ReflectionUtils.tryToLoadClass(getClass().getName(), null)); + } - @Test - void tryToLoadClassForObjectArray() { - assertThat(ReflectionUtils.tryToLoadClass(String[].class.getName())).isEqualTo(success(String[].class)); - } + @Test + @SuppressWarnings("deprecation") + void loadClassWhenClassNotFoundException() { + assertThat(ReflectionUtils.loadClass("foo.bar.EnigmaClassThatDoesNotExist")).isEmpty(); + } - @Test - void tryToLoadClassForObjectArrayUsingSourceCodeSyntax() { - assertThat(ReflectionUtils.tryToLoadClass("java.lang.String[]")).isEqualTo(success(String[].class)); - } + @Test + void tryToLoadClassWhenClassNotFoundException() { + assertThrows(ClassNotFoundException.class, + () -> ReflectionUtils.tryToLoadClass("foo.bar.EnigmaClassThatDoesNotExist").get()); + } - @Test - void tryToLoadClassForTwoDimensionalPrimitiveArray() { - assertThat(ReflectionUtils.tryToLoadClass(int[][].class.getName())).isEqualTo(success(int[][].class)); - } + @Test + void tryToLoadClassFailsWithinReasonableTimeForInsanelyLargeAndInvalidMultidimensionalPrimitiveArrayName() { + // Create a class name of the form int[][][]...[][][]X + String className = IntStream.rangeClosed(1, 20_000)// + .mapToObj(i -> "[]")// + .collect(joining("", "int", "X")); - @Test - void tryToLoadClassForTwoDimensionaldimensionalPrimitiveArrayUsingSourceCodeSyntax() { - assertThat(ReflectionUtils.tryToLoadClass("int[][]")).isEqualTo(success(int[][].class)); - } + // The following should ideally fail in less than 50ms. So we just make + // sure it fails in less than 500ms in order to (hopefully) allow the + // test to pass on CI servers with limited resources. + assertTimeoutPreemptively(ofMillis(500), () -> assertThrows(ClassNotFoundException.class, + () -> ReflectionUtils.tryToLoadClass(className).get())); + } - @Test - void tryToLoadClassForMultidimensionalPrimitiveArray() { - assertThat(ReflectionUtils.tryToLoadClass(int[][][][][].class.getName())).isEqualTo( - success(int[][][][][].class)); - } + @Test + @SuppressWarnings("deprecation") + void loadClass() { + var optional = ReflectionUtils.loadClass(Integer.class.getName()); + assertThat(optional).contains(Integer.class); + } - @Test - void tryToLoadClassForMultidimensionalPrimitiveArrayUsingSourceCodeSyntax() { - assertThat(ReflectionUtils.tryToLoadClass("int[][][][][]")).isEqualTo(success(int[][][][][].class)); - } + @Test + void tryToLoadClass() { + assertThat(ReflectionUtils.tryToLoadClass(Integer.class.getName())).isEqualTo(success(Integer.class)); + } - @Test - void tryToLoadClassForMultidimensionalObjectArray() { - assertThat(ReflectionUtils.tryToLoadClass(String[][][][][].class.getName())).isEqualTo( - success(String[][][][][].class)); - } + @Test + void tryToLoadClassTrimsClassName() { + assertThat(ReflectionUtils.tryToLoadClass(" " + Integer.class.getName() + "\t"))// + .isEqualTo(success(Integer.class)); + } - @Test - void tryToLoadClassForMultidimensionalObjectArrayUsingSourceCodeSyntax() { - assertThat(ReflectionUtils.tryToLoadClass("java.lang.String[][][][][]")).isEqualTo( - success(String[][][][][].class)); - } + @Test + void tryToLoadClassForPrimitive() { + assertThat(ReflectionUtils.tryToLoadClass(int.class.getName())).isEqualTo(success(int.class)); + } - @Test - void getFullyQualifiedMethodNamePreconditions() { - // @formatter:off - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getFullyQualifiedMethodName(null, null)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getFullyQualifiedMethodName(null, "testMethod")); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getFullyQualifiedMethodName(Object.class, null)); - // @formatter:on - } + @Test + void tryToLoadClassForPrimitiveArray() { + assertThat(ReflectionUtils.tryToLoadClass(int[].class.getName())).isEqualTo(success(int[].class)); + } - @Test - void getFullyQualifiedMethodNameForMethodWithoutParameters() { - assertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, "toString"))// - .isEqualTo("java.lang.Object#toString()"); - } + @Test + void tryToLoadClassForPrimitiveArrayUsingSourceCodeSyntax() { + assertThat(ReflectionUtils.tryToLoadClass("int[]")).isEqualTo(success(int[].class)); + } - @Test - void getFullyQualifiedMethodNameForMethodWithNullParameters() { - assertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, "toString", (Class[]) null))// - .isEqualTo("java.lang.Object#toString()"); - } + @Test + void tryToLoadClassForObjectArray() { + assertThat(ReflectionUtils.tryToLoadClass(String[].class.getName())).isEqualTo(success(String[].class)); + } - @Test - void getFullyQualifiedMethodNameForMethodWithSingleParameter() { - assertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, "equals", Object.class))// - .isEqualTo("java.lang.Object#equals(java.lang.Object)"); - } + @Test + void tryToLoadClassForObjectArrayUsingSourceCodeSyntax() { + assertThat(ReflectionUtils.tryToLoadClass("java.lang.String[]")).isEqualTo(success(String[].class)); + } - @Test - void getFullyQualifiedMethodNameForMethodWithMultipleParameters() { - // @formatter:off - assertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, "testMethod", int.class, Object.class))// - .isEqualTo("java.lang.Object#testMethod(int, java.lang.Object)"); - // @formatter:on - } + @Test + void tryToLoadClassForTwoDimensionalPrimitiveArray() { + assertThat(ReflectionUtils.tryToLoadClass(int[][].class.getName())).isEqualTo(success(int[][].class)); + } - @Test - void parseFullyQualifiedMethodNamePreconditions() { - // @formatter:off - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName(null)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("")); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName(" ")); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("java.lang.Object#")); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("#equals")); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("#")); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("java.lang.Object")); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("equals")); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("()")); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("(int, java.lang.Object)")); - // @formatter:on - } + @Test + void tryToLoadClassForTwoDimensionaldimensionalPrimitiveArrayUsingSourceCodeSyntax() { + assertThat(ReflectionUtils.tryToLoadClass("int[][]")).isEqualTo(success(int[][].class)); + } - @Test - void parseFullyQualifiedMethodNameForMethodWithoutParameters() { - assertThat(ReflectionUtils.parseFullyQualifiedMethodName("com.example.Test#method()"))// - .containsExactly("com.example.Test", "method", ""); - } + @Test + void tryToLoadClassForMultidimensionalPrimitiveArray() { + assertThat(ReflectionUtils.tryToLoadClass(int[][][][][].class.getName()))// + .isEqualTo(success(int[][][][][].class)); + } - @Test - void parseFullyQualifiedMethodNameForMethodWithSingleParameter() { - assertThat(ReflectionUtils.parseFullyQualifiedMethodName("com.example.Test#method(java.lang.Object)"))// - .containsExactly("com.example.Test", "method", "java.lang.Object"); - } + @Test + void tryToLoadClassForMultidimensionalPrimitiveArrayUsingSourceCodeSyntax() { + assertThat(ReflectionUtils.tryToLoadClass("int[][][][][]")).isEqualTo(success(int[][][][][].class)); + } - @Test - void parseFullyQualifiedMethodNameForMethodWithMultipleParameters() { - assertThat(ReflectionUtils.parseFullyQualifiedMethodName("com.example.Test#method(int, java.lang.Object)"))// - .containsExactly("com.example.Test", "method", "int, java.lang.Object"); - } + @Test + void tryToLoadClassForMultidimensionalObjectArray() { + assertThat(ReflectionUtils.tryToLoadClass(String[][][][][].class.getName()))// + .isEqualTo(success(String[][][][][].class)); + } - @Test - @SuppressWarnings("deprecation") - void getOutermostInstancePreconditions() { - // @formatter:off - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getOutermostInstance(null, null)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getOutermostInstance(null, Object.class)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getOutermostInstance(new Object(), null)); - // @formatter:on - } + @Test + void tryToLoadClassForMultidimensionalObjectArrayUsingSourceCodeSyntax() { + assertThat(ReflectionUtils.tryToLoadClass("java.lang.String[][][][][]"))// + .isEqualTo(success(String[][][][][].class)); + } - @Test - @SuppressWarnings("deprecation") - void getOutermostInstance() { - var firstClass = new FirstClass(); - var secondClass = firstClass.new SecondClass(); - var thirdClass = secondClass.new ThirdClass(); - - assertThat(ReflectionUtils.getOutermostInstance(thirdClass, FirstClass.SecondClass.ThirdClass.class))// - .contains(thirdClass); - assertThat(ReflectionUtils.getOutermostInstance(thirdClass, FirstClass.SecondClass.class))// - .contains(secondClass); - assertThat(ReflectionUtils.getOutermostInstance(thirdClass, FirstClass.class)).contains(firstClass); - assertThat(ReflectionUtils.getOutermostInstance(thirdClass, String.class)).isEmpty(); } - @Test - void getAllClasspathRootDirectories(@TempDir Path tempDirectory) throws Exception { - var root1 = tempDirectory.resolve("root1").toAbsolutePath(); - var root2 = tempDirectory.resolve("root2").toAbsolutePath(); - var testClassPath = root1 + File.pathSeparator + root2; - - var originalClassPath = System.setProperty("java.class.path", testClassPath); - try { - createDirectories(root1, root2); + @Nested + class FullyQualifiedMethodNameTests { - assertThat(ReflectionUtils.getAllClasspathRootDirectories()).containsOnly(root1, root2); + @Test + void getFullyQualifiedMethodNamePreconditions() { + // @formatter:off + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getFullyQualifiedMethodName(null, null)); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getFullyQualifiedMethodName(null, "testMethod")); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getFullyQualifiedMethodName(Object.class, null)); + // @formatter:on } - finally { - System.setProperty("java.class.path", originalClassPath); + + @Test + void getFullyQualifiedMethodNameForMethodWithoutParameters() { + assertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, "toString"))// + .isEqualTo("java.lang.Object#toString()"); } - } - @Test - void findNestedClassesPreconditions() { - // @formatter:off - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.findNestedClasses(null, null)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.findNestedClasses(null, clazz -> true)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.findNestedClasses(FirstClass.class, null)); - // @formatter:on - } + @Test + void getFullyQualifiedMethodNameForMethodWithNullParameters() { + assertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, "toString", (Class[]) null))// + .isEqualTo("java.lang.Object#toString()"); + } - @Test - void findNestedClasses() { - // @formatter:off - assertThat(findNestedClasses(Object.class)).isEmpty(); + @Test + void getFullyQualifiedMethodNameForMethodWithSingleParameter() { + assertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, "equals", Object.class))// + .isEqualTo("java.lang.Object#equals(java.lang.Object)"); + } - assertThat(findNestedClasses(ClassWithNestedClasses.class)) - .containsOnly(Nested1.class, Nested2.class, Nested3.class); + @Test + void getFullyQualifiedMethodNameForMethodWithMultipleParameters() { + // @formatter:off + assertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, "testMethod", int.class, Object.class))// + .isEqualTo("java.lang.Object#testMethod(int, java.lang.Object)"); + // @formatter:on + } - assertThat(ReflectionUtils.findNestedClasses(ClassWithNestedClasses.class, clazz -> clazz.getName().contains("1"))) - .containsExactly(Nested1.class); + @Test + void parseFullyQualifiedMethodNamePreconditions() { + // @formatter:off + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName(null)); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("")); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName(" ")); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("java.lang.Object#")); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("#equals")); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("#")); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("java.lang.Object")); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("equals")); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("()")); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.parseFullyQualifiedMethodName("(int, java.lang.Object)")); + // @formatter:on + } - assertThat(ReflectionUtils.findNestedClasses(ClassWithNestedClasses.class, ReflectionUtils::isStatic)) - .containsExactly(Nested3.class); + @Test + void parseFullyQualifiedMethodNameForMethodWithoutParameters() { + assertThat(ReflectionUtils.parseFullyQualifiedMethodName("com.example.Test#method()"))// + .containsExactly("com.example.Test", "method", ""); + } - assertThat(findNestedClasses(ClassExtendingClassWithNestedClasses.class)) - .containsOnly(Nested1.class, Nested2.class, Nested3.class, Nested4.class, Nested5.class); + @Test + void parseFullyQualifiedMethodNameForMethodWithSingleParameter() { + assertThat(ReflectionUtils.parseFullyQualifiedMethodName("com.example.Test#method(java.lang.Object)"))// + .containsExactly("com.example.Test", "method", "java.lang.Object"); + } - assertThat(findNestedClasses(ClassWithNestedClasses.Nested1.class)).isEmpty(); - // @formatter:on - } + @Test + void parseFullyQualifiedMethodNameForMethodWithMultipleParameters() { + assertThat(ReflectionUtils.parseFullyQualifiedMethodName("com.example.Test#method(int, java.lang.Object)"))// + .containsExactly("com.example.Test", "method", "int, java.lang.Object"); + } - /** - * @since 1.6 - */ - @Test - void findNestedClassesWithSeeminglyRecursiveHierarchies() { - assertThat(findNestedClasses(AbstractOuterClass.class))// - .containsExactly(AbstractOuterClass.InnerClass.class); - - // OuterClass contains recursive hierarchies, but the non-matching - // predicate should prevent cycle detection. - // See https://github.com/junit-team/junit5/issues/2249 - assertThat(ReflectionUtils.findNestedClasses(OuterClass.class, clazz -> false)).isEmpty(); - // RecursiveInnerInnerClass is part of a recursive hierarchy, but the non-matching - // predicate should prevent cycle detection. - assertThat(ReflectionUtils.findNestedClasses(RecursiveInnerInnerClass.class, clazz -> false)).isEmpty(); - - // Sibling types don't actually result in cycles. - assertThat(findNestedClasses(StaticNestedSiblingClass.class))// - .containsExactly(AbstractOuterClass.InnerClass.class); - assertThat(findNestedClasses(InnerSiblingClass.class))// - .containsExactly(AbstractOuterClass.InnerClass.class); - - // Interfaces with static nested classes - assertThat(findNestedClasses(OuterClassImplementingInterface.class))// - .containsExactly(InnerClassImplementingInterface.class, Nested4.class); - assertThat(findNestedClasses(InnerClassImplementingInterface.class))// - .containsExactly(Nested4.class); } - /** - * @since 1.6 - */ - @Test - void findNestedClassesWithRecursiveHierarchies() { - Runnable runnable1 = () -> assertNestedCycle(OuterClass.class, InnerClass.class, OuterClass.class); - Runnable runnable2 = () -> assertNestedCycle(StaticNestedClass.class, InnerClass.class, OuterClass.class); - Runnable runnable3 = () -> assertNestedCycle(RecursiveInnerClass.class, OuterClass.class); - Runnable runnable4 = () -> assertNestedCycle(RecursiveInnerInnerClass.class, OuterClass.class); - Runnable runnable5 = () -> assertNestedCycle(InnerClass.class, RecursiveInnerInnerClass.class, - OuterClass.class); - Stream.of(runnable1, runnable1, runnable1, runnable2, runnable2, runnable2, runnable3, runnable3, runnable3, - runnable4, runnable4, runnable4, runnable5, runnable5, runnable5).parallel().forEach(Runnable::run); - } + @Nested + class NestedClassTests { - private static List> findNestedClasses(Class clazz) { - return ReflectionUtils.findNestedClasses(clazz, c -> true); - } + @Test + void findNestedClassesPreconditions() { + // @formatter:off + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.findNestedClasses(null, null)); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.findNestedClasses(null, clazz -> true)); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.findNestedClasses(getClass(), null)); + // @formatter:on + } - private void assertNestedCycle(Class from, Class to) { - assertNestedCycle(from, from, to); - } + @Test + void findNestedClasses() { + // @formatter:off + assertThat(findNestedClasses(Object.class)).isEmpty(); - private void assertNestedCycle(Class start, Class from, Class to) { - assertThatExceptionOfType(JUnitException.class)// - .as("expected cycle from %s to %s", from.getSimpleName(), to.getSimpleName())// - .isThrownBy(() -> findNestedClasses(start))// - .withMessageMatching(String.format("Detected cycle in inner class hierarchy between .+%s and .+%s", - from.getSimpleName(), to.getSimpleName())); - } + assertThat(findNestedClasses(ClassWithNestedClasses.class)) + .containsOnly(Nested1.class, Nested2.class, Nested3.class); - /** - * @since 1.3 - */ - @Test - @TrackLogRecords - void findNestedClassesWithInvalidNestedClassFile(LogRecordListener listener) throws Exception { - var jarUrl = getClass().getResource("/gh-1436-invalid-nested-class-file.jar"); + assertThat(ReflectionUtils.findNestedClasses(ClassWithNestedClasses.class, clazz -> clazz.getName().contains("1"))) + .containsExactly(Nested1.class); - try (var classLoader = new URLClassLoader(new URL[] { jarUrl })) { - var fqcn = "tests.NestedInterfaceGroovyTests"; - var classWithInvalidNestedClassFile = classLoader.loadClass(fqcn); + assertThat(ReflectionUtils.findNestedClasses(ClassWithNestedClasses.class, ReflectionUtils::isStatic)) + .containsExactly(Nested3.class); - assertEquals(fqcn, classWithInvalidNestedClassFile.getName()); - var noClassDefFoundError = assertThrows(NoClassDefFoundError.class, - classWithInvalidNestedClassFile::getDeclaredClasses); - assertEquals("tests/NestedInterfaceGroovyTests$NestedInterface$1", noClassDefFoundError.getMessage()); + assertThat(findNestedClasses(ClassExtendingClassWithNestedClasses.class)) + .containsOnly(Nested1.class, Nested2.class, Nested3.class, Nested4.class, Nested5.class); - assertThat(findNestedClasses(classWithInvalidNestedClassFile)).isEmpty(); - // @formatter:off - var logMessage = listener.stream(ReflectionUtils.class, Level.FINE) - .findFirst() - .map(LogRecord::getMessage) - .orElse("didn't find log record"); + assertThat(findNestedClasses(ClassWithNestedClasses.Nested1.class)).isEmpty(); // @formatter:on - assertThat(logMessage).isEqualTo("Failed to retrieve declared classes for " + fqcn); } - } - - @Test - void getDeclaredConstructorPreconditions() { - // @formatter:off - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getDeclaredConstructor(null)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.getDeclaredConstructor(ClassWithTwoConstructors.class)); - // @formatter:on - } - - @Test - void getDeclaredConstructor() { - Constructor constructor = ReflectionUtils.getDeclaredConstructor(getClass()); - assertNotNull(constructor); - assertEquals(getClass(), constructor.getDeclaringClass()); - constructor = ReflectionUtils.getDeclaredConstructor(ClassWithOneCustomConstructor.class); - assertNotNull(constructor); - assertEquals(ClassWithOneCustomConstructor.class, constructor.getDeclaringClass()); - assertEquals(String.class, constructor.getParameterTypes()[0]); - } + /** + * @since 1.6 + */ + @Test + void findNestedClassesWithSeeminglyRecursiveHierarchies() { + assertThat(findNestedClasses(AbstractOuterClass.class))// + .containsExactly(AbstractOuterClass.InnerClass.class); + + // OuterClass contains recursive hierarchies, but the non-matching + // predicate should prevent cycle detection. + // See https://github.com/junit-team/junit5/issues/2249 + assertThat(ReflectionUtils.findNestedClasses(OuterClass.class, clazz -> false)).isEmpty(); + // RecursiveInnerInnerClass is part of a recursive hierarchy, but the non-matching + // predicate should prevent cycle detection. + assertThat(ReflectionUtils.findNestedClasses(RecursiveInnerInnerClass.class, clazz -> false)).isEmpty(); + + // Sibling types don't actually result in cycles. + assertThat(findNestedClasses(StaticNestedSiblingClass.class))// + .containsExactly(AbstractOuterClass.InnerClass.class); + assertThat(findNestedClasses(InnerSiblingClass.class))// + .containsExactly(AbstractOuterClass.InnerClass.class); + + // Interfaces with static nested classes + assertThat(findNestedClasses(OuterClassImplementingInterface.class))// + .containsExactly(InnerClassImplementingInterface.class, Nested4.class); + assertThat(findNestedClasses(InnerClassImplementingInterface.class))// + .containsExactly(Nested4.class); + } + + /** + * @since 1.6 + */ + @Test + void findNestedClassesWithRecursiveHierarchies() { + Runnable runnable1 = () -> assertNestedCycle(OuterClass.class, InnerClass.class, OuterClass.class); + Runnable runnable2 = () -> assertNestedCycle(StaticNestedClass.class, InnerClass.class, OuterClass.class); + Runnable runnable3 = () -> assertNestedCycle(RecursiveInnerClass.class, OuterClass.class); + Runnable runnable4 = () -> assertNestedCycle(RecursiveInnerInnerClass.class, OuterClass.class); + Runnable runnable5 = () -> assertNestedCycle(InnerClass.class, RecursiveInnerInnerClass.class, + OuterClass.class); + Stream.of(runnable1, runnable1, runnable1, runnable2, runnable2, runnable2, runnable3, runnable3, runnable3, + runnable4, runnable4, runnable4, runnable5, runnable5, runnable5).parallel().forEach(Runnable::run); + } - @Test - void tryToGetMethodPreconditions() { - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToGetMethod(null, null)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToGetMethod(String.class, null)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToGetMethod(null, "hashCode")); - } + private static List> findNestedClasses(Class clazz) { + return ReflectionUtils.findNestedClasses(clazz, c -> true); + } - @Test - void tryToGetMethod() throws Exception { - assertThat(ReflectionUtils.tryToGetMethod(Object.class, "hashCode").get()).isEqualTo( - Object.class.getMethod("hashCode")); - assertThat(ReflectionUtils.tryToGetMethod(String.class, "charAt", int.class).get())// - .isEqualTo(String.class.getMethod("charAt", int.class)); + private void assertNestedCycle(Class from, Class to) { + assertNestedCycle(from, from, to); + } - assertThat(ReflectionUtils.tryToGetMethod(Path.class, "subpath", int.class, int.class).get())// - .isEqualTo(Path.class.getMethod("subpath", int.class, int.class)); - assertThat(ReflectionUtils.tryToGetMethod(String.class, "chars").get()).isEqualTo( - String.class.getMethod("chars")); + private void assertNestedCycle(Class start, Class from, Class to) { + assertThatExceptionOfType(JUnitException.class)// + .as("expected cycle from %s to %s", from.getSimpleName(), to.getSimpleName())// + .isThrownBy(() -> findNestedClasses(start))// + .withMessageMatching(String.format("Detected cycle in inner class hierarchy between .+%s and .+%s", + from.getSimpleName(), to.getSimpleName())); + } - assertThat(ReflectionUtils.tryToGetMethod(String.class, "noSuchMethod").toOptional()).isEmpty(); - assertThat(ReflectionUtils.tryToGetMethod(Object.class, "clone", int.class).toOptional()).isEmpty(); - } + /** + * @since 1.3 + */ + @Test + void findNestedClassesWithInvalidNestedClassFile(@TrackLogRecords LogRecordListener listener) throws Exception { + var jarUrl = getClass().getResource("/gh-1436-invalid-nested-class-file.jar"); + + try (var classLoader = new URLClassLoader(new URL[] { jarUrl })) { + var fqcn = "tests.NestedInterfaceGroovyTests"; + var classWithInvalidNestedClassFile = classLoader.loadClass(fqcn); + + assertEquals(fqcn, classWithInvalidNestedClassFile.getName()); + var noClassDefFoundError = assertThrows(NoClassDefFoundError.class, + classWithInvalidNestedClassFile::getDeclaredClasses); + assertThat(noClassDefFoundError) // + .hasMessageMatching("tests[./]NestedInterfaceGroovyTests\\$NestedInterface\\$1"); + + assertThat(findNestedClasses(classWithInvalidNestedClassFile)).isEmpty(); + // @formatter:off + var logMessage = listener.stream(ReflectionUtils.class, Level.FINE) + .findFirst() + .map(LogRecord::getMessage) + .orElse("didn't find log record"); + // @formatter:on + assertThat(logMessage).isEqualTo("Failed to retrieve declared classes for " + fqcn); + } + } - @Test - void isMethodPresentPreconditions() { - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.isMethodPresent(null, m -> true)); - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.isMethodPresent(getClass(), null)); - } + static class ClassWithNestedClasses { - @Test - void isMethodPresent() { - Predicate isMethod1 = method -> (method.getName().equals("method1") - && method.getParameterTypes().length == 1 && method.getParameterTypes()[0] == String.class); + class Nested1 { + } - assertThat(ReflectionUtils.isMethodPresent(MethodShadowingChild.class, isMethod1)).isTrue(); + class Nested2 { + } - assertThat(ReflectionUtils.isMethodPresent(getClass(), isMethod1)).isFalse(); - } + static class Nested3 { + } + } - @Test - void findMethodByParameterTypesPreconditions() { - // @formatter:off - assertThrows(PreconditionViolationException.class, () -> findMethod(null, null)); - assertThrows(PreconditionViolationException.class, () -> findMethod(null, "method")); + interface InterfaceWithNestedClass { - RuntimeException exception = assertThrows(PreconditionViolationException.class, () -> findMethod(String.class, null)); - assertThat(exception).hasMessage("Method name must not be null or blank"); + class Nested4 { + } + } - exception = assertThrows(PreconditionViolationException.class, () -> findMethod(String.class, " ")); - assertThat(exception).hasMessage("Method name must not be null or blank"); + interface Interface45 extends InterfaceWithNestedClass { - exception = assertThrows(PreconditionViolationException.class, () -> findMethod(Files.class, "copy", (Class[]) null)); - assertThat(exception).hasMessage("Parameter types array must not be null"); + class Nested5 { + } + } - exception = assertThrows(PreconditionViolationException.class, () -> findMethod(Files.class, "copy", (Class) null)); - assertThat(exception).hasMessage("Individual parameter types must not be null"); + static class ClassExtendingClassWithNestedClasses extends ClassWithNestedClasses implements Interface45 { + } - exception = assertThrows(PreconditionViolationException.class, () -> findMethod(Files.class, "copy", new Class[] { Path.class, null })); - assertThat(exception).hasMessage("Individual parameter types must not be null"); - // @formatter:on } - @Test - void findMethodByParameterTypes() throws Exception { - assertThat(findMethod(Object.class, "noSuchMethod")).isEmpty(); - assertThat(findMethod(String.class, "noSuchMethod")).isEmpty(); + @Nested + class MethodUtilitiesTests { - assertThat(findMethod(String.class, "chars")).contains(String.class.getMethod("chars")); - assertThat(findMethod(Files.class, "copy", Path.class, OutputStream.class))// - .contains(Files.class.getMethod("copy", Path.class, OutputStream.class)); + @Test + void tryToGetMethodPreconditions() { + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToGetMethod(null, null)); + assertThrows(PreconditionViolationException.class, + () -> ReflectionUtils.tryToGetMethod(String.class, null)); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.tryToGetMethod(null, "hashCode")); + } - assertThat(findMethod(MethodShadowingChild.class, "method1", String.class))// - .contains(MethodShadowingChild.class.getMethod("method1", String.class)); - } + @Test + void tryToGetMethod() throws Exception { + assertThat(ReflectionUtils.tryToGetMethod(Object.class, "hashCode").get())// + .isEqualTo(Object.class.getMethod("hashCode")); + assertThat(ReflectionUtils.tryToGetMethod(String.class, "charAt", int.class).get())// + .isEqualTo(String.class.getMethod("charAt", int.class)); - @Test - void findMethodByParameterTypesInGenericInterface() { - Class ifc = InterfaceWithGenericDefaultMethod.class; - var method = findMethod(ifc, "foo", Number.class); - assertThat(method).isNotEmpty(); - assertThat(method.get().getName()).isEqualTo("foo"); - } + assertThat(ReflectionUtils.tryToGetMethod(Path.class, "subpath", int.class, int.class).get())// + .isEqualTo(Path.class.getMethod("subpath", int.class, int.class)); + assertThat(ReflectionUtils.tryToGetMethod(String.class, "chars").get())// + .isEqualTo(String.class.getMethod("chars")); - /** - * @see #findMethodByParameterTypesWithOverloadedMethodNextToGenericDefaultMethod() - */ - @Test - void findMethodByParameterTypesInGenericInterfaceViaParameterizedSubclass() { - Class clazz = InterfaceWithGenericDefaultMethodImpl.class; - var method = findMethod(clazz, "foo", Long.class); - assertThat(method).isNotEmpty(); - assertThat(method.get().getName()).isEqualTo("foo"); - - // One might expect or desire that the signature for the generic foo(N) - // default method would be "foo(java.lang.Long)" when looked up via the - // concrete parameterized class, but it apparently is only _visible_ as - // "foo(java.lang.Number)" via reflection. Hence the following assertion - // checks for java.lang.Number instead of java.lang.Long. - assertThat(method.get().getParameterTypes()[0]).isEqualTo(Number.class); - } + assertThat(ReflectionUtils.tryToGetMethod(String.class, "noSuchMethod").toOptional()).isEmpty(); + assertThat(ReflectionUtils.tryToGetMethod(Object.class, "clone", int.class).toOptional()).isEmpty(); + } - /** - * This test is identical to - * {@link #findMethodByParameterTypesInGenericInterfaceViaParameterizedSubclass()}, - * except that this test attempts to find the overloaded - * {@link InterfaceWithGenericDefaultMethodImpl#foo(Double)} method instead of - * the {@link InterfaceWithGenericDefaultMethod#foo(Number)} default method. - */ - @Test - void findMethodByParameterTypesWithOverloadedMethodNextToGenericDefaultMethod() { - Class clazz = InterfaceWithGenericDefaultMethodImpl.class; - Class parameterType = Double.class; - var method = findMethod(clazz, "foo", parameterType); - assertThat(method).isNotEmpty(); - assertThat(method.get().getName()).isEqualTo("foo"); - assertThat(method.get().getParameterTypes()[0]).isEqualTo(parameterType); - } + @Test + void isMethodPresentPreconditions() { + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.isMethodPresent(null, m -> true)); + assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.isMethodPresent(getClass(), null)); + } - @Test - void findMethodByParameterNamesWithPrimitiveArrayParameter() throws Exception { - assertFindMethodByParameterNames("methodWithPrimitiveArray", int[].class); - } + @Test + void isMethodPresent() { + Predicate isMethod1 = method -> (method.getName().equals("method1") + && method.getParameterTypes().length == 1 && method.getParameterTypes()[0] == String.class); - @Test - void findMethodByParameterNamesWithTwoDimensionalPrimitiveArrayParameter() throws Exception { - assertFindMethodByParameterNames("methodWithTwoDimensionalPrimitiveArray", int[][].class); - } + assertThat(ReflectionUtils.isMethodPresent(MethodShadowingChild.class, isMethod1)).isTrue(); - @Test - void findMethodByParameterNamesWithMultidimensionalPrimitiveArrayParameter() throws Exception { - assertFindMethodByParameterNames("methodWithMultidimensionalPrimitiveArray", int[][][][][].class); - } + assertThat(ReflectionUtils.isMethodPresent(getClass(), isMethod1)).isFalse(); + } - @Test - void findMethodByParameterNamesWithObjectArrayParameter() throws Exception { - assertFindMethodByParameterNames("methodWithObjectArray", String[].class); } - @Test - void findMethodByParameterNamesWithMultidimensionalObjectArrayParameter() throws Exception { - assertFindMethodByParameterNames("methodWithMultidimensionalObjectArray", Double[][][][][].class); - } + @Nested + class FindMethodTests { - /** - * @since 5.10 - */ - @Test - void findMethodByParameterNamesWithWithCustomTypeFromDifferentClassLoader() throws Exception { - var methodName = "customMethod"; - var customTypeName = CustomType.class.getName(); - var nestedTypeName = CustomType.NestedType.class.getName(); - - try (var testClassLoader = TestClassLoader.forClassNamePrefix(customTypeName)) { - var customType = testClassLoader.loadClass(customTypeName); - assertThat(customType.getClassLoader()).isSameAs(testClassLoader); - - var optional = findMethod(customType, methodName, nestedTypeName); - assertThat(optional).get().satisfies(method -> { - assertThat(method.getName()).isEqualTo(methodName); - - var declaringClass = method.getDeclaringClass(); - assertThat(declaringClass.getClassLoader()).isSameAs(testClassLoader); - assertThat(declaringClass.getName()).isEqualTo(customTypeName); - assertThat(declaringClass).isNotEqualTo(CustomType.class); - - var parameterTypes = method.getParameterTypes(); - assertThat(parameterTypes).extracting(Class::getName).containsExactly(nestedTypeName); - Class parameterType = parameterTypes[0]; - assertThat(parameterType).isNotEqualTo(CustomType.NestedType.class); - assertThat(parameterType.getClassLoader()).isSameAs(testClassLoader); - }); - } - } + @Test + void findMethodByParameterTypesPreconditions() { + // @formatter:off + assertThrows(PreconditionViolationException.class, () -> findMethod(null, null)); + assertThrows(PreconditionViolationException.class, () -> findMethod(null, "method")); - @Test - void findMethodByParameterNamesWithParameterizedMapParameter() throws Exception { - var methodName = "methodWithParameterizedMap"; + RuntimeException exception = assertThrows(PreconditionViolationException.class, () -> findMethod(String.class, null)); + assertThat(exception).hasMessage("Method name must not be null or blank"); - // standard, supported use case - assertFindMethodByParameterNames(methodName, Map.class); + exception = assertThrows(PreconditionViolationException.class, () -> findMethod(String.class, " ")); + assertThat(exception).hasMessage("Method name must not be null or blank"); - // generic type info in parameter list - var method = getClass().getDeclaredMethod(methodName, Map.class); - var genericParameterTypeName = method.getGenericParameterTypes()[0].getTypeName(); - var exception = assertThrows(JUnitException.class, - () -> findMethod(getClass(), methodName, genericParameterTypeName)); + exception = assertThrows(PreconditionViolationException.class, () -> findMethod(Files.class, "copy", (Class[]) null)); + assertThat(exception).hasMessage("Parameter types array must not be null"); - assertThat(exception).hasMessageStartingWith("Failed to load parameter type [java.util.Map findMethod(Files.class, "copy", (Class) null)); + assertThat(exception).hasMessage("Individual parameter types must not be null"); - private void assertFindMethodByParameterNames(String methodName, Class parameterType) - throws NoSuchMethodException { + exception = assertThrows(PreconditionViolationException.class, () -> findMethod(Files.class, "copy", new Class[] { Path.class, null })); + assertThat(exception).hasMessage("Individual parameter types must not be null"); + // @formatter:on + } - var method = getClass().getDeclaredMethod(methodName, parameterType); - var optional = findMethod(getClass(), methodName, parameterType.getName()); - assertThat(optional).contains(method); - } + @Test + void findMethodByParameterTypes() throws Exception { + assertThat(findMethod(Object.class, "noSuchMethod")).isEmpty(); + assertThat(findMethod(String.class, "noSuchMethod")).isEmpty(); - @Test - void findMethodsPreconditions() { - // @formatter:off - assertThrows(PreconditionViolationException.class, () -> findMethods(null, null)); - assertThrows(PreconditionViolationException.class, () -> findMethods(null, clazz -> true)); - assertThrows(PreconditionViolationException.class, () -> findMethods(String.class, null)); - - assertThrows(PreconditionViolationException.class, () -> findMethods(null, null, null)); - assertThrows(PreconditionViolationException.class, () -> findMethods(null, clazz -> true, BOTTOM_UP)); - assertThrows(PreconditionViolationException.class, () -> findMethods(String.class, null, BOTTOM_UP)); - assertThrows(PreconditionViolationException.class, () -> findMethods(String.class, clazz -> true, null)); - // @formatter:on - } + assertThat(findMethod(String.class, "chars")).contains(String.class.getMethod("chars")); + assertThat(findMethod(Files.class, "copy", Path.class, OutputStream.class))// + .contains(Files.class.getMethod("copy", Path.class, OutputStream.class)); - @Test - void findMethodsInInterface() { - assertOneFooMethodIn(InterfaceWithOneDeclaredMethod.class); - assertOneFooMethodIn(InterfaceWithDefaultMethod.class); - assertOneFooMethodIn(InterfaceWithDefaultMethodImpl.class); - assertOneFooMethodIn(InterfaceWithStaticMethod.class); - assertOneFooMethodIn(InterfaceWithStaticMethodImpl.class); - } + assertThat(findMethod(MethodShadowingChild.class, "method1", String.class))// + .contains(MethodShadowingChild.class.getMethod("method1", String.class)); + } - private static void assertOneFooMethodIn(Class clazz) { - assertThat(findMethods(clazz, isFooMethod)).hasSize(1); - } + @Test + void findMethodByParameterTypesInGenericInterface() { + Class ifc = InterfaceWithGenericDefaultMethod.class; + var method = findMethod(ifc, "foo", Number.class); + assertThat(method).isNotEmpty(); + assertThat(method.get().getName()).isEqualTo("foo"); + } - /** - * @since 1.9.1 - * @see https://github.com/junit-team/junit5/issues/2993 - */ - @Test - void findMethodsFindsDistinctMethodsDeclaredInMultipleInterfaces() { - Predicate isStringsMethod = method -> method.getName().equals("strings"); - assertThat(findMethods(DoubleInheritedInterfaceMethodTestCase.class, isStringsMethod)).hasSize(1); - } + /** + * @see #findMethodByParameterTypesWithOverloadedMethodNextToGenericDefaultMethod() + */ + @Test + void findMethodByParameterTypesInGenericInterfaceViaParameterizedSubclass() { + Class clazz = InterfaceWithGenericDefaultMethodImpl.class; + var method = findMethod(clazz, "foo", Long.class); + assertThat(method).isNotEmpty(); + assertThat(method.get().getName()).isEqualTo("foo"); + + // One might expect or desire that the signature for the generic foo(N) + // default method would be "foo(java.lang.Long)" when looked up via the + // concrete parameterized class, but it apparently is only _visible_ as + // "foo(java.lang.Number)" via reflection. Hence the following assertion + // checks for java.lang.Number instead of java.lang.Long. + assertThat(method.get().getParameterTypes()[0]).isEqualTo(Number.class); + } + + /** + * This test is identical to + * {@link #findMethodByParameterTypesInGenericInterfaceViaParameterizedSubclass()}, + * except that this test attempts to find the overloaded + * {@link InterfaceWithGenericDefaultMethodImpl#foo(Double)} method instead of + * the {@link InterfaceWithGenericDefaultMethod#foo(Number)} default method. + */ + @Test + void findMethodByParameterTypesWithOverloadedMethodNextToGenericDefaultMethod() { + Class clazz = InterfaceWithGenericDefaultMethodImpl.class; + Class parameterType = Double.class; + var method = findMethod(clazz, "foo", parameterType); + assertThat(method).isNotEmpty(); + assertThat(method.get().getName()).isEqualTo("foo"); + assertThat(method.get().getParameterTypes()[0]).isEqualTo(parameterType); + } - @Test - void findMethodsInObject() { - var methods = findMethods(Object.class, method -> true); - assertNotNull(methods); - assertTrue(methods.size() > 10); - } + @Test + void findMethodByParameterNamesWithPrimitiveArrayParameter() throws Exception { + assertFindMethodByParameterNames("methodWithPrimitiveArray", int[].class); + } - @Test - void findMethodsInVoid() { - assertThat(findMethods(void.class, method -> true)).isEmpty(); - assertThat(findMethods(Void.class, method -> true)).isEmpty(); - } + @Test + void findMethodByParameterNamesWithTwoDimensionalPrimitiveArrayParameter() throws Exception { + assertFindMethodByParameterNames("methodWithTwoDimensionalPrimitiveArray", int[][].class); + } - @Test - void findMethodsInPrimitive() { - assertThat(findMethods(int.class, method -> true)).isEmpty(); - } + @Test + void findMethodByParameterNamesWithMultidimensionalPrimitiveArrayParameter() throws Exception { + assertFindMethodByParameterNames("methodWithMultidimensionalPrimitiveArray", int[][][][][].class); + } - @Test - void findMethodsInArrays() { - assertThat(findMethods(int[].class, method -> true)).isEmpty(); - assertThat(findMethods(Integer[].class, method -> true)).isEmpty(); - } + @Test + void findMethodByParameterNamesWithObjectArrayParameter() throws Exception { + assertFindMethodByParameterNames("methodWithObjectArray", String[].class); + } - @Test - void findMethodsIgnoresSyntheticMethods() { - assertTrue(stream(ClassWithSyntheticMethod.class.getDeclaredMethods()).anyMatch(Method::isSynthetic), - "ClassWithSyntheticMethod must actually contain at least one synthetic method."); + @Test + void findMethodByParameterNamesWithMultidimensionalObjectArrayParameter() throws Exception { + assertFindMethodByParameterNames("methodWithMultidimensionalObjectArray", Double[][][][][].class); + } - var methods = findMethods(ClassWithSyntheticMethod.class, method -> true); - assertThat(methods).isEmpty(); - } + /** + * @since 5.10 + */ + @Test + void findMethodByParameterNamesWithWithCustomTypeFromDifferentClassLoader() throws Exception { + var methodName = "customMethod"; + var customTypeName = CustomType.class.getName(); + var nestedTypeName = CustomType.NestedType.class.getName(); + + try (var testClassLoader = TestClassLoader.forClassNamePrefix(customTypeName)) { + var customType = testClassLoader.loadClass(customTypeName); + assertThat(customType.getClassLoader()).isSameAs(testClassLoader); + + var optional = findMethod(customType, methodName, nestedTypeName); + assertThat(optional).get().satisfies(method -> { + assertThat(method.getName()).isEqualTo(methodName); + + var declaringClass = method.getDeclaringClass(); + assertThat(declaringClass.getClassLoader()).isSameAs(testClassLoader); + assertThat(declaringClass.getName()).isEqualTo(customTypeName); + assertThat(declaringClass).isNotEqualTo(CustomType.class); + + var parameterTypes = method.getParameterTypes(); + assertThat(parameterTypes).extracting(Class::getName).containsExactly(nestedTypeName); + Class parameterType = parameterTypes[0]; + assertThat(parameterType).isNotEqualTo(CustomType.NestedType.class); + assertThat(parameterType.getClassLoader()).isSameAs(testClassLoader); + }); + } + } - @Test - void findMethodsUsingHierarchyUpMode() throws Exception { - assertThat(findMethods(ChildClass.class, method -> method.getName().contains("method"), BOTTOM_UP))// - .containsExactly(ChildClass.class.getMethod("method4"), ParentClass.class.getMethod("method3"), - GrandparentInterface.class.getMethod("method2"), GrandparentClass.class.getMethod("method1")); + @Test + void findMethodByParameterNamesWithParameterizedMapParameter() throws Exception { + var methodName = "methodWithParameterizedMap"; - assertThat(findMethods(ChildClass.class, method -> method.getName().contains("other"), BOTTOM_UP))// - .containsExactly(ChildClass.class.getMethod("otherMethod3"), - ParentClass.class.getMethod("otherMethod2"), GrandparentClass.class.getMethod("otherMethod1")); + // standard, supported use case + assertFindMethodByParameterNames(methodName, Map.class); - assertThat(findMethods(ChildClass.class, method -> method.getName().equals("method2"), BOTTOM_UP))// - .containsExactly(ParentClass.class.getMethod("method2")); + // generic type info in parameter list + var clazz = getClass(); + var method = clazz.getDeclaredMethod(methodName, Map.class); + var genericParameterTypeName = method.getGenericParameterTypes()[0].getTypeName(); + var exception = assertThrows(JUnitException.class, + () -> findMethod(clazz, methodName, genericParameterTypeName)); - assertThat(findMethods(ChildClass.class, method -> method.getName().equals("wrongName"), BOTTOM_UP)).isEmpty(); + assertThat(exception).hasMessageStartingWith( + "Failed to load parameter type [java.util.Map method.getName().contains("method"), BOTTOM_UP))// - .containsExactly(ParentClass.class.getMethod("method3"), - GrandparentInterface.class.getMethod("method2"), GrandparentClass.class.getMethod("method1")); - } + private void assertFindMethodByParameterNames(String methodName, Class parameterType) + throws NoSuchMethodException { - @Test - void findMethodsUsingHierarchyDownMode() throws Exception { - assertThat(findMethods(ChildClass.class, method -> method.getName().contains("method"), TOP_DOWN))// - .containsExactly(GrandparentClass.class.getMethod("method1"), - GrandparentInterface.class.getMethod("method2"), ParentClass.class.getMethod("method3"), - ChildClass.class.getMethod("method4")); + var clazz = getClass(); + var method = clazz.getDeclaredMethod(methodName, parameterType); + var optional = findMethod(clazz, methodName, parameterType.getName()); + assertThat(optional).contains(method); + } - assertThat(findMethods(ChildClass.class, method -> method.getName().contains("other"), TOP_DOWN))// - .containsExactly(GrandparentClass.class.getMethod("otherMethod1"), - ParentClass.class.getMethod("otherMethod2"), ChildClass.class.getMethod("otherMethod3")); + void methodWithPrimitiveArray(int[] nums) { + } - assertThat(findMethods(ChildClass.class, method -> method.getName().equals("method2"), TOP_DOWN))// - .containsExactly(ParentClass.class.getMethod("method2")); + void methodWithTwoDimensionalPrimitiveArray(int[][] grid) { + } - assertThat(findMethods(ChildClass.class, method -> method.getName().equals("wrongName"), TOP_DOWN)).isEmpty(); + void methodWithMultidimensionalPrimitiveArray(int[][][][][] grid) { + } - assertThat(findMethods(ParentClass.class, method -> method.getName().contains("method"), TOP_DOWN))// - .containsExactly(GrandparentClass.class.getMethod("method1"), - GrandparentInterface.class.getMethod("method2"), ParentClass.class.getMethod("method3")); - } + void methodWithObjectArray(String[] info) { + } - @Test - void findMethodsWithShadowingUsingHierarchyUpMode() throws Exception { - assertThat(findMethods(MethodShadowingChild.class, methodContains1, BOTTOM_UP))// - .containsExactly(MethodShadowingChild.class.getMethod("method1", String.class)); - - assertThat(findMethods(MethodShadowingChild.class, methodContains2, BOTTOM_UP))// - .containsExactly(MethodShadowingParent.class.getMethod("method2", int.class, int.class, int.class), - MethodShadowingInterface.class.getMethod("method2", int.class, int.class)); - - assertThat(findMethods(MethodShadowingChild.class, methodContains4, BOTTOM_UP))// - .containsExactly(MethodShadowingChild.class.getMethod("method4", boolean.class)); - - assertThat(findMethods(MethodShadowingChild.class, methodContains5, BOTTOM_UP))// - .containsExactly(MethodShadowingChild.class.getMethod("method5", Long.class), - MethodShadowingParent.class.getMethod("method5", String.class)); - - var methods = findMethods(MethodShadowingChild.class, method -> true, BOTTOM_UP); - assertEquals(6, methods.size()); - assertThat(methods.subList(0, 3)).containsOnly(MethodShadowingChild.class.getMethod("method4", boolean.class), - MethodShadowingChild.class.getMethod("method1", String.class), - MethodShadowingChild.class.getMethod("method5", Long.class)); - assertThat(methods.subList(3, 5)).containsOnly( - MethodShadowingParent.class.getMethod("method2", int.class, int.class, int.class), - MethodShadowingParent.class.getMethod("method5", String.class)); - assertEquals(MethodShadowingInterface.class.getMethod("method2", int.class, int.class), methods.get(5)); - } + void methodWithTwoDimensionalObjectArray(String[][] info) { + } - @Test - void findMethodsWithShadowingUsingHierarchyDownMode() throws Exception { - assertThat(findMethods(MethodShadowingChild.class, methodContains1, TOP_DOWN))// - .containsExactly(MethodShadowingChild.class.getMethod("method1", String.class)); - - assertThat(findMethods(MethodShadowingChild.class, methodContains2, TOP_DOWN))// - .containsExactly(MethodShadowingInterface.class.getMethod("method2", int.class, int.class), - MethodShadowingParent.class.getMethod("method2", int.class, int.class, int.class)); - - assertThat(findMethods(MethodShadowingChild.class, methodContains4, TOP_DOWN))// - .containsExactly(MethodShadowingChild.class.getMethod("method4", boolean.class)); - - assertThat(findMethods(MethodShadowingChild.class, methodContains5, TOP_DOWN))// - .containsExactly(MethodShadowingParent.class.getMethod("method5", String.class), - MethodShadowingChild.class.getMethod("method5", Long.class)); - - var methods = findMethods(MethodShadowingChild.class, method -> true, TOP_DOWN); - assertEquals(6, methods.size()); - assertEquals(MethodShadowingInterface.class.getMethod("method2", int.class, int.class), methods.get(0)); - assertThat(methods.subList(1, 3)).containsOnly( - MethodShadowingParent.class.getMethod("method2", int.class, int.class, int.class), - MethodShadowingParent.class.getMethod("method5", String.class)); - assertThat(methods.subList(3, 6)).containsOnly(MethodShadowingChild.class.getMethod("method4", boolean.class), - MethodShadowingChild.class.getMethod("method1", String.class), - MethodShadowingChild.class.getMethod("method5", Long.class)); - } + void methodWithMultidimensionalObjectArray(Double[][][][][] data) { + } - @Test - void findMethodsWithStaticHidingUsingHierarchyUpMode() throws Exception { - Class ifc = StaticMethodHidingInterface.class; - Class parent = StaticMethodHidingParent.class; - Class child = StaticMethodHidingChild.class; - - var ifcMethod2 = ifc.getDeclaredMethod("method2", int.class, int.class); - var childMethod1 = child.getDeclaredMethod("method1", String.class); - var childMethod4 = child.getDeclaredMethod("method4", boolean.class); - var childMethod5 = child.getDeclaredMethod("method5", Long.class); - var parentMethod2 = parent.getDeclaredMethod("method2", int.class, int.class, int.class); - var parentMethod5 = parent.getDeclaredMethod("method5", String.class); - - assertThat(findMethods(child, methodContains1, BOTTOM_UP)).containsExactly(childMethod1); - assertThat(findMethods(child, methodContains2, BOTTOM_UP)).containsExactly(parentMethod2, ifcMethod2); - assertThat(findMethods(child, methodContains4, BOTTOM_UP)).containsExactly(childMethod4); - assertThat(findMethods(child, methodContains5, BOTTOM_UP)).containsExactly(childMethod5, parentMethod5); - - var methods = findMethods(child, method -> true, BOTTOM_UP); - assertEquals(6, methods.size()); - assertThat(methods.subList(0, 3)).containsOnly(childMethod1, childMethod4, childMethod5); - assertThat(methods.subList(3, 5)).containsOnly(parentMethod2, parentMethod5); - assertEquals(ifcMethod2, methods.get(5)); - } + void methodWithParameterizedMap(Map map) { + } - @Test - void findMethodsWithStaticHidingUsingHierarchyDownMode() throws Exception { - Class ifc = StaticMethodHidingInterface.class; - Class parent = StaticMethodHidingParent.class; - Class child = StaticMethodHidingChild.class; - - var ifcMethod2 = ifc.getDeclaredMethod("method2", int.class, int.class); - var childMethod1 = child.getDeclaredMethod("method1", String.class); - var childMethod4 = child.getDeclaredMethod("method4", boolean.class); - var childMethod5 = child.getDeclaredMethod("method5", Long.class); - var parentMethod2 = parent.getDeclaredMethod("method2", int.class, int.class, int.class); - var parentMethod5 = parent.getDeclaredMethod("method5", String.class); - - assertThat(findMethods(child, methodContains1, TOP_DOWN)).containsExactly(childMethod1); - assertThat(findMethods(child, methodContains2, TOP_DOWN)).containsExactly(ifcMethod2, parentMethod2); - assertThat(findMethods(child, methodContains4, TOP_DOWN)).containsExactly(childMethod4); - assertThat(findMethods(child, methodContains5, TOP_DOWN)).containsExactly(parentMethod5, childMethod5); - - var methods = findMethods(child, method -> true, TOP_DOWN); - assertEquals(6, methods.size()); - assertEquals(ifcMethod2, methods.get(0)); - assertThat(methods.subList(1, 3)).containsOnly(parentMethod2, parentMethod5); - assertThat(methods.subList(3, 6)).containsOnly(childMethod1, childMethod4, childMethod5); } - @Test - void findMethodsReturnsAllOverloadedMethodsThatAreNotShadowed() { - Class clazz = InterfaceWithGenericDefaultMethodImpl.class; + @Nested + class FindMethodsTests { - // Search for all foo(*) methods. - var methods = findMethods(clazz, isFooMethod); + @Test + void findMethodsPreconditions() { + // @formatter:off + assertThrows(PreconditionViolationException.class, () -> findMethods(null, null)); + assertThrows(PreconditionViolationException.class, () -> findMethods(null, clazz -> true)); + assertThrows(PreconditionViolationException.class, () -> findMethods(String.class, null)); + + assertThrows(PreconditionViolationException.class, () -> findMethods(null, null, null)); + assertThrows(PreconditionViolationException.class, () -> findMethods(null, clazz -> true, BOTTOM_UP)); + assertThrows(PreconditionViolationException.class, () -> findMethods(String.class, null, BOTTOM_UP)); + assertThrows(PreconditionViolationException.class, () -> findMethods(String.class, clazz -> true, null)); + // @formatter:on + } - // One might expect or desire that the signature for the generic foo(N) - // default method would be "foo(java.lang.Long)" when looked up via the - // concrete parameterized class, but it apparently is only _visible_ as - // "foo(java.lang.Number)" via reflection. - assertThat(signaturesOf(methods)).containsExactly("foo(java.lang.Number)", "foo(java.lang.Double)"); - } + @Test + void findMethodsInInterface() { + assertOneFooMethodIn(InterfaceWithOneDeclaredMethod.class); + assertOneFooMethodIn(InterfaceWithDefaultMethod.class); + assertOneFooMethodIn(InterfaceWithDefaultMethodImpl.class); + assertOneFooMethodIn(InterfaceWithStaticMethod.class); + assertOneFooMethodIn(InterfaceWithStaticMethodImpl.class); + } - @Test - void findMethodsDoesNotReturnOverriddenDefaultMethods() { - Class clazz = InterfaceWithOverriddenGenericDefaultMethodImpl.class; + private static void assertOneFooMethodIn(Class clazz) { + assertThat(findMethods(clazz, isFooMethod)).hasSize(1); + } - // Search for all foo(*) methods. - var methods = findMethods(clazz, isFooMethod); - var signatures = signaturesOf(methods); + /** + * @since 1.9.1 + * @see https://github.com/junit-team/junit5/issues/2993 + */ + @Test + void findMethodsFindsDistinctMethodsDeclaredInMultipleInterfaces() { + Predicate isStringsMethod = method -> method.getName().equals("strings"); + assertThat(findMethods(DoubleInheritedInterfaceMethodTestCase.class, isStringsMethod)).hasSize(1); + } - // Although the subsequent assertion covers this case as well, this - // assertion is in place to provide a more informative failure message. - assertThat(signatures).as("overridden default method should not be in results").doesNotContain( - "foo(java.lang.Number)"); - assertThat(signatures).containsExactly("foo(java.lang.Long)", "foo(java.lang.Double)"); - } + @Test + void findMethodsInObject() { + var methods = findMethods(Object.class, method -> true); + assertNotNull(methods); + assertTrue(methods.size() > 10); + } - private static List signaturesOf(List methods) { - // @formatter:off - return methods.stream() - .map(m -> String.format("%s(%s)", m.getName(), ClassUtils.nullSafeToString(m.getParameterTypes()))) - .collect(toList()); - // @formatter:on - } + @Test + void findMethodsInVoid() { + assertThat(findMethods(void.class, method -> true)).isEmpty(); + assertThat(findMethods(Void.class, method -> true)).isEmpty(); + } - @Test - void findMethodsIgnoresBridgeMethods() throws Exception { - assertFalse(Modifier.isPublic(PublicChildClass.class.getSuperclass().getModifiers())); - assertTrue(Modifier.isPublic(PublicChildClass.class.getModifiers())); - assertTrue(PublicChildClass.class.getDeclaredMethod("method1").isBridge()); - assertTrue(PublicChildClass.class.getDeclaredMethod("method3").isBridge()); - - var methods = findMethods(PublicChildClass.class, method -> true); - var signatures = signaturesOf(methods); - assertThat(signatures).containsOnly("method1()", "method2()", "method3()", "otherMethod1()", "otherMethod2()"); - assertEquals(0, methods.stream().filter(Method::isBridge).count()); - } + @Test + void findMethodsInPrimitive() { + assertThat(findMethods(int.class, method -> true)).isEmpty(); + } - @Test - void isGeneric() { - for (var method : Generic.class.getMethods()) { - assertTrue(ReflectionUtils.isGeneric(method)); + @Test + void findMethodsInArrays() { + assertThat(findMethods(int[].class, method -> true)).isEmpty(); + assertThat(findMethods(Integer[].class, method -> true)).isEmpty(); } - for (var method : PublicClass.class.getMethods()) { - assertFalse(ReflectionUtils.isGeneric(method)); + + @Test + void findMethodsIgnoresSyntheticMethods() { + assertTrue(stream(ClassWithSyntheticMethod.class.getDeclaredMethods()).anyMatch(Method::isSynthetic), + "ClassWithSyntheticMethod must actually contain at least one synthetic method."); + + var methods = findMethods(ClassWithSyntheticMethod.class, method -> true); + assertThat(methods).isEmpty(); } - } - @Test - void readFieldValuesPreconditions() { - assertThrows(PreconditionViolationException.class, () -> ReflectionUtils.readFieldValues(null, new Object())); - assertThrows(PreconditionViolationException.class, - () -> ReflectionUtils.readFieldValues(new ArrayList<>(), new Object(), null)); - } + @Test + void findMethodsUsingHierarchyUpMode() throws Exception { + assertThat(findMethods(ChildClass.class, method -> method.getName().contains("method"), BOTTOM_UP))// + .containsExactly(ChildClass.class.getMethod("method4"), ParentClass.class.getMethod("method3"), + GrandparentInterface.class.getMethod("method2"), GrandparentClass.class.getMethod("method1")); - @Test - void readFieldValuesFromInstance() { - var fields = ReflectionUtils.findFields(ClassWithFields.class, f -> true, TOP_DOWN); + assertThat(findMethods(ChildClass.class, method -> method.getName().contains("other"), BOTTOM_UP))// + .containsExactly(ChildClass.class.getMethod("otherMethod3"), + ParentClass.class.getMethod("otherMethod2"), GrandparentClass.class.getMethod("otherMethod1")); - var values = ReflectionUtils.readFieldValues(fields, new ClassWithFields()); + assertThat(findMethods(ChildClass.class, method -> method.getName().equals("method2"), BOTTOM_UP))// + .containsExactly(ParentClass.class.getMethod("method2")); - assertThat(values).containsExactly("enigma", 3.14, "text", 2.5, null, 42, "constant", 99); - } + assertThat(findMethods(ChildClass.class, method -> method.getName().equals("wrongName"), BOTTOM_UP))// + .isEmpty(); - @Test - void readFieldValuesFromClass() { - var fields = ReflectionUtils.findFields(ClassWithFields.class, ReflectionUtils::isStatic, TOP_DOWN); + assertThat(findMethods(ParentClass.class, method -> method.getName().contains("method"), BOTTOM_UP))// + .containsExactly(ParentClass.class.getMethod("method3"), + GrandparentInterface.class.getMethod("method2"), GrandparentClass.class.getMethod("method1")); + } - var values = ReflectionUtils.readFieldValues(fields, null); + @Test + void findMethodsUsingHierarchyDownMode() throws Exception { + assertThat(findMethods(ChildClass.class, method -> method.getName().contains("method"), TOP_DOWN))// + .containsExactly(GrandparentClass.class.getMethod("method1"), + GrandparentInterface.class.getMethod("method2"), ParentClass.class.getMethod("method3"), + ChildClass.class.getMethod("method4")); - assertThat(values).containsExactly(2.5, "constant", 99); - } + assertThat(findMethods(ChildClass.class, method -> method.getName().contains("other"), TOP_DOWN))// + .containsExactly(GrandparentClass.class.getMethod("otherMethod1"), + ParentClass.class.getMethod("otherMethod2"), ChildClass.class.getMethod("otherMethod3")); - @Test - void readFieldValuesFromInstanceWithTypeFilterForString() { - var fields = ReflectionUtils.findFields(ClassWithFields.class, isA(String.class), TOP_DOWN); + assertThat(findMethods(ChildClass.class, method -> method.getName().equals("method2"), TOP_DOWN))// + .containsExactly(ParentClass.class.getMethod("method2")); - var values = ReflectionUtils.readFieldValues(fields, new ClassWithFields(), isA(String.class)); + assertThat(findMethods(ChildClass.class, method -> method.getName().equals("wrongName"), TOP_DOWN))// + .isEmpty(); - assertThat(values).containsExactly("enigma", "text", null, "constant"); - } + assertThat(findMethods(ParentClass.class, method -> method.getName().contains("method"), TOP_DOWN))// + .containsExactly(GrandparentClass.class.getMethod("method1"), + GrandparentInterface.class.getMethod("method2"), ParentClass.class.getMethod("method3")); + } - @Test - void readFieldValuesFromClassWithTypeFilterForString() { - var fields = ReflectionUtils.findFields(ClassWithFields.class, isA(String.class).and(ReflectionUtils::isStatic), - TOP_DOWN); + @Test + void findMethodsWithShadowingUsingHierarchyUpMode() throws Exception { + assertThat(findMethods(MethodShadowingChild.class, methodContains1, BOTTOM_UP))// + .containsExactly(MethodShadowingChild.class.getMethod("method1", String.class)); - var values = ReflectionUtils.readFieldValues(fields, null, isA(String.class)); + assertThat(findMethods(MethodShadowingChild.class, methodContains2, BOTTOM_UP))// + .containsExactly(MethodShadowingParent.class.getMethod("method2", int.class, int.class, int.class), + MethodShadowingInterface.class.getMethod("method2", int.class, int.class)); - assertThat(values).containsExactly("constant"); - } + assertThat(findMethods(MethodShadowingChild.class, methodContains4, BOTTOM_UP))// + .containsExactly(MethodShadowingChild.class.getMethod("method4", boolean.class)); - @Test - void readFieldValuesFromInstanceWithTypeFilterForInteger() { - var fields = ReflectionUtils.findFields(ClassWithFields.class, isA(int.class), TOP_DOWN); + assertThat(findMethods(MethodShadowingChild.class, methodContains5, BOTTOM_UP))// + .containsExactly(MethodShadowingChild.class.getMethod("method5", Long.class), + MethodShadowingParent.class.getMethod("method5", String.class)); - var values = ReflectionUtils.readFieldValues(fields, new ClassWithFields(), isA(int.class)); + var methods = findMethods(MethodShadowingChild.class, method -> true, BOTTOM_UP); + assertEquals(6, methods.size()); + assertThat(methods.subList(0, 3)).containsOnly( + MethodShadowingChild.class.getMethod("method4", boolean.class), + MethodShadowingChild.class.getMethod("method1", String.class), + MethodShadowingChild.class.getMethod("method5", Long.class)); + assertThat(methods.subList(3, 5)).containsOnly( + MethodShadowingParent.class.getMethod("method2", int.class, int.class, int.class), + MethodShadowingParent.class.getMethod("method5", String.class)); + assertEquals(MethodShadowingInterface.class.getMethod("method2", int.class, int.class), methods.get(5)); + } - assertThat(values).containsExactly(42); - } + @Test + void findMethodsWithShadowingUsingHierarchyDownMode() throws Exception { + assertThat(findMethods(MethodShadowingChild.class, methodContains1, TOP_DOWN))// + .containsExactly(MethodShadowingChild.class.getMethod("method1", String.class)); + + assertThat(findMethods(MethodShadowingChild.class, methodContains2, TOP_DOWN))// + .containsExactly(MethodShadowingInterface.class.getMethod("method2", int.class, int.class), + MethodShadowingParent.class.getMethod("method2", int.class, int.class, int.class)); + + assertThat(findMethods(MethodShadowingChild.class, methodContains4, TOP_DOWN))// + .containsExactly(MethodShadowingChild.class.getMethod("method4", boolean.class)); + + assertThat(findMethods(MethodShadowingChild.class, methodContains5, TOP_DOWN))// + .containsExactly(MethodShadowingParent.class.getMethod("method5", String.class), + MethodShadowingChild.class.getMethod("method5", Long.class)); + + var methods = findMethods(MethodShadowingChild.class, method -> true, TOP_DOWN); + assertEquals(6, methods.size()); + assertEquals(MethodShadowingInterface.class.getMethod("method2", int.class, int.class), methods.get(0)); + assertThat(methods.subList(1, 3)).containsOnly( + MethodShadowingParent.class.getMethod("method2", int.class, int.class, int.class), + MethodShadowingParent.class.getMethod("method5", String.class)); + assertThat(methods.subList(3, 6)).containsOnly( + MethodShadowingChild.class.getMethod("method4", boolean.class), + MethodShadowingChild.class.getMethod("method1", String.class), + MethodShadowingChild.class.getMethod("method5", Long.class)); + } + + /** + * In non-legacy mode, "static hiding" does not occur. + */ + @Test + void findMethodsWithoutStaticHidingUsingHierarchyUpMode() throws Exception { + Class ifc = StaticMethodHidingInterface.class; + Class parent = StaticMethodHidingParent.class; + Class child = StaticMethodHidingChild.class; + + var ifcMethod1 = ifc.getDeclaredMethod("method1", String.class); + var ifcMethod2 = ifc.getDeclaredMethod("method2", int.class, int.class); + var childMethod1 = child.getDeclaredMethod("method1", String.class); + var childMethod4 = child.getDeclaredMethod("method4", boolean.class); + var childMethod5 = child.getDeclaredMethod("method5", Long.class); + var parentMethod1 = parent.getDeclaredMethod("method1", String.class); + var parentMethod2 = parent.getDeclaredMethod("method2", int.class, int.class, int.class); + var parentMethod4 = parent.getDeclaredMethod("method4", boolean.class); + var parentMethod5 = parent.getDeclaredMethod("method5", String.class); + + assertThat(findMethods(child, methodContains1, BOTTOM_UP)).containsExactly(childMethod1, parentMethod1, + ifcMethod1); + assertThat(findMethods(child, methodContains2, BOTTOM_UP)).containsExactly(parentMethod2, ifcMethod2); + assertThat(findMethods(child, methodContains4, BOTTOM_UP)).containsExactly(childMethod4, parentMethod4); + assertThat(findMethods(child, methodContains5, BOTTOM_UP)).containsExactly(childMethod5, parentMethod5); + + var methods = findMethods(child, method -> true, BOTTOM_UP); + assertEquals(9, methods.size()); + assertThat(methods.subList(0, 3)).containsOnly(childMethod1, childMethod4, childMethod5); + assertThat(methods.subList(3, 7)).containsOnly(parentMethod1, parentMethod2, parentMethod4, parentMethod5); + assertThat(methods.subList(7, 9)).containsOnly(ifcMethod1, ifcMethod2); + } + + /** + * In legacy mode, "static hiding" occurs. + */ + @Test + void findMethodsWithStaticHidingUsingHierarchyUpModeInLegacyMode() throws Exception { + try { + ReflectionUtils.useLegacySearchSemantics = true; + + Class ifc = StaticMethodHidingInterface.class; + Class parent = StaticMethodHidingParent.class; + Class child = StaticMethodHidingChild.class; + + var ifcMethod2 = ifc.getDeclaredMethod("method2", int.class, int.class); + var childMethod1 = child.getDeclaredMethod("method1", String.class); + var childMethod4 = child.getDeclaredMethod("method4", boolean.class); + var childMethod5 = child.getDeclaredMethod("method5", Long.class); + var parentMethod2 = parent.getDeclaredMethod("method2", int.class, int.class, int.class); + var parentMethod5 = parent.getDeclaredMethod("method5", String.class); + + assertThat(findMethods(child, methodContains1, BOTTOM_UP)).containsExactly(childMethod1); + assertThat(findMethods(child, methodContains2, BOTTOM_UP)).containsExactly(parentMethod2, ifcMethod2); + assertThat(findMethods(child, methodContains4, BOTTOM_UP)).containsExactly(childMethod4); + assertThat(findMethods(child, methodContains5, BOTTOM_UP)).containsExactly(childMethod5, parentMethod5); + + var methods = findMethods(child, method -> true, BOTTOM_UP); + assertEquals(6, methods.size()); + assertThat(methods.subList(0, 3)).containsOnly(childMethod1, childMethod4, childMethod5); + assertThat(methods.subList(3, 5)).containsOnly(parentMethod2, parentMethod5); + assertEquals(ifcMethod2, methods.get(5)); + } + finally { + ReflectionUtils.useLegacySearchSemantics = false; + } + } + + /** + * In non-legacy mode, "static hiding" does not occur. + */ + @Test + void findMethodsWithoutStaticHidingUsingHierarchyDownMode() throws Exception { + Class ifc = StaticMethodHidingInterface.class; + Class parent = StaticMethodHidingParent.class; + Class child = StaticMethodHidingChild.class; + + var ifcMethod1 = ifc.getDeclaredMethod("method1", String.class); + var ifcMethod2 = ifc.getDeclaredMethod("method2", int.class, int.class); + var childMethod1 = child.getDeclaredMethod("method1", String.class); + var childMethod4 = child.getDeclaredMethod("method4", boolean.class); + var childMethod5 = child.getDeclaredMethod("method5", Long.class); + var parentMethod1 = parent.getDeclaredMethod("method1", String.class); + var parentMethod2 = parent.getDeclaredMethod("method2", int.class, int.class, int.class); + var parentMethod4 = parent.getDeclaredMethod("method4", boolean.class); + var parentMethod5 = parent.getDeclaredMethod("method5", String.class); + + assertThat(findMethods(child, methodContains1, TOP_DOWN)).containsExactly(ifcMethod1, parentMethod1, + childMethod1); + assertThat(findMethods(child, methodContains2, TOP_DOWN)).containsExactly(ifcMethod2, parentMethod2); + assertThat(findMethods(child, methodContains4, TOP_DOWN)).containsExactly(parentMethod4, childMethod4); + assertThat(findMethods(child, methodContains5, TOP_DOWN)).containsExactly(parentMethod5, childMethod5); + + var methods = findMethods(child, method -> true, TOP_DOWN); + assertEquals(9, methods.size()); + methods.forEach(System.err::println); + assertThat(methods.subList(0, 2)).containsOnly(ifcMethod1, ifcMethod2); + assertThat(methods.subList(2, 6)).containsOnly(parentMethod1, parentMethod2, parentMethod4, parentMethod5); + assertThat(methods.subList(6, 9)).containsOnly(childMethod1, childMethod4, childMethod5); + } + + /** + * In legacy mode, "static hiding" occurs. + */ + @Test + void findMethodsWithStaticHidingUsingHierarchyDownModeInLegacyMode() throws Exception { + try { + ReflectionUtils.useLegacySearchSemantics = true; + + Class ifc = StaticMethodHidingInterface.class; + Class parent = StaticMethodHidingParent.class; + Class child = StaticMethodHidingChild.class; + + var ifcMethod2 = ifc.getDeclaredMethod("method2", int.class, int.class); + var childMethod1 = child.getDeclaredMethod("method1", String.class); + var childMethod4 = child.getDeclaredMethod("method4", boolean.class); + var childMethod5 = child.getDeclaredMethod("method5", Long.class); + var parentMethod2 = parent.getDeclaredMethod("method2", int.class, int.class, int.class); + var parentMethod5 = parent.getDeclaredMethod("method5", String.class); + + assertThat(findMethods(child, methodContains1, TOP_DOWN)).containsExactly(childMethod1); + assertThat(findMethods(child, methodContains2, TOP_DOWN)).containsExactly(ifcMethod2, parentMethod2); + assertThat(findMethods(child, methodContains4, TOP_DOWN)).containsExactly(childMethod4); + assertThat(findMethods(child, methodContains5, TOP_DOWN)).containsExactly(parentMethod5, childMethod5); + + var methods = findMethods(child, method -> true, TOP_DOWN); + assertEquals(6, methods.size()); + assertEquals(ifcMethod2, methods.get(0)); + assertThat(methods.subList(1, 3)).containsOnly(parentMethod2, parentMethod5); + assertThat(methods.subList(3, 6)).containsOnly(childMethod1, childMethod4, childMethod5); + } + finally { + ReflectionUtils.useLegacySearchSemantics = false; + } + } - @Test - void readFieldValuesFromClassWithTypeFilterForInteger() { - var fields = ReflectionUtils.findFields(ClassWithFields.class, - isA(Integer.class).and(ReflectionUtils::isStatic), TOP_DOWN); + @Test + void findMethodsDoesNotReturnOverriddenMethods() { + Predicate isSpecial = method -> method.isAnnotationPresent(Special.class); - var values = ReflectionUtils.readFieldValues(fields, null, isA(Integer.class)); + // Search for all @Special methods. + var methods = findMethods(SuperclassWithInstanceMethods.class, isSpecial); - assertThat(values).containsExactly(99); - } + assertThat(signaturesOf(methods))// + .containsExactlyInAnyOrder("specialFoo()", "specialFoo(int)", "specialFoo(char)", "specialBar()", + "specialBaz()"); - @Test - void readFieldValuesFromInstanceWithTypeFilterForDouble() { - var fields = ReflectionUtils.findFields(ClassWithFields.class, isA(double.class), TOP_DOWN); + // Search for all @Special methods. + methods = findMethods(SubclassWithOverriddenInstanceMethods.class, isSpecial); - var values = ReflectionUtils.readFieldValues(fields, new ClassWithFields(), isA(double.class)); + assertThat(signaturesOf(methods))// + .containsExactlyInAnyOrder("foo()", "specialFoo()", "specialFoo(int)", "specialBar()"); + } - assertThat(values).containsExactly(3.14); - } + @Test + void findMethodsReturnsAllOverloadedMethodsInGenericTypeHieararchy() { + Class clazz = InterfaceWithGenericDefaultMethodImpl.class; - @Test - void readFieldValuesFromClassWithTypeFilterForDouble() { - var fields = ReflectionUtils.findFields(ClassWithFields.class, isA(Double.class).and(ReflectionUtils::isStatic), - TOP_DOWN); + // Search for all foo(*) methods. + var methods = findMethods(clazz, isFooMethod); - var values = ReflectionUtils.readFieldValues(fields, null, isA(Double.class)); + // One might expect or desire that the signature for the generic foo(N) + // default method would be "foo(java.lang.Long)" when looked up via the + // concrete parameterized class, but it apparently is only _visible_ as + // "foo(java.lang.Number)" via reflection. + assertThat(signaturesOf(methods)).containsExactly("foo(java.lang.Number)", "foo(java.lang.Double)"); + } - assertThat(values).containsExactly(2.5); - } + @Test + void findMethodsDoesNotReturnOverriddenDefaultMethods() { + Class clazz = InterfaceWithOverriddenGenericDefaultMethodImpl.class; - private Predicate isA(Class type) { - return f -> f.getType().isAssignableFrom(type); - } + // Search for all foo(*) methods. + var methods = findMethods(clazz, isFooMethod); + var signatures = signaturesOf(methods); - private static void createDirectories(Path... paths) throws IOException { - for (var path : paths) { - Files.createDirectory(path); + // Although the subsequent assertion covers this case as well, this + // assertion is in place to provide a more informative failure message. + assertThat(signatures).as("overridden default method should not be in results").doesNotContain( + "foo(java.lang.Number)"); + assertThat(signatures).containsExactly("foo(java.lang.Long)", "foo(java.lang.Double)"); } - } - // ------------------------------------------------------------------------- + private static List signaturesOf(List methods) { + // @formatter:off + return methods.stream() + .map(m -> String.format("%s(%s)", m.getName(), ClassUtils.nullSafeToString(m.getParameterTypes()))) + .collect(toList()); + // @formatter:on + } - void methodWithPrimitiveArray(int[] nums) { - } + @Test + void findMethodsIgnoresBridgeMethods() throws Exception { + assertFalse(Modifier.isPublic(PublicChildClass.class.getSuperclass().getModifiers())); + assertTrue(Modifier.isPublic(PublicChildClass.class.getModifiers())); + assertTrue(PublicChildClass.class.getDeclaredMethod("method1").isBridge()); + assertTrue(PublicChildClass.class.getDeclaredMethod("method3").isBridge()); + + var methods = findMethods(PublicChildClass.class, method -> true); + var signatures = signaturesOf(methods); + assertThat(signatures).containsOnly("method1()", "method2()", "method3()", "otherMethod1()", + "otherMethod2()"); + assertEquals(0, methods.stream().filter(Method::isBridge).count()); + } + + /** + * @see https://github.com/junit-team/junit5/issues/3553 + */ + @Test + void findMethodsDoesNotAllowInstanceMethodToHideStaticMethod() throws Exception { + final String BEFORE = "before"; + Class superclass = SuperclassWithStaticPackagePrivateBeforeMethod.class; + Method staticMethod = superclass.getDeclaredMethod(BEFORE); + Class subclass = SubclassWithNonStaticPackagePrivateBeforeMethod.class; + Method nonStaticMethod = subclass.getDeclaredMethod(BEFORE); + + // Prerequisite + var methods = findMethods(superclass, ReflectionUtils::isStatic); + assertThat(methods).containsExactly(staticMethod); + + // Actual use cases for this test + methods = findMethods(subclass, ReflectionUtils::isStatic); + assertThat(methods).containsExactly(staticMethod); + methods = findMethods(subclass, ReflectionUtils::isNotStatic); + assertThat(methods).containsExactly(nonStaticMethod); + } + + interface StringsInterface1 { + static Stream strings() { + return Stream.of("abc", "def"); + } + } - void methodWithTwoDimensionalPrimitiveArray(int[][] grid) { - } + interface StringsInterface2 extends StringsInterface1 { + } - void methodWithMultidimensionalPrimitiveArray(int[][][][][] grid) { - } + /** + * Inherits strings() from interfaces StringsInterface1 and StringsInterface2. + */ + static class DoubleInheritedInterfaceMethodTestCase implements StringsInterface1, StringsInterface2 { + } - void methodWithObjectArray(String[] info) { - } + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + @interface Special { + } - void methodWithTwoDimensionalObjectArray(String[][] info) { - } + static class SuperclassWithInstanceMethods { - void methodWithMultidimensionalObjectArray(Double[][][][][] data) { - } + void foo() { + } - void methodWithParameterizedMap(Map map) { - } + void bar() { + } - interface StringsInterface1 { - static Stream strings() { - return Stream.of("abc", "def"); - } - } + void baz() { + } - interface StringsInterface2 extends StringsInterface1 { - } + @Special + void specialFoo() { + } - /** - * Inherits strings() from interfaces StringsInterface1 and StringsInterface2. - */ - static class DoubleInheritedInterfaceMethodTestCase implements StringsInterface1, StringsInterface2 { - } + @Special + void specialFoo(int i) { + } - interface Generic { + @Special + void specialFoo(char ch) { + } - X foo(); + @Special + int specialBar() { + return 99; + } - Y foo(X x, Y y); + @Special + String specialBaz() { + return "42"; + } + } - Z foo(Z[][] zees); + static class SubclassWithOverriddenInstanceMethods extends SuperclassWithInstanceMethods { - int foo(T t); + // foo() is now special. + @Special + @Override + void foo() { + } - T foo(int i); - } + // No longer special. + // Simulates overriding a @Test method without redeclaring @Test. + @Override + void specialFoo(char ch) { + } - class ClassWithSyntheticMethod { + // No longer special. + // Simulates overriding a @TestFactory method without redeclaring @TestFactory. + @Override + String specialBaz() { + return super.specialBaz(); + } + } - // The following lambda expression results in a synthetic method in the - // compiled byte code. - Comparable synthetic = number -> 0; } - interface InterfaceWithOneDeclaredMethod { + @Nested + class ReadFieldTests { - void foo(); - } + @Test + @SuppressWarnings("deprecation") + void readFieldValueOfNonexistentStaticField() { + assertThat(readFieldValue(MyClass.class, "doesNotExist", null)).isNotPresent(); + assertThat(readFieldValue(MySubClass.class, "staticField", null)).isNotPresent(); + } - interface InterfaceWithDefaultMethod { + @Test + void tryToReadFieldValueOfNonexistentStaticField() { + assertThrows(NoSuchFieldException.class, + () -> tryToReadFieldValue(MyClass.class, "doesNotExist", null).get()); + assertThrows(NoSuchFieldException.class, + () -> tryToReadFieldValue(MySubClass.class, "staticField", null).get()); + } - default void foo() { + @Test + @SuppressWarnings("deprecation") + void readFieldValueOfNonexistentInstanceField() { + assertThat(readFieldValue(MyClass.class, "doesNotExist", new MyClass(42))).isNotPresent(); + assertThat(readFieldValue(MyClass.class, "doesNotExist", new MySubClass(42))).isNotPresent(); } - } - static class InterfaceWithDefaultMethodImpl implements InterfaceWithDefaultMethod { - } + @Test + void tryToReadFieldValueOfNonexistentInstanceField() { + assertThrows(NoSuchFieldException.class, + () -> tryToReadFieldValue(MyClass.class, "doesNotExist", new MyClass(42)).get()); + assertThrows(NoSuchFieldException.class, + () -> tryToReadFieldValue(MyClass.class, "doesNotExist", new MySubClass(42)).get()); + } - interface InterfaceWithGenericDefaultMethod { + @Test + @SuppressWarnings("deprecation") + void readFieldValueOfExistingStaticField() throws Exception { + assertThat(readFieldValue(MyClass.class, "staticField", null)).contains(42); - default void foo(N number) { + var field = MyClass.class.getDeclaredField("staticField"); + assertThat(readFieldValue(field)).contains(42); + assertThat(readFieldValue(field, null)).contains(42); } - } - static class InterfaceWithGenericDefaultMethodImpl implements InterfaceWithGenericDefaultMethod { + @Test + void tryToReadFieldValueOfExistingStaticField() throws Exception { + assertThat(tryToReadFieldValue(MyClass.class, "staticField", null).get()).isEqualTo(42); - void foo(Double number) { + var field = MyClass.class.getDeclaredField("staticField"); + assertThat(tryToReadFieldValue(field).get()).isEqualTo(42); + assertThat(tryToReadFieldValue(field, null).get()).isEqualTo(42); } - } - static class InterfaceWithOverriddenGenericDefaultMethodImpl implements InterfaceWithGenericDefaultMethod { + @Test + @SuppressWarnings("deprecation") + void readFieldValueOfExistingInstanceField() throws Exception { + var instance = new MyClass(42); + assertThat(readFieldValue(MyClass.class, "instanceField", instance)).contains(42); - @Override - public void foo(Long number) { + var field = MyClass.class.getDeclaredField("instanceField"); + assertThat(readFieldValue(field, instance)).contains(42); } - void foo(Double number) { + @Test + @SuppressWarnings("deprecation") + void attemptToReadFieldValueOfExistingInstanceFieldAsStaticField() throws Exception { + var field = MyClass.class.getDeclaredField("instanceField"); + Exception exception = assertThrows(PreconditionViolationException.class, () -> readFieldValue(field, null)); + assertThat(exception)// + .hasMessageStartingWith("Cannot read non-static field")// + .hasMessageEndingWith("on a null instance."); } - } - interface InterfaceWithStaticMethod { + @Test + void tryToReadFieldValueOfExistingInstanceField() throws Exception { + var instance = new MyClass(42); + assertThat(tryToReadFieldValue(MyClass.class, "instanceField", instance).get()).isEqualTo(42); - static void foo() { + var field = MyClass.class.getDeclaredField("instanceField"); + assertThat(tryToReadFieldValue(field, instance).get()).isEqualTo(42); + assertThrows(PreconditionViolationException.class, () -> tryToReadFieldValue(field, null).get()); } - } - static class InterfaceWithStaticMethodImpl implements InterfaceWithStaticMethod { } - interface InterfaceA { - } + @Nested + class FindAndReadFieldsTests { - interface InterfaceB { - } + /** + * @see https://github.com/junit-team/junit5/issues/3553 + */ + @Test + void findFieldsDoesNotAllowInstanceFieldToHideStaticField() throws Exception { + final String TEMP_DIR = "tempDir"; + Class superclass = SuperclassWithStaticPackagePrivateTempDirField.class; + Field staticField = superclass.getDeclaredField(TEMP_DIR); + Class subclass = SubclassWithNonStaticPackagePrivateTempDirField.class; + Field nonStaticField = subclass.getDeclaredField(TEMP_DIR); - interface InterfaceC extends InterfaceA, InterfaceB { - } + // Prerequisite + var fields = findFields(superclass, ReflectionUtils::isStatic, TOP_DOWN); + assertThat(fields).containsExactly(staticField); - interface InterfaceD { - } + // Actual use cases for this test + fields = findFields(subclass, ReflectionUtils::isStatic, TOP_DOWN); + assertThat(fields).containsExactly(staticField); + fields = findFields(subclass, ReflectionUtils::isNotStatic, TOP_DOWN); + assertThat(fields).containsExactly(nonStaticField); + } - static class A implements InterfaceA, InterfaceD { - } + @Test + void readFieldValuesPreconditions() { + List fields = new ArrayList<>(); + assertThrows(PreconditionViolationException.class, () -> readFieldValues(null, new Object())); + assertThrows(PreconditionViolationException.class, () -> readFieldValues(fields, null, null)); + assertThrows(PreconditionViolationException.class, () -> readFieldValues(fields, new Object(), null)); + } - static class B extends A implements InterfaceC { - } + @Test + void readFieldValuesFromInstance() { + var fields = findFields(ClassWithFields.class, f -> true, TOP_DOWN); - static class C { + var values = readFieldValues(fields, new ClassWithFields()); - C() { + assertThat(values).containsExactly("enigma", 3.14, "text", 2.5, null, 42, "constant", 99); } - C(String a, String b) { + @Test + void readFieldValuesFromClass() { + var fields = findFields(ClassWithFields.class, ReflectionUtils::isStatic, TOP_DOWN); + + var values = readFieldValues(fields, null); + + assertThat(values).containsExactly(2.5, "constant", 99); } - } + /** + * @see https://github.com/junit-team/junit5/issues/3646 + * @since 1.11 + */ + @Test + void readFieldValuesFromInteracesAndClassesInTypeHierarchy() { + var fields = findFields(InterfaceWithField.class, ReflectionUtils::isStatic, TOP_DOWN); + var values = readFieldValues(fields, null); + assertThat(values).containsOnly("ifc"); - static class Exploder { + fields = findFields(SuperclassWithFieldAndFieldFromInterface.class, ReflectionUtils::isStatic, TOP_DOWN); + values = readFieldValues(fields, null); + assertThat(values).containsExactly("ifc", "super"); - Exploder() { - throw new RuntimeException("boom"); + fields = findFields(SubclassWithFieldAndFieldFromInterface.class, ReflectionUtils::isStatic, TOP_DOWN); + values = readFieldValues(fields, null); + assertThat(values).containsExactly("ifc", "super", "sub"); } - } + @Test + void readFieldValuesFromInstanceWithTypeFilterForString() { + var fields = findFields(ClassWithFields.class, isA(String.class), TOP_DOWN); - static class MyClass { + var values = readFieldValues(fields, new ClassWithFields(), isA(String.class)); - static final int staticField = 42; + assertThat(values).containsExactly("enigma", "text", null, "constant"); + } - final int instanceField; + @Test + void readFieldValuesFromClassWithTypeFilterForString() { + var fields = findFields(ClassWithFields.class, isA(String.class).and(ReflectionUtils::isStatic), TOP_DOWN); - MyClass(int value) { - this.instanceField = value; + var values = readFieldValues(fields, null, isA(String.class)); + + assertThat(values).containsExactly("constant"); } - } - static class MySubClass extends MyClass { + @Test + void readFieldValuesFromInstanceWithTypeFilterForInteger() { + var fields = findFields(ClassWithFields.class, isA(int.class), TOP_DOWN); - MySubClass(int value) { - super(value); + var values = readFieldValues(fields, new ClassWithFields(), isA(int.class)); + + assertThat(values).containsExactly(42); } - } - // Intentionally non-static - public class PublicClass { + @Test + void readFieldValuesFromClassWithTypeFilterForInteger() { + var fields = findFields(ClassWithFields.class, isA(Integer.class).and(ReflectionUtils::isStatic), TOP_DOWN); - public void publicMethod() { - } + var values = readFieldValues(fields, null, isA(Integer.class)); - public void method(String str, Integer num) { + assertThat(values).containsExactly(99); } - public void method(String[] strings, Integer[] nums) { - } + @Test + void readFieldValuesFromInstanceWithTypeFilterForDouble() { + var fields = findFields(ClassWithFields.class, isA(double.class), TOP_DOWN); - public void method(boolean b, char c) { - } + var values = readFieldValues(fields, new ClassWithFields(), isA(double.class)); - public void method(char[] characters, int[] nums) { + assertThat(values).containsExactly(3.14); } - } - private class PrivateClass { + @Test + void readFieldValuesFromClassWithTypeFilterForDouble() { + var fields = findFields(ClassWithFields.class, isA(Double.class).and(ReflectionUtils::isStatic), TOP_DOWN); - @SuppressWarnings("unused") - private void privateMethod() { - } - } + var values = readFieldValues(fields, null, isA(Double.class)); - protected class ProtectedClass { + assertThat(values).containsExactly(2.5); + } - @SuppressWarnings("unused") - protected void protectedMethod() { + private static Predicate isA(Class type) { + return f -> f.getType().isAssignableFrom(type); } - } - class PackageVisibleClass { + public static class ClassWithFields { - @SuppressWarnings("unused") - void packageVisibleMethod() { - } - } + public static final String CONST = "constant"; - final class FinalClass { + public static final Integer CONST_INTEGER = 99; - @SuppressWarnings("unused") - final void finalMethod() { - } - } + public static final Double CONST_DOUBLE = 2.5; - abstract static class AbstractClass { + public final String stringField = "text"; - abstract void abstractMethod(); - } + @SuppressWarnings("unused") + private final String privateStringField = "enigma"; - static class StaticClass { + final String nullStringField = null; - static void staticMethod() { - } - } + public final int integerField = 42; - static class ClassWithVoidAndNonVoidMethods { + public final double doubleField = 3.14; - void voidMethod() { } - Void methodReturningVoidReference() { - return null; + interface InterfaceWithField { + + String interfacePath = "ifc"; } - String methodReturningObject() { - return ""; + static class SuperclassWithFieldAndFieldFromInterface implements InterfaceWithField { + + static final String superPath = "super"; } - int methodReturningPrimitive() { - return 0; + static class SubclassWithFieldAndFieldFromInterface extends SuperclassWithFieldAndFieldFromInterface + implements InterfaceWithField { + + static final String subPath = "sub"; } } - static class InvocationTracker { + // ------------------------------------------------------------------------- - static boolean publicStaticMethodInvoked; - static boolean privateStaticMethodInvoked; + interface Generic { - boolean publicMethodInvoked; - boolean privateMethodInvoked; + X foo(); - public static void publicStaticMethod() { - publicStaticMethodInvoked = true; - } + Y foo(X x, Y y); - @SuppressWarnings("unused") - private static void privateStaticMethod() { - privateStaticMethodInvoked = true; - } + Z foo(Z[][] zees); - public void publicMethod() { - publicMethodInvoked = true; - } + int foo(T t); - @SuppressWarnings("unused") - private void privateMethod() { - privateMethodInvoked = true; - } + T foo(int i); } - static class FirstClass { - - class SecondClass { + class ClassWithSyntheticMethod { - class ThirdClass { - } - } + // The following lambda expression results in a synthetic method in the + // compiled byte code. + Comparable synthetic = number -> 0; } - static class ClassWithNestedClasses { + interface InterfaceWithOneDeclaredMethod { - class Nested1 { - } + void foo(); + } - class Nested2 { - } + interface InterfaceWithDefaultMethod { - static class Nested3 { + default void foo() { } } - interface InterfaceWithNestedClass { + static class InterfaceWithDefaultMethodImpl implements InterfaceWithDefaultMethod { + } + + interface InterfaceWithGenericDefaultMethod { - class Nested4 { + default void foo(N number) { } } - interface Interface45 extends InterfaceWithNestedClass { + static class InterfaceWithGenericDefaultMethodImpl implements InterfaceWithGenericDefaultMethod { - class Nested5 { + void foo(Double number) { } } - static class ClassExtendingClassWithNestedClasses extends ClassWithNestedClasses implements Interface45 { - } + static class InterfaceWithOverriddenGenericDefaultMethodImpl implements InterfaceWithGenericDefaultMethod { - abstract static class AbstractOuterClass { + @Override + public void foo(Long number) { + } - class InnerClass { + void foo(Double number) { } } - static class OuterClass extends AbstractOuterClass { + interface InterfaceWithStaticMethod { - // sibling of OuterClass due to common super type - static class StaticNestedSiblingClass extends AbstractOuterClass { + static void foo() { } + } - // sibling of OuterClass due to common super type - class InnerSiblingClass extends AbstractOuterClass { - } + static class InterfaceWithStaticMethodImpl implements InterfaceWithStaticMethod { + } - static class StaticNestedClass extends OuterClass { - } + static class MyClass { - class RecursiveInnerClass extends OuterClass { - } + static final int staticField = 42; - class InnerClass { - class RecursiveInnerInnerClass extends OuterClass { - } + final int instanceField; + + MyClass(int value) { + this.instanceField = value; } } - static class OuterClassImplementingInterface implements InterfaceWithNestedClass { + static class MySubClass extends MyClass { - class InnerClassImplementingInterface implements InterfaceWithNestedClass { + MySubClass(int value) { + super(value); } } @@ -1811,6 +2129,18 @@ public void otherMethod3() { } } + // "public" modifier is necessary here, so that the compiler creates a bridge method. + public static class PublicChildClass extends ParentClass { + + @Override + public void otherMethod1() { + } + + @Override + public void otherMethod2() { + } + } + interface MethodShadowingInterface { default void method1(String string) { @@ -1886,53 +2216,37 @@ static void method5(Long i) { } } - // "public" modifier is necessary here, so that the compiler creates a bridge method. - public static class PublicChildClass extends ParentClass { - - @Override - public void otherMethod1() { - } + abstract static class AbstractOuterClass { - @Override - public void otherMethod2() { + class InnerClass { } } - public static class ClassWithFields { - - public static final String CONST = "constant"; - - public static final Integer CONST_INTEGER = 99; - - public static final Double CONST_DOUBLE = 2.5; - - public final String stringField = "text"; - - @SuppressWarnings("unused") - private final String privateStringField = "enigma"; - - final String nullStringField = null; + static class OuterClass extends AbstractOuterClass { - public final int integerField = 42; + // sibling of OuterClass due to common super type + static class StaticNestedSiblingClass extends AbstractOuterClass { + } - public final double doubleField = 3.14; + // sibling of OuterClass due to common super type + class InnerSiblingClass extends AbstractOuterClass { + } - } + static class StaticNestedClass extends OuterClass { + } - @SuppressWarnings("unused") - private static class ClassWithOneCustomConstructor { + class RecursiveInnerClass extends OuterClass { + } - ClassWithOneCustomConstructor(String str) { + class InnerClass { + class RecursiveInnerInnerClass extends OuterClass { + } } } - @SuppressWarnings("unused") - private static class ClassWithTwoConstructors { - - ClassWithTwoConstructors() { - } + static class OuterClassImplementingInterface implements InterfaceWithNestedClass { - ClassWithTwoConstructors(String str) { + class InnerClassImplementingInterface implements InterfaceWithNestedClass { } } diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsWithGenericTypeHierarchiesTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsWithGenericTypeHierarchiesTests.java index 8e8bb060be53..75fbe7376782 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsWithGenericTypeHierarchiesTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsWithGenericTypeHierarchiesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/RuntimeUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/RuntimeUtilsTests.java index 4174ab46b576..0367fcdcbf72 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/RuntimeUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/RuntimeUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/SerializationUtils.java b/platform-tests/src/test/java/org/junit/platform/commons/util/SerializationUtils.java index f6989b4e9f62..deaf5e207f9d 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/SerializationUtils.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/SerializationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/StringUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/StringUtilsTests.java index d74b1d523e21..c90dbd58c32b 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/StringUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/StringUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ToStringBuilderTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ToStringBuilderTests.java index 9802f344c582..ddf167ff89d3 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ToStringBuilderTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ToStringBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/AExecutionConditionClass.java b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/AExecutionConditionClass.java index d9d4b2ff73dd..18367db12db0 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/AExecutionConditionClass.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/AExecutionConditionClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/ATestExecutionListenerClass.java b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/ATestExecutionListenerClass.java index 99ad33e0b01e..319fb326daa6 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/ATestExecutionListenerClass.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/ATestExecutionListenerClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/AVanillaEmpty.java b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/AVanillaEmpty.java index bda71e43b3ec..1cd156254f05 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/AVanillaEmpty.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/AVanillaEmpty.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BExecutionConditionClass.java b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BExecutionConditionClass.java index a06b33004175..3767c86e9178 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BExecutionConditionClass.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BExecutionConditionClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BTestExecutionListenerClass.java b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BTestExecutionListenerClass.java index f8e9f5f2428d..dd80bad88608 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BTestExecutionListenerClass.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BTestExecutionListenerClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BVanillaEmpty.java b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BVanillaEmpty.java index 0ddbb17213a9..b5e683adb975 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BVanillaEmpty.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/BVanillaEmpty.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/CustomType.java b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/CustomType.java index 8a56cae42072..3088ef0dda2a 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/classes/CustomType.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/classes/CustomType.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/ClassLevelDir.java b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/ClassLevelDir.java new file mode 100644 index 000000000000..e35f513135e1 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/ClassLevelDir.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util.pkg1; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Mimics {@code @TempDir}. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ClassLevelDir { +} diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/InstanceLevelDir.java b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/InstanceLevelDir.java new file mode 100644 index 000000000000..a8f31bb10a2d --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/InstanceLevelDir.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util.pkg1; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Mimics {@code @TempDir}. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface InstanceLevelDir { +} diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/SuperclassWithStaticPackagePrivateBeforeMethod.java b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/SuperclassWithStaticPackagePrivateBeforeMethod.java new file mode 100644 index 000000000000..493a8a2bbc24 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/SuperclassWithStaticPackagePrivateBeforeMethod.java @@ -0,0 +1,25 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util.pkg1; + +import org.junit.jupiter.api.BeforeAll; + +/** + * @see https://github.com/junit-team/junit5/issues/3553 + */ +public class SuperclassWithStaticPackagePrivateBeforeMethod { + + @BeforeAll + static void before() { + // no-op + } + +} diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/SuperclassWithStaticPackagePrivateTempDirField.java b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/SuperclassWithStaticPackagePrivateTempDirField.java new file mode 100644 index 000000000000..778076cf14d2 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/SuperclassWithStaticPackagePrivateTempDirField.java @@ -0,0 +1,23 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util.pkg1; + +import java.nio.file.Path; + +/** + * @see https://github.com/junit-team/junit5/issues/3553 + */ +public class SuperclassWithStaticPackagePrivateTempDirField { + + @ClassLevelDir + static Path tempDir; + +} diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/subpkg/SubclassWithNonStaticPackagePrivateBeforeMethod.java b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/subpkg/SubclassWithNonStaticPackagePrivateBeforeMethod.java new file mode 100644 index 000000000000..0cc671a42b28 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/subpkg/SubclassWithNonStaticPackagePrivateBeforeMethod.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util.pkg1.subpkg; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateBeforeMethod; + +/** + * @see https://github.com/junit-team/junit5/issues/3553 + */ +public class SubclassWithNonStaticPackagePrivateBeforeMethod extends SuperclassWithStaticPackagePrivateBeforeMethod { + + @BeforeEach + void before() { + // no-op + } + +} diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/subpkg/SubclassWithNonStaticPackagePrivateTempDirField.java b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/subpkg/SubclassWithNonStaticPackagePrivateTempDirField.java new file mode 100644 index 000000000000..a6749d41c917 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/subpkg/SubclassWithNonStaticPackagePrivateTempDirField.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util.pkg1.subpkg; + +import java.nio.file.Path; + +import org.junit.platform.commons.util.pkg1.InstanceLevelDir; +import org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateTempDirField; + +/** + * @see https://github.com/junit-team/junit5/issues/3553 + */ +public class SubclassWithNonStaticPackagePrivateTempDirField extends SuperclassWithStaticPackagePrivateTempDirField { + + @InstanceLevelDir + Path tempDir; + +} diff --git a/platform-tests/src/test/java/org/junit/platform/console/ConsoleDetailsTests.java b/platform-tests/src/test/java/org/junit/platform/console/ConsoleDetailsTests.java index 1ed5017d39f7..b40aa222c0ec 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/ConsoleDetailsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/ConsoleDetailsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherIntegrationTests.java index 768ae68c13ec..c3338dfe6753 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java index b56c5dd1be42..ef30a6d3db59 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -12,6 +12,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.params.provider.Arguments.arguments; import java.io.PrintWriter; import java.io.StringWriter; @@ -19,6 +20,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.platform.console.tasks.ConsoleTestExecutor; @@ -30,34 +32,45 @@ class ConsoleLauncherTests { private final StringWriter stringWriter = new StringWriter(); private final PrintWriter printSink = new PrintWriter(stringWriter); - @ParameterizedTest(name = "{0}") + @ParameterizedTest(name = "cmd={0}") + @EmptySource @MethodSource("commandsWithEmptyOptionExitCodes") void displayHelp(String command) { - var consoleLauncher = new ConsoleLauncher((__, ___) -> null, printSink, printSink); + var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); var exitCode = consoleLauncher.run(command, "--help").getExitCode(); assertEquals(0, exitCode); - assertThat(stringWriter.toString()).contains("--help"); + assertThat(output()).contains("--help"); + } + + @ParameterizedTest(name = "cmd={0}") + @EmptySource + @MethodSource("commandsWithEmptyOptionExitCodes") + void displayVersion(String command) { + var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); + var exitCode = consoleLauncher.run(command, "--version").getExitCode(); + + assertEquals(0, exitCode); + assertThat(output()).contains("JUnit Platform Console Launcher"); } @ParameterizedTest(name = "{0}") @MethodSource("commandsWithEmptyOptionExitCodes") void displayBanner(String command) { - var consoleLauncher = new ConsoleLauncher((__, ___) -> null, printSink, printSink); + var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); consoleLauncher.run(command); - assertThat(stringWriter.toString()).contains( - "Thanks for using JUnit! Support its development at https://junit.org/sponsoring"); + assertThat(output()).contains("Thanks for using JUnit!"); } @ParameterizedTest(name = "{0}") @MethodSource("commandsWithEmptyOptionExitCodes") void disableBanner(String command, int expectedExitCode) { - var consoleLauncher = new ConsoleLauncher((__, ___) -> null, printSink, printSink); + var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); var exitCode = consoleLauncher.run(command, "--disable-banner").getExitCode(); assertEquals(expectedExitCode, exitCode); - assertThat(stringWriter.toString()).doesNotContain("Thanks for using JUnit!"); + assertThat(output()).doesNotContain("Thanks for using JUnit!"); } @ParameterizedTest(name = "{0}") @@ -67,7 +80,11 @@ void executeWithUnknownCommandLineOption(String command) { var exitCode = consoleLauncher.run(command, "--all").getExitCode(); assertEquals(-1, exitCode); - assertThat(stringWriter.toString()).contains("Unknown option: '--all'").contains("Usage:"); + assertThat(output()).contains("Unknown option: '--all'").contains("Usage:"); + } + + private String output() { + return stringWriter.toString(); } @ParameterizedTest(name = "{0}") @@ -81,9 +98,9 @@ void executeWithoutCommandLineOptions(String command, int expectedExitCode) { static Stream commandsWithEmptyOptionExitCodes() { return Stream.of( // - Arguments.of("execute", -1), // - Arguments.of("discover", -1), // - Arguments.of("engines", 0) // + arguments("execute", -1), // + arguments("discover", -1), // + arguments("engines", 0) // ); } diff --git a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java index f2349908aeed..7ae6ba8fd921 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java +++ b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapperResult.java b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapperResult.java index 9cf164a31b65..d0efb56e0e35 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapperResult.java +++ b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapperResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/options/CommandLineOptionsParsingTests.java b/platform-tests/src/test/java/org/junit/platform/console/options/CommandLineOptionsParsingTests.java index 03d2d92e77ee..d34740b1b0d6 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/options/CommandLineOptionsParsingTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/options/CommandLineOptionsParsingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -42,6 +42,8 @@ import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; +import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.discovery.DiscoverySelectors; /** * @since 1.10 @@ -536,11 +538,29 @@ void parseValidConfigurationParameters(ArgsType type) { // @formatter:on } + @ParameterizedTest + @EnumSource + void parseValidConfigurationParametersResource(ArgsType type) { + // @formatter:off + assertAll( + () -> assertThat(type.parseArgLine("--config-resource foo.properties").discovery.getConfigurationParametersResources()) + .containsOnly("foo.properties"), + () -> assertThat(type.parseArgLine("--config-resource foo.properties --config-resource bar.properties").discovery.getConfigurationParametersResources()) + .containsExactly("foo.properties", "bar.properties") + ); + // @formatter:on + } + @Test void parseInvalidConfigurationParameters() { assertOptionWithMissingRequiredArgumentThrowsException("-config", "--config"); } + @Test + void parseInvalidConfigurationParametersResource() { + assertOptionWithMissingRequiredArgumentThrowsException("--config-resource"); + } + @ParameterizedTest @EnumSource void parseInvalidConfigurationParametersWithDuplicateKey(ArgsType type) { @@ -549,6 +569,32 @@ void parseInvalidConfigurationParametersWithDuplicateKey(ArgsType type) { assertThat(e.getMessage()).isEqualTo("Duplicate key 'foo' for values 'bar' and 'baz'."); } + @ParameterizedTest + @EnumSource + void parseValidSelectorIdentifier(ArgsType type) { + // @formatter:off + assertAll( + () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), parseIdentifiers(type,"--select resource:/foo.csv")), + () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), parseIdentifiers(type,"--select method:com.acme.Foo#m()")), + () -> assertEquals(List.of(selectClass("com.acme.Foo")), parseIdentifiers(type,"--select class:com.acme.Foo")), + () -> assertEquals(List.of(selectPackage("com.acme.foo")), parseIdentifiers(type,"--select package:com.acme.foo")), + () -> assertEquals(List.of(selectModule("com.acme.foo")), parseIdentifiers(type,"--select module:com.acme.foo")), + () -> assertEquals(List.of(selectDirectory("foo/bar")), parseIdentifiers(type,"--select directory:foo/bar")), + () -> assertEquals(List.of(selectFile("foo.txt"), selectUri("file:///foo.txt")), parseIdentifiers(type,"--select file:foo.txt --select uri:file:///foo.txt")) + ); + // @formatter:on + } + + private static List parseIdentifiers(ArgsType type, String argLine) + throws IOException { + return DiscoverySelectors.parseAll(type.parseArgLine(argLine).discovery.getSelectorIdentifiers()).toList(); + } + + @Test + void parseInvalidSelectorIdentifier() { + assertOptionWithMissingRequiredArgumentThrowsException("--select"); + } + private void assertOptionWithMissingRequiredArgumentThrowsException(String... options) { assertAll( Stream.of(options).map(opt -> () -> assertThrows(Exception.class, () -> ArgsType.args.parseArgLine(opt)))); @@ -574,6 +620,7 @@ Result parseArgLine(String argLine) throws IOException { } } }; + abstract Result parseArgLine(String argLine) throws IOException; private static String[] split(String argLine) { diff --git a/platform-tests/src/test/java/org/junit/platform/console/options/ConsoleUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/console/options/ConsoleUtilsTests.java index 09e807eb902e..b322c1657afd 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/options/ConsoleUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/options/ConsoleUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/options/ExecuteTestsCommandTests.java b/platform-tests/src/test/java/org/junit/platform/console/options/ExecuteTestsCommandTests.java index ef867d0fe4ee..cddf92b68611 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/options/ExecuteTestsCommandTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/options/ExecuteTestsCommandTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/options/ThemeTests.java b/platform-tests/src/test/java/org/junit/platform/console/options/ThemeTests.java index 8c96fed4c58d..cb49cbf631f4 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/options/ThemeTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/options/ThemeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForInnerTest.java b/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForInnerTest.java index b8181cae63d5..287cbbc35005 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForInnerTest.java +++ b/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForInnerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForInnerTests.java b/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForInnerTests.java index 410e9d8bd139..bd7d2accebc1 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForInnerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForInnerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForStaticNestedTest.java b/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForStaticNestedTest.java index 8143890bb2d3..ecbe48c2e587 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForStaticNestedTest.java +++ b/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForStaticNestedTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForStaticNestedTests.java b/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForStaticNestedTests.java index c13387258398..9f51d250dee0 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForStaticNestedTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForStaticNestedTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/subpackage/SecondTest.java b/platform-tests/src/test/java/org/junit/platform/console/subpackage/SecondTest.java index 56bccb3ee8bd..cdc962fe84d0 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/subpackage/SecondTest.java +++ b/platform-tests/src/test/java/org/junit/platform/console/subpackage/SecondTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/subpackage/Test.java b/platform-tests/src/test/java/org/junit/platform/console/subpackage/Test.java index bcecdc12595a..cf5d40fd7afe 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/subpackage/Test.java +++ b/platform-tests/src/test/java/org/junit/platform/console/subpackage/Test.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/subpackage/Test1.java b/platform-tests/src/test/java/org/junit/platform/console/subpackage/Test1.java index d554e0b23d55..af3e80934eab 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/subpackage/Test1.java +++ b/platform-tests/src/test/java/org/junit/platform/console/subpackage/Test1.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/subpackage/Tests.java b/platform-tests/src/test/java/org/junit/platform/console/subpackage/Tests.java index 52142a320f42..8cb8a8c507fc 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/subpackage/Tests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/subpackage/Tests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/subpackage/ThirdTests.java b/platform-tests/src/test/java/org/junit/platform/console/subpackage/ThirdTests.java index 9de75239692c..88fec97e3acd 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/subpackage/ThirdTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/subpackage/ThirdTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/tasks/ColorPaletteTests.java b/platform-tests/src/test/java/org/junit/platform/console/tasks/ColorPaletteTests.java index 3a0909c35edb..fce602b53011 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/tasks/ColorPaletteTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/tasks/ColorPaletteTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/tasks/ConsoleTestExecutorTests.java b/platform-tests/src/test/java/org/junit/platform/console/tasks/ConsoleTestExecutorTests.java index 7c51142118d8..9f04b044f370 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/tasks/ConsoleTestExecutorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/tasks/ConsoleTestExecutorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/tasks/CustomContextClassLoaderExecutorTests.java b/platform-tests/src/test/java/org/junit/platform/console/tasks/CustomContextClassLoaderExecutorTests.java index ef53d1fe001e..a3b72bb4692c 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/tasks/CustomContextClassLoaderExecutorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/tasks/CustomContextClassLoaderExecutorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java b/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java index 87b35ef20360..b8eea36cfd1e 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -303,6 +303,19 @@ void propagatesIterationSelectors() { assertThat(iterationSelectors.get(1).getIterationIndices()).containsExactly(2); } + @Test + void propagatesSelectorIdentifiers() { + var methodSelector = selectMethod("com.acme.Foo#m()"); + var classSelector = selectClass("com.example.Bar"); + options.setSelectorIdentifiers( + List.of(methodSelector.toIdentifier().orElseThrow(), classSelector.toIdentifier().orElseThrow())); + + var request = convert(); + + assertThat(request.getSelectorsByType(MethodSelector.class)).containsExactly(methodSelector); + assertThat(request.getSelectorsByType(ClassSelector.class)).containsExactly(classSelector); + } + @Test @SuppressWarnings("deprecation") void convertsConfigurationParameters() { @@ -317,6 +330,22 @@ void convertsConfigurationParameters() { assertThat(configurationParameters.getBoolean("baz")).contains(true); } + @Test + @SuppressWarnings("deprecation") + void convertsConfigurationParametersResources() { + options.setScanClasspath(true); + options.setConfigurationParameters(mapOf(entry("foo", "bar"), entry("com.example.prop.first", "baz"))); + options.setConfigurationParametersResources(List.of("config-test.properties")); + + var request = convert(); + var configurationParameters = request.getConfigurationParameters(); + + assertThat(configurationParameters.size()).isEqualTo(2); + assertThat(configurationParameters.get("foo")).contains("bar"); + assertThat(configurationParameters.get("com.example.prop.first")).contains("baz"); + assertThat(configurationParameters.get("com.example.prop.second")).contains("second value"); + } + private LauncherDiscoveryRequest convert() { var creator = new DiscoveryRequestCreator(); return creator.toDiscoveryRequest(options); diff --git a/platform-tests/src/test/java/org/junit/platform/console/tasks/FlatPrintingListenerTests.java b/platform-tests/src/test/java/org/junit/platform/console/tasks/FlatPrintingListenerTests.java index a79bd43f85ae..8b0c7a5a29b4 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/tasks/FlatPrintingListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/tasks/FlatPrintingListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/tasks/TestFeedPrintingListenerTests.java b/platform-tests/src/test/java/org/junit/platform/console/tasks/TestFeedPrintingListenerTests.java index 2006f269782c..c6443a624f4e 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/tasks/TestFeedPrintingListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/tasks/TestFeedPrintingListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/console/tasks/TreeNodeTests.java b/platform-tests/src/test/java/org/junit/platform/console/tasks/TreeNodeTests.java index d17f340c481e..93cf99cbcd80 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/tasks/TreeNodeTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/tasks/TreeNodeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -67,6 +67,7 @@ void reportEntriesCanBeAddedConcurrently() throws Exception { assertThat(treeNode.reports).hasSize(NUM_THREADS * ITEMS_PER_THREAD); } + @SuppressWarnings("resource") private void runConcurrently(Runnable action) throws InterruptedException { ExecutorService executor = new ThreadPoolExecutor(NUM_THREADS, NUM_THREADS, 10, SECONDS, new ArrayBlockingQueue<>(NUM_THREADS)); diff --git a/platform-tests/src/test/java/org/junit/platform/console/tasks/TreePrinterTests.java b/platform-tests/src/test/java/org/junit/platform/console/tasks/TreePrinterTests.java index f7d5982754fc..8316437899d3 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/tasks/TreePrinterTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/tasks/TreePrinterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -19,7 +19,6 @@ import java.io.ByteArrayOutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; @@ -39,13 +38,8 @@ class TreePrinterTests { private final PrintWriter out = new PrintWriter(new OutputStreamWriter(stream, charset)); private List actual() { - try { - out.flush(); - return List.of(stream.toString(charset.name()).split("\\R")); - } - catch (UnsupportedEncodingException e) { - throw new AssertionError(charset.name() + " is an unsupported encoding?!", e); - } + out.flush(); + return List.of(stream.toString(charset).split("\\R")); } @Test diff --git a/platform-tests/src/test/java/org/junit/platform/console/tasks/VerboseTreeListenerTests.java b/platform-tests/src/test/java/org/junit/platform/console/tasks/VerboseTreeListenerTests.java index 2d140433a36a..52c74188e08a 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/tasks/VerboseTreeListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/tasks/VerboseTreeListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/FilterCompositionTests.java b/platform-tests/src/test/java/org/junit/platform/engine/FilterCompositionTests.java index 422d86df3ea7..92a7f879e960 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/FilterCompositionTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/FilterCompositionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/TestDescriptorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/TestDescriptorTests.java index a98e1ec35af0..6b8907e463dd 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/TestDescriptorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/TestDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/TestTagTests.java b/platform-tests/src/test/java/org/junit/platform/engine/TestTagTests.java index d334b2f9a4a0..e18b6cc990f9 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/TestTagTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/TestTagTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/UniqueIdFormatTests.java b/platform-tests/src/test/java/org/junit/platform/engine/UniqueIdFormatTests.java index 6913a7dc289c..ee6c731d6643 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/UniqueIdFormatTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/UniqueIdFormatTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/UniqueIdTests.java b/platform-tests/src/test/java/org/junit/platform/engine/UniqueIdTests.java index 9c87d2611314..c126da921107 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/UniqueIdTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/UniqueIdTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClassNameFilterTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClassNameFilterTests.java index 622f00413342..990c1dc1e179 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClassNameFilterTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClassNameFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClassSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClassSelectorTests.java index 2280fa8173b9..8799c8bf5a89 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClassSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClassSelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClasspathResourceSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClasspathResourceSelectorTests.java index 1755982663c3..ae696da1de20 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClasspathResourceSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClasspathResourceSelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClasspathRootSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClasspathRootSelectorTests.java index 3f23e8aabc91..bfdb69c072e1 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClasspathRootSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ClasspathRootSelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/DirectorySelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/DirectorySelectorTests.java index 59f6522bb54a..1ae830d99289 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/DirectorySelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/DirectorySelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/DiscoverySelectorsTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/DiscoverySelectorsTests.java index dfb967afd9fd..5a1a96506927 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/DiscoverySelectorsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/DiscoverySelectorsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,9 +11,12 @@ package org.junit.platform.engine.discovery; import static java.lang.String.join; +import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.type; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForMethod; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathResource; @@ -26,6 +29,7 @@ import static org.junit.platform.engine.discovery.DiscoverySelectors.selectNestedClass; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectNestedMethod; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectUri; import java.io.File; @@ -33,7 +37,9 @@ import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -49,6 +55,8 @@ import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.test.TestClassLoader; import org.junit.platform.commons.util.ReflectionUtils; +import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; /** * Unit tests for {@link DiscoverySelectors}. @@ -73,11 +81,11 @@ void selectUriByName() { } @Test - void selectUriByURI() throws Exception { + void selectUriByURI() { assertViolatesPrecondition(() -> selectUri((URI) null)); assertViolatesPrecondition(() -> selectUri(" ")); - var uri = new URI("https://junit.org"); + var uri = URI.create("https://junit.org"); var selector = selectUri(uri); assertEquals(uri, selector.getUri()); @@ -88,6 +96,15 @@ void selectUriByURI() throws Exception { @Nested class SelectFileTests { + @Test + void parseUriSelector() { + var selector = parseIdentifier(selectUri("https://junit.org")); + assertThat(selector) // + .asInstanceOf(type(UriSelector.class)) // + .extracting(UriSelector::getUri) // + .isEqualTo(URI.create("https://junit.org")); + } + @Test void selectFileByName() { assertViolatesPrecondition(() -> selectFile((String) null)); @@ -113,7 +130,7 @@ void selectFileByNameAndPosition() { assertEquals(path, selector.getRawPath()); assertEquals(new File(path), selector.getFile()); assertEquals(Paths.get(path), selector.getPath()); - assertEquals(filePosition, selector.getPosition().get()); + assertEquals(filePosition, selector.getPosition().orElseThrow()); } @Test @@ -147,7 +164,7 @@ void selectFileByFileReferenceAndPosition() throws Exception { assertEquals(path, selector.getRawPath()); assertEquals(file.getCanonicalFile(), selector.getFile()); assertEquals(Paths.get(path), selector.getPath()); - assertEquals(FilePosition.from(12, 34), selector.getPosition().get()); + assertEquals(FilePosition.from(12, 34), selector.getPosition().orElseThrow()); } } @@ -155,6 +172,59 @@ void selectFileByFileReferenceAndPosition() throws Exception { @Nested class SelectDirectoryTests { + @Test + void parseFileSelectorWithRelativePath() { + var path = "src/test/resources/do_not_delete_me.txt"; + var selector = parseIdentifier(selectFile(path)); + + assertThat(selector) // + .asInstanceOf(type(FileSelector.class)) // + .extracting(FileSelector::getRawPath, FileSelector::getFile, FileSelector::getPath, + FileSelector::getPosition) // + .containsExactly(path, new File(path), Paths.get(path), Optional.empty()); + } + + @Test + void parseFileSelectorWithAbsolutePath() { + var path = "src/test/resources/do_not_delete_me.txt"; + var absolutePath = new File(path).getAbsolutePath(); + var selector = parseIdentifier(selectFile(absolutePath)); + + assertThat(selector) // + .asInstanceOf(type(FileSelector.class)) // + .extracting(FileSelector::getRawPath, FileSelector::getFile, FileSelector::getPath, + FileSelector::getPosition) // + .containsExactly(absolutePath, new File(absolutePath), Paths.get(absolutePath), Optional.empty()); + } + + @Test + void parseFileSelectorWithRelativePathAndFilePosition() { + var path = "src/test/resources/do_not_delete_me.txt"; + var filePosition = FilePosition.from(12, 34); + var selector = parseIdentifier(selectFile(path, filePosition)); + + assertThat(selector) // + .asInstanceOf(type(FileSelector.class)) // + .extracting(FileSelector::getRawPath, FileSelector::getFile, FileSelector::getPath, + FileSelector::getPosition) // + .containsExactly(path, new File(path), Paths.get(path), Optional.of(filePosition)); + } + + @Test + void parseFileSelectorWithAbsolutePathAndFilePosition() { + var path = "src/test/resources/do_not_delete_me.txt"; + var absolutePath = new File(path).getAbsolutePath(); + var filePosition = FilePosition.from(12, 34); + var selector = parseIdentifier(selectFile(absolutePath, filePosition)); + + assertThat(selector) // + .asInstanceOf(type(FileSelector.class)) // + .extracting(FileSelector::getRawPath, FileSelector::getFile, FileSelector::getPath, + FileSelector::getPosition) // + .containsExactly(absolutePath, new File(absolutePath), Paths.get(absolutePath), + Optional.of(filePosition)); + } + @Test void selectDirectoryByName() { assertViolatesPrecondition(() -> selectDirectory((String) null)); @@ -189,6 +259,32 @@ void selectDirectoryByFileReference() throws Exception { @Nested class SelectClasspathResourceTests { + @Test + void parseDirectorySelectorWithRelativePath() { + var path = "src/test/resources"; + + var selector = parseIdentifier(selectDirectory(path)); + + assertThat(selector) // + .asInstanceOf(type(DirectorySelector.class)) // + .extracting(DirectorySelector::getRawPath, DirectorySelector::getDirectory, + DirectorySelector::getPath) // + .containsExactly(path, new File(path), Paths.get(path)); + } + + @Test + void parseDirectorySelectorWithAbsolutePath() { + var path = new File("src/test/resources").getAbsolutePath(); + + var selector = parseIdentifier(selectDirectory(path)); + + assertThat(selector) // + .asInstanceOf(type(DirectorySelector.class)) // + .extracting(DirectorySelector::getRawPath, DirectorySelector::getDirectory, + DirectorySelector::getPath) // + .containsExactly(path, new File(path), Paths.get(path)); + } + @Test void selectClasspathResources() { assertViolatesPrecondition(() -> selectClasspathResource(null)); @@ -216,12 +312,51 @@ void selectClasspathResourcesWithFilePosition() { // with unnecessary "/" prefix var selector = selectClasspathResource("/foo/bar/spec.xml", filePosition); assertEquals("foo/bar/spec.xml", selector.getClasspathResourceName()); - assertEquals(FilePosition.from(12, 34), selector.getPosition().get()); + assertEquals(FilePosition.from(12, 34), selector.getPosition().orElseThrow()); // standard use case selector = selectClasspathResource("A/B/C/spec.json", filePosition); assertEquals("A/B/C/spec.json", selector.getClasspathResourceName()); - assertEquals(filePosition, selector.getPosition().get()); + assertEquals(filePosition, selector.getPosition().orElseThrow()); + } + + @Test + void parseClasspathResources() { + // with unnecessary "/" prefix + var selector = parseIdentifier(selectClasspathResource("/foo/bar/spec.xml")); + assertThat(selector) // + .asInstanceOf(type(ClasspathResourceSelector.class)) // + .extracting(ClasspathResourceSelector::getClasspathResourceName, + ClasspathResourceSelector::getPosition) // + .containsExactly("foo/bar/spec.xml", Optional.empty()); + + // standard use case + selector = parseIdentifier(selectClasspathResource("A/B/C/spec.json")); + assertThat(selector) // + .asInstanceOf(type(ClasspathResourceSelector.class)) // + .extracting(ClasspathResourceSelector::getClasspathResourceName, + ClasspathResourceSelector::getPosition) // + .containsExactly("A/B/C/spec.json", Optional.empty()); + } + + @Test + void parseClasspathResourcesWithFilePosition() { + var filePosition = FilePosition.from(12, 34); + // with unnecessary "/" prefix + var selector = parseIdentifier(selectClasspathResource("/foo/bar/spec.xml", FilePosition.from(12, 34))); + assertThat(selector) // + .asInstanceOf(type(ClasspathResourceSelector.class)) // + .extracting(ClasspathResourceSelector::getClasspathResourceName, + ClasspathResourceSelector::getPosition) // + .containsExactly("foo/bar/spec.xml", Optional.of(filePosition)); + + // standard use case + selector = parseIdentifier(selectClasspathResource("A/B/C/spec.json", FilePosition.from(12, 34))); + assertThat(selector) // + .asInstanceOf(type(ClasspathResourceSelector.class)) // + .extracting(ClasspathResourceSelector::getClasspathResourceName, + ClasspathResourceSelector::getPosition) // + .containsExactly("A/B/C/spec.json", Optional.of(filePosition)); } } @@ -235,6 +370,15 @@ void selectModuleByName() { assertEquals("java.base", selector.getModuleName()); } + @Test + void parseModuleByName() { + var selector = parseIdentifier(selectModule("java.base")); + assertThat(selector) // + .asInstanceOf(type(ModuleSelector.class)) // + .extracting(ModuleSelector::getModuleName) // + .isEqualTo("java.base"); + } + @Test void selectModuleByNamePreconditions() { assertViolatesPrecondition(() -> selectModule(null)); @@ -266,6 +410,15 @@ void selectPackageByName() { assertEquals(getClass().getPackage().getName(), selector.getPackageName()); } + @Test + void parsePackageByName() { + var selector = parseIdentifier(selectPackage(getClass().getPackage().getName())); + assertThat(selector) // + .asInstanceOf(type(PackageSelector.class)) // + .extracting(PackageSelector::getPackageName) // + .isEqualTo(getClass().getPackage().getName()); + } + } @Nested @@ -294,7 +447,7 @@ void selectClasspathRootsWithExistingDirectory(@TempDir Path tempDir) { @Test void selectClasspathRootsWithExistingJarFile() throws Exception { - var jarUri = getClass().getResource("/jartest.jar").toURI(); + var jarUri = requireNonNull(getClass().getResource("/jartest.jar")).toURI(); var jarFile = Paths.get(jarUri); var selectors = selectClasspathRoots(Set.of(jarFile)); @@ -313,6 +466,15 @@ void selectClassByName() { assertEquals(getClass(), selector.getJavaClass()); } + @Test + void pareClassByName() { + var selector = parseIdentifier(selectClass(getClass())); + assertThat(selector) // + .asInstanceOf(type(ClassSelector.class)) // + .extracting(ClassSelector::getJavaClass) // + .isEqualTo(getClass()); + } + @Test void selectClassByNameWithExplicitClassLoader() throws Exception { try (var testClassLoader = TestClassLoader.forClasses(getClass())) { @@ -333,7 +495,7 @@ class SelectMethodTests { @Test @DisplayName("Preconditions: selectMethod(className, methodName)") void selectMethodByClassNameAndMethodNamePreconditions() { - assertViolatesPrecondition(() -> selectMethod("TestClass", (String) null)); + assertViolatesPrecondition(() -> selectMethod("TestClass", null)); assertViolatesPrecondition(() -> selectMethod("TestClass", "")); assertViolatesPrecondition(() -> selectMethod("TestClass", " ")); assertViolatesPrecondition(() -> selectMethod((String) null, "method")); @@ -422,7 +584,8 @@ static Stream invalidFullyQualifiedMethodNames() { void selectMethodByFullyQualifiedName() throws Exception { Class clazz = testClass(); var method = clazz.getDeclaredMethod("myTest"); - assertSelectMethodByFullyQualifiedName(clazz, method); + var selector = assertSelectMethodByFullyQualifiedName(clazz, method); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -445,88 +608,110 @@ void selectMethodByFullyQualifiedNameWithExplicitClassLoader() throws Exception void selectMethodByFullyQualifiedNameForDefaultMethodInInterface() throws Exception { Class clazz = TestCaseWithDefaultMethod.class; var method = clazz.getMethod("myTest"); - assertSelectMethodByFullyQualifiedName(clazz, method); + var selector = assertSelectMethodByFullyQualifiedName(clazz, method); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithPrimitiveParameter() throws Exception { var method = testClass().getDeclaredMethod("myTest", int.class); - assertSelectMethodByFullyQualifiedName(testClass(), method, int.class, "int"); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, int.class, "int"); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithPrimitiveParameterUsingSourceCodeSyntax() throws Exception { var method = testClass().getDeclaredMethod("myTest", int.class); - assertSelectMethodByFullyQualifiedName(testClass(), method, "int", "int"); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, "int", "int"); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithObjectParameter() throws Exception { var method = testClass().getDeclaredMethod("myTest", String.class); - assertSelectMethodByFullyQualifiedName(testClass(), method, String.class, String.class.getName()); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, String.class, + String.class.getName()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithObjectParameterUsingSourceCodeSyntax() throws Exception { var method = testClass().getDeclaredMethod("myTest", String.class); - assertSelectMethodByFullyQualifiedName(testClass(), method, "java.lang.String", String.class.getName()); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, "java.lang.String", + String.class.getName()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithPrimitiveArrayParameter() throws Exception { var method = testClass().getDeclaredMethod("myTest", int[].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, int[].class, int[].class.getName()); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, int[].class, + int[].class.getName()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithPrimitiveArrayParameterUsingSourceCodeSyntax() throws Exception { var method = testClass().getDeclaredMethod("myTest", int[].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, "int[]", "int[]"); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, "int[]", "int[]"); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithObjectArrayParameter() throws Exception { var method = testClass().getDeclaredMethod("myTest", String[].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, String[].class, String[].class.getName()); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, String[].class, + String[].class.getName()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithObjectArrayParameterUsingSourceCodeSyntax() throws Exception { var method = testClass().getDeclaredMethod("myTest", String[].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, "java.lang.String[]", "java.lang.String[]"); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, "java.lang.String[]", + "java.lang.String[]"); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithTwoDimensionalPrimitiveArrayParameter() throws Exception { var method = testClass().getDeclaredMethod("myTest", int[][].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, int[][].class, int[][].class.getName()); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, int[][].class, + int[][].class.getName()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithTwoDimensionalPrimitiveArrayParameterUsingSourceCodeSyntax() throws Exception { var method = testClass().getDeclaredMethod("myTest", int[][].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, "int[][]", "int[][]"); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, "int[][]", "int[][]"); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithTwoDimensionalObjectArrayParameter() throws Exception { var method = testClass().getDeclaredMethod("myTest", String[][].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, String[][].class, String[][].class.getName()); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, String[][].class, + String[][].class.getName()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithTwoDimensionalObjectArrayParameterUsingSourceCodeSyntax() throws Exception { var method = testClass().getDeclaredMethod("myTest", String[][].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, "java.lang.String[][]", "java.lang.String[][]"); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, "java.lang.String[][]", + "java.lang.String[][]"); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithMultidimensionalPrimitiveArrayParameter() throws Exception { var method = testClass().getDeclaredMethod("myTest", int[][][][][].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, int[][][][][].class, + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, int[][][][][].class, int[][][][][].class.getName()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -534,14 +719,17 @@ void selectMethodByFullyQualifiedNameWithMultidimensionalPrimitiveArrayParameter throws Exception { var method = testClass().getDeclaredMethod("myTest", int[][][][][].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, "int[][][][][]", "int[][][][][]"); + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, "int[][][][][]", + "int[][][][][]"); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameWithMultidimensionalObjectArrayParameter() throws Exception { var method = testClass().getDeclaredMethod("myTest", Double[][][][][].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, Double[][][][][].class, + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, Double[][][][][].class, Double[][][][][].class.getName()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -549,15 +737,16 @@ void selectMethodByFullyQualifiedNameWithMultidimensionalObjectArrayParameterUsi throws Exception { var method = testClass().getDeclaredMethod("myTest", Double[][][][][].class); - assertSelectMethodByFullyQualifiedName(testClass(), method, "java.lang.Double[][][][][]", + var selector = assertSelectMethodByFullyQualifiedName(testClass(), method, "java.lang.Double[][][][][]", "java.lang.Double[][][][][]"); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test void selectMethodByFullyQualifiedNameEndingInOpeningParenthesis() { var className = "org.example.MyClass"; // The following bizarre method name is not permissible in Java source - // code; however, it's permitted by the JVM -- for example, in Groovy + // code; however, it'selector permitted by the JVM -- for example, in Groovy // or Kotlin source code using back ticks. var methodName = ")--("; var fqmn = className + "#" + methodName; @@ -566,6 +755,7 @@ void selectMethodByFullyQualifiedNameEndingInOpeningParenthesis() { assertEquals(className, selector.getClassName()); assertEquals(methodName, selector.getMethodName()); assertEquals("", selector.getParameterTypeNames()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } /** @@ -581,6 +771,7 @@ void selectMethodByFullyQualifiedNameContainingHashtags() { assertEquals(className, selector.getClassName()); assertEquals(methodName, selector.getMethodName()); assertEquals("", selector.getParameterTypeNames()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } /** @@ -597,6 +788,7 @@ void selectMethodByFullyQualifiedNameContainingHashtagsAndWithParameterList() { assertEquals(className, selector.getClassName()); assertEquals(methodName, selector.getMethodName()); assertEquals(methodParameters, selector.getParameterTypeNames()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } /** @@ -613,6 +805,7 @@ void selectMethodByFullyQualifiedNameContainingParentheses() { assertEquals(className, selector.getClassName()); assertEquals(methodName, selector.getMethodName()); assertEquals("", selector.getParameterTypeNames()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } /** @@ -629,6 +822,7 @@ void selectMethodByFullyQualifiedNameEndingWithParentheses() { assertEquals(className, selector.getClassName()); assertEquals(methodName, selector.getMethodName()); assertEquals("", selector.getParameterTypeNames()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } /** @@ -646,19 +840,21 @@ void selectMethodByFullyQualifiedNameEndingWithParenthesesAndWithParameterList() assertEquals(className, selector.getClassName()); assertEquals(methodName, selector.getMethodName()); assertEquals(methodParameters, selector.getParameterTypeNames()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } - private void assertSelectMethodByFullyQualifiedName(Class clazz, Method method) { + private MethodSelector assertSelectMethodByFullyQualifiedName(Class clazz, Method method) { var selector = selectMethod(fqmn(clazz, method.getName())); assertEquals(method, selector.getJavaMethod()); assertEquals(clazz, selector.getJavaClass()); assertEquals(clazz.getName(), selector.getClassName()); assertEquals(method.getName(), selector.getMethodName()); assertEquals("", selector.getParameterTypeNames()); + return selector; } - private void assertSelectMethodByFullyQualifiedName(Class clazz, Method method, Class parameterType, - String expectedParameterTypes) { + private MethodSelector assertSelectMethodByFullyQualifiedName(Class clazz, Method method, + Class parameterType, String expectedParameterTypes) { var selector = selectMethod(fqmn(parameterType)); assertEquals(method, selector.getJavaMethod()); @@ -666,10 +862,11 @@ private void assertSelectMethodByFullyQualifiedName(Class clazz, Method metho assertEquals(clazz.getName(), selector.getClassName()); assertEquals(method.getName(), selector.getMethodName()); assertEquals(expectedParameterTypes, selector.getParameterTypeNames()); + return selector; } - private void assertSelectMethodByFullyQualifiedName(Class clazz, Method method, String parameterName, - String expectedParameterTypes) { + private MethodSelector assertSelectMethodByFullyQualifiedName(Class clazz, Method method, + String parameterName, String expectedParameterTypes) { var selector = selectMethod(fqmnWithParamNames(parameterName)); assertEquals(method, selector.getJavaMethod()); @@ -677,6 +874,7 @@ private void assertSelectMethodByFullyQualifiedName(Class clazz, Method metho assertEquals(clazz.getName(), selector.getClassName()); assertEquals(method.getName(), selector.getMethodName()); assertEquals(expectedParameterTypes, selector.getParameterTypeNames()); + return selector; } @Test @@ -689,6 +887,7 @@ void selectMethodByClassAndMethodName() throws Exception { assertEquals(method, selector.getJavaMethod()); assertEquals("myTest", selector.getMethodName()); assertEquals("", selector.getParameterTypeNames()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -704,6 +903,7 @@ void selectMethodByClassMethodNameAndParameterTypeNames() throws Exception { assertThat(selector.getJavaMethod()).isEqualTo(method); assertThat(selector.getParameterTypeNames()).isEqualTo("java.lang.String, boolean[]"); assertThat(selector.getParameterTypes()).containsExactly(String.class, boolean[].class); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -719,6 +919,7 @@ void selectMethodByClassNameMethodNameAndParameterTypes() throws Exception { assertThat(selector.getJavaMethod()).isEqualTo(method); assertThat(selector.getParameterTypeNames()).isEqualTo("java.lang.String, boolean[]"); assertThat(selector.getParameterTypes()).containsExactly(String.class, boolean[].class); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -739,6 +940,7 @@ void selectMethodByClassNameMethodNameAndParameterTypeNamesWithExplicitClassLoad assertThat(selector.getJavaMethod()).isEqualTo(method); assertThat(selector.getParameterTypeNames()).isEqualTo("java.lang.String, boolean[]"); assertThat(selector.getParameterTypes()).containsExactly(String.class, boolean[].class); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } } @@ -755,6 +957,7 @@ void selectMethodByClassMethodNameAndParameterTypes() throws Exception { assertThat(selector.getJavaMethod()).isEqualTo(method); assertThat(selector.getParameterTypeNames()).isEqualTo("java.lang.String, boolean[]"); assertThat(selector.getParameterTypes()).containsExactly(String.class, boolean[].class); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -770,6 +973,7 @@ void selectMethodWithParametersByMethodReference() throws Exception { assertThat(selector.getJavaMethod()).isEqualTo(method); assertThat(selector.getParameterTypeNames()).isEqualTo("java.lang.String, boolean[]"); assertThat(selector.getParameterTypes()).containsExactly(String.class, boolean[].class); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -788,6 +992,7 @@ void selectMethodByClassAndNameForSpockSpec() { assertEquals(className, selector.getClassName()); assertEquals(methodName, selector.getMethodName()); assertEquals("", selector.getParameterTypeNames()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } private static Class testClass() { @@ -796,6 +1001,41 @@ private static Class testClass() { } + @Test + void parseClasspathRootsWithNonExistingDirectory() { + var selectorStream = parseIdentifiers(selectClasspathRoots(Set.of(Path.of("some/local/path")))); + assertThat(selectorStream).isEmpty(); + } + + @Test + void parseClasspathRootsWithNonExistingJarFile() { + var selectorStream = parseIdentifiers(selectClasspathRoots(Set.of(Path.of("some.jar")))); + assertThat(selectorStream).isEmpty(); + } + + @Test + void parseClasspathRootsWithExistingDirectory(@TempDir Path tempDir) { + var selectorStream = parseIdentifiers(selectClasspathRoots(Set.of(tempDir))); + var selector = selectorStream.findAny().orElseThrow(); + assertThat(selector) // + .asInstanceOf(type(ClasspathRootSelector.class)) // + .extracting(ClasspathRootSelector::getClasspathRoot) // + .isEqualTo(tempDir.toUri()); + } + + @Test + void parseClasspathRootsWithExistingJarFile() throws Exception { + var jarUri = requireNonNull(getClass().getResource("/jartest.jar")).toURI(); + var jarPath = Path.of(jarUri); + + var selectorStream = parseIdentifiers(selectClasspathRoots(Set.of(jarPath))); + var selector = selectorStream.findAny().orElseThrow(); + assertThat(selector) // + .asInstanceOf(type(ClasspathRootSelector.class)) // + .extracting(ClasspathRootSelector::getClasspathRoot) // + .isEqualTo(jarUri); + } + @Nested class SelectNestedClassAndSelectNestedMethodTests { @@ -813,6 +1053,7 @@ void selectNestedClassByClassNames() { assertThat(selector.getEnclosingClassNames()).containsOnly(enclosingClassName); assertThat(selector.getNestedClassName()).isEqualTo(nestedClassName); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -831,6 +1072,7 @@ void selectNestedClassByClassNamesWithExplicitClassLoader() throws Exception { assertThat(selector.getEnclosingClasses()).extracting(Class::getClassLoader).containsOnly( testClassLoader); assertThat(selector.getNestedClass().getClassLoader()).isSameAs(testClassLoader); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } } @@ -845,6 +1087,7 @@ void selectDoubleNestedClassByClassNames() { assertThat(selector.getEnclosingClassNames()).containsExactly(enclosingClassName, nestedClassName); assertThat(selector.getNestedClassName()).isEqualTo(doubleNestedClassName); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -867,6 +1110,7 @@ void selectNestedMethodByEnclosingClassNamesAndMethodName() throws Exception { assertThat(selector.getEnclosingClassNames()).containsOnly(enclosingClassName); assertThat(selector.getNestedClassName()).isEqualTo(nestedClassName); assertThat(selector.getMethodName()).isEqualTo(methodName); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -888,6 +1132,7 @@ void selectNestedMethodByEnclosingClassNamesAndMethodNameWithExplicitClassLoader assertThat(selector.getNestedClass().getClassLoader()).isSameAs(testClassLoader); assertThat(selector.getMethodName()).isEqualTo(methodName); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } } @@ -903,6 +1148,7 @@ void selectNestedMethodByEnclosingClassesAndMethodName() throws Exception { assertThat(selector.getEnclosingClassNames()).containsOnly(enclosingClassName); assertThat(selector.getNestedClassName()).isEqualTo(nestedClassName); assertThat(selector.getMethodName()).isEqualTo(methodName); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -918,6 +1164,7 @@ void selectNestedMethodByEnclosingClassNamesMethodNameAndParameterTypeNames() th assertThat(selector.getEnclosingClassNames()).containsOnly(enclosingClassName); assertThat(selector.getNestedClassName()).isEqualTo(nestedClassName); assertThat(selector.getMethodName()).isEqualTo(methodName); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } /** @@ -937,6 +1184,7 @@ void selectNestedMethodByEnclosingClassNamesMethodNameAndParameterTypes() throws assertThat(selector.getNestedClassName()).isEqualTo(nestedClassName); assertThat(selector.getMethodName()).isEqualTo(methodName); assertThat(selector.getParameterTypeNames()).isEqualTo("java.lang.String"); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } /** @@ -965,6 +1213,7 @@ void selectNestedMethodByEnclosingClassNamesMethodNameAndParameterTypeNamesWithE assertThat(selector.getMethodName()).isEqualTo(methodName); assertThat(selector.getParameterTypeNames()).isEqualTo(String.class.getName()); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } } @@ -986,6 +1235,7 @@ void selectNestedMethodByEnclosingClassesMethodNameAndParameterTypes() throws Ex assertThat(selector.getNestedClassName()).isEqualTo(nestedClassName); assertThat(selector.getMethodName()).isEqualTo(methodName); assertThat(selector.getParameterTypeNames()).isEqualTo("java.lang.String"); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -1004,6 +1254,7 @@ void selectDoubleNestedMethodByEnclosingClassNamesAndMethodName() throws Excepti assertThat(selector.getEnclosingClassNames()).containsExactly(enclosingClassName, nestedClassName); assertThat(selector.getNestedClassName()).isEqualTo(doubleNestedClassName); assertThat(selector.getMethodName()).isEqualTo(doubleNestedMethodName); + assertThat(parseIdentifier(selector)).isEqualTo(selector); } @Test @@ -1071,6 +1322,7 @@ void selectNestedMethodByEnclosingClassesClassMethodNameAndParameterTypesPrecond static class ClassWithNestedInnerClass { // @Nested + @SuppressWarnings({ "InnerClassMayBeStatic", "unused" }) class NestedClass { // @Test @@ -1093,12 +1345,50 @@ void doubleNestedTest() { } + @Nested + class SelectIterationTests { + @Test + void selectsIteration() throws Exception { + Class clazz = DiscoverySelectorsTests.class; + var method = clazz.getDeclaredMethod("myTest", int.class); + var parentSelector = selectMethod(clazz, method); + var selector = DiscoverySelectors.selectIteration(parentSelector, 23, 42); + assertThat(selector.getParentSelector()).isSameAs(parentSelector); + assertThat(selector.getIterationIndices()).containsExactly(23, 42); + assertThat(parseIdentifier(selector)).isEqualTo(selector); + } + } + + @Nested + class SelectUniqueIdTests { + @Test + void selectsUniqueId() { + var selector = selectUniqueId(uniqueIdForMethod(DiscoverySelectorsTests.class, "myTest(int)")); + assertThat(selector.getUniqueId()).isNotNull(); + assertThat(parseIdentifier(selector)).isEqualTo(selector); + } + } + // ------------------------------------------------------------------------- private void assertViolatesPrecondition(Executable precondition) { assertThrows(PreconditionViolationException.class, precondition); } + private static DiscoverySelector parseIdentifier(DiscoverySelector selector) { + return DiscoverySelectors.parse(toIdentifierString(selector)).orElseThrow(); + } + + private static Stream parseIdentifiers( + Collection selectors) { + return DiscoverySelectors.parseAll( + selectors.stream().map(it -> DiscoverySelectorIdentifier.parse(toIdentifierString(it))).toList()); + } + + private static String toIdentifierString(DiscoverySelector selector) { + return selector.toIdentifier().orElseThrow().toString(); + } + private static String fqmn(Class... params) { return fqmn(DiscoverySelectorsTests.class, "myTest", params); } @@ -1118,37 +1408,48 @@ default void myTest() { } } + private static class TestCaseWithDefaultMethod implements TestInterface { } + @SuppressWarnings("unused") void myTest() { } + @SuppressWarnings("unused") void myTest(int num) { } + @SuppressWarnings("unused") void myTest(int[] nums) { } + @SuppressWarnings("unused") void myTest(int[][] grid) { } + @SuppressWarnings("unused") void myTest(int[][][][][] grid) { } + @SuppressWarnings("unused") void myTest(String info) { } + @SuppressWarnings("unused") void myTest(String info, boolean[] flags) { } + @SuppressWarnings("unused") void myTest(String[] info) { } + @SuppressWarnings("unused") void myTest(String[][] info) { } + @SuppressWarnings("unused") void myTest(Double[][][][][] data) { } diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/FilePositionTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/FilePositionTests.java index bd5afe94a2c8..e39acbeedf4d 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/FilePositionTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/FilePositionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/FileSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/FileSelectorTests.java index 2d4b47d1d410..7c167b6bb966 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/FileSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/FileSelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/IterationSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/IterationSelectorTests.java new file mode 100644 index 000000000000..9eb66c820c2a --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/IterationSelectorTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.engine.discovery; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectIteration; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Array; +import java.util.Optional; +import java.util.stream.IntStream; + +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.aggregator.AggregateWith; +import org.junit.jupiter.params.aggregator.ArgumentsAccessor; +import org.junit.jupiter.params.aggregator.ArgumentsAggregationException; +import org.junit.jupiter.params.aggregator.ArgumentsAggregator; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.engine.DiscoverySelector; +import org.junit.platform.engine.DiscoverySelectorIdentifier; + +class IterationSelectorTests { + + @ParameterizedTest + @CsvSource(delimiter = '|', textBlock = """ + 1 | 1 + 1,2 | 1 | 2 + 1..3 | 1 | 2 | 3 + 1,3 | 1 | 3 + 1..3,5..7 | 1 | 2 | 3 | 5 | 6 | 7 + """) + void collapsesRangesWhenConvertingToIdentifier(String expected, + @AggregateWith(VarargsAggregator.class) int... iterationIndices) { + var parent = "parent:value"; + var parentSelector = selectorWithIdentifier(parent); + var selector = selectIteration(parentSelector, iterationIndices); + + var identifier = selector.toIdentifier().orElseThrow(); + assertEquals("iteration:%s[%s]".formatted(parent, expected), identifier.toString()); + + DiscoverySelectorIdentifierParser.Context context = mock(); + when(context.parse(parent)).thenAnswer(__ -> Optional.of(parentSelector)); + assertEquals(selector, new IterationSelector.IdentifierParser().parse(identifier, context).orElseThrow()); + } + + private static DiscoverySelector selectorWithIdentifier(String identifier) { + DiscoverySelector parent = mock(); + when(parent.toIdentifier()) // + .thenReturn(Optional.of(DiscoverySelectorIdentifier.parse(identifier))); + return parent; + } + + private static class VarargsAggregator implements ArgumentsAggregator { + @Override + public Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) + throws ArgumentsAggregationException { + Class parameterType = context.getParameter().getType(); + Preconditions.condition(parameterType.isArray(), () -> "must be an array type, but was " + parameterType); + Class componentType = parameterType.getComponentType(); + IntStream indices = IntStream.range(context.getIndex(), accessor.size()); + if (componentType == int.class) { + return indices.map(accessor::getInteger).toArray(); + } + return indices.mapToObj(index -> accessor.get(index, componentType)).toArray( + size -> (Object[]) Array.newInstance(componentType, size)); + } + } +} diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/MethodSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/MethodSelectorTests.java index b6d890ba0861..21dafc77214a 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/MethodSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/MethodSelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -39,19 +39,17 @@ void equalsAndHashCode() { Stream.of(selector2, selector3, selector4).forEach(selector -> { assertEqualsAndHashCode(selector1, selector, new MethodSelector(null, TEST_CASE_NAME, "method", "int")); - assertEqualsAndHashCode(selector1, selector, - new MethodSelector((ClassLoader) null, TEST_CASE_NAME, "method", "")); + assertEqualsAndHashCode(selector1, selector, new MethodSelector(null, TEST_CASE_NAME, "method", "")); assertEqualsAndHashCode(selector1, selector, new MethodSelector(null, TEST_CASE_NAME, "X", "int, boolean")); - assertEqualsAndHashCode(selector1, selector, - new MethodSelector((ClassLoader) null, TEST_CASE_NAME, "X", "")); + assertEqualsAndHashCode(selector1, selector, new MethodSelector(null, TEST_CASE_NAME, "X", "")); assertEqualsAndHashCode(selector1, selector, new MethodSelector(null, "X", "method", "int, boolean")); - assertEqualsAndHashCode(selector1, selector, new MethodSelector((ClassLoader) null, "X", "method", "")); + assertEqualsAndHashCode(selector1, selector, new MethodSelector(null, "X", "method", "")); }); } @Test void preservesOriginalExceptionWhenTryingToLoadJavaClass() { - var selector = new MethodSelector((ClassLoader) null, "org.example.BogusClass", "method", "int, boolean"); + var selector = new MethodSelector(null, "org.example.BogusClass", "method", "int, boolean"); assertThat(selector.getClassName()).isEqualTo("org.example.BogusClass"); assertThat(selector.getMethodName()).isEqualTo("method"); @@ -65,7 +63,7 @@ void preservesOriginalExceptionWhenTryingToLoadJavaClass() { @Test void preservesOriginalExceptionWhenTryingToLoadClassForParameterType() { - var selector = new MethodSelector((ClassLoader) null, TEST_CASE_NAME, "method", "int[], org.example.Bogus"); + var selector = new MethodSelector(null, TEST_CASE_NAME, "method", "int[], org.example.Bogus"); assertThat(selector.getClassName()).isEqualTo(TEST_CASE_NAME); assertThat(selector.getMethodName()).isEqualTo("method"); diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ModuleSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ModuleSelectorTests.java index a637a6c77e4f..9ea129728212 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ModuleSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ModuleSelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/NestedClassSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/NestedClassSelectorTests.java index 93fe50b6fc4f..4f73cb36ea5f 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/NestedClassSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/NestedClassSelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/NestedMethodSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/NestedMethodSelectorTests.java index f696cc47f3e1..33235ed769d8 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/NestedMethodSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/NestedMethodSelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/PackageNameFilterTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/PackageNameFilterTests.java index 68c20f76c35f..cedd2ed6f439 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/PackageNameFilterTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/PackageNameFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/PackageSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/PackageSelectorTests.java index 3de77a7b70e0..cff34011791a 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/PackageSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/PackageSelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/UniqueIdSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/UniqueIdSelectorTests.java index 13084e5fb2ef..98dedf083850 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/UniqueIdSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/UniqueIdSelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/UriSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/UriSelectorTests.java index 1405c8e4e6c2..76def76ec775 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/UriSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/UriSelectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/config/PrefixedConfigurationParametersTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/config/PrefixedConfigurationParametersTests.java index 5875c3491a69..bf2f411e6b06 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/config/PrefixedConfigurationParametersTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/config/PrefixedConfigurationParametersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java index 97d5614470d7..3b8c8ae0d5f8 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestSourceTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestSourceTests.java index 50dc4a1ad938..9ce87b9b47f2 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestSourceTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/ClassSourceTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/ClassSourceTests.java index 2d5b6eadffa1..ef8b2b8d9a91 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/ClassSourceTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/ClassSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSourceTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSourceTests.java index cb1f75bda45c..1b861208a4ea 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSourceTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/CompositeTestSourceTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/CompositeTestSourceTests.java index 25d49a0aa191..121000e5c961 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/CompositeTestSourceTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/CompositeTestSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DefaultUriSourceTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DefaultUriSourceTests.java index bb488afb85a9..1fe34b233a90 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DefaultUriSourceTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DefaultUriSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DemoClassTestDescriptor.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DemoClassTestDescriptor.java index 8531561cd6f2..5f4e67153a52 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DemoClassTestDescriptor.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DemoClassTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DemoMethodTestDescriptor.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DemoMethodTestDescriptor.java index 9b73b713b1f8..85f25606424c 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DemoMethodTestDescriptor.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DemoMethodTestDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/FilePositionTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/FilePositionTests.java index 943184689139..af5562bc8f57 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/FilePositionTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/FilePositionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/FileSystemSourceTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/FileSystemSourceTests.java index f89d127acaa5..fd205bc2c6dd 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/FileSystemSourceTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/FileSystemSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/MethodSourceTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/MethodSourceTests.java index 44c7f064d692..3b8984829a61 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/MethodSourceTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/MethodSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -239,7 +239,7 @@ void getJavaMethodFromStringShouldFindVoidMethod() throws Exception { @Test void getJavaMethodFromStringShouldFindMethodWithParameter() throws Exception { - var testMethod = getClass().getDeclaredMethod("method3", Integer.TYPE); + var testMethod = getClass().getDeclaredMethod("method3", int.class); var source = MethodSource.from(getClass().getName(), testMethod.getName(), testMethod.getParameterTypes()); assertThat(source.getJavaMethod()).isEqualTo(testMethod); @@ -254,7 +254,7 @@ void getJavaMethodFromStringShouldThrowExceptionIfParameterTypesAreNotSupplied() @Test void getJavaMethodFromStringShouldThrowExceptionIfParameterTypesDoNotMatch() { - var source = MethodSource.from(getClass().getName(), "method3", Double.TYPE); + var source = MethodSource.from(getClass().getName(), "method3", double.class); assertThrows(PreconditionViolationException.class, source::getJavaMethod); } diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/PackageSourceTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/PackageSourceTests.java index b7217e6d9ea4..c9a752ca5a68 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/PackageSourceTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/PackageSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/CompositeLockTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/CompositeLockTests.java index 8f1ddee3b755..75e0a262f98e 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/CompositeLockTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/CompositeLockTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -19,9 +19,11 @@ import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.Lock; +import java.util.stream.IntStream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; +import org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode; /** * @since 1.3 @@ -34,7 +36,7 @@ void acquiresAllLocksInOrder() throws Exception { var lock1 = mock(Lock.class); var lock2 = mock(Lock.class); - new CompositeLock(List.of(lock1, lock2)).acquire(); + new CompositeLock(anyResources(2), List.of(lock1, lock2)).acquire(); var inOrder = inOrder(lock1, lock2); inOrder.verify(lock1).lockInterruptibly(); @@ -47,7 +49,7 @@ void releasesAllLocksInReverseOrder() throws Exception { var lock1 = mock(Lock.class); var lock2 = mock(Lock.class); - new CompositeLock(List.of(lock1, lock2)).acquire().close(); + new CompositeLock(anyResources(2), List.of(lock1, lock2)).acquire().close(); var inOrder = inOrder(lock1, lock2); inOrder.verify(lock2).unlock(); @@ -64,7 +66,7 @@ void releasesLocksInReverseOrderWhenInterruptedDuringAcquire() throws Exception var thread = new Thread(() -> { try { - new CompositeLock(List.of(firstLock, secondLock, unavailableLock)).acquire(); + new CompositeLock(anyResources(3), List.of(firstLock, secondLock, unavailableLock)).acquire(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); @@ -90,4 +92,10 @@ private Lock mockLock(String name, Executable lockAction) throws InterruptedExce return lock; } + private List anyResources(int n) { + return IntStream.range(0, n) // + .mapToObj(j -> new ExclusiveResource("key" + j, LockMode.READ)) // + .toList(); + } + } diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfigurationStrategyTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfigurationStrategyTests.java index b7ac504017e8..dde62fa84c50 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfigurationStrategyTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfigurationStrategyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ForkJoinDeadLockTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ForkJoinDeadLockTests.java new file mode 100644 index 000000000000..b50b2e23c700 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ForkJoinDeadLockTests.java @@ -0,0 +1,179 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.engine.support.hierarchical; + +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; +import static org.junit.jupiter.api.parallel.ResourceAccessMode.READ; +import static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE; +import static org.junit.jupiter.api.parallel.Resources.SYSTEM_PROPERTIES; + +import java.time.LocalTime; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterTestExecutionCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.Isolated; +import org.junit.jupiter.api.parallel.ResourceLock; +import org.junit.jupiter.engine.Constants; +import org.junit.platform.engine.discovery.ClassSelector; +import org.junit.platform.engine.discovery.DiscoverySelectors; +import org.junit.platform.testkit.engine.EngineTestKit; + +// https://github.com/junit-team/junit5/issues/3945 +@Timeout(10) +public class ForkJoinDeadLockTests { + + @Test + void forkJoinExecutionDoesNotLeadToDeadLock() { + run(NonIsolatedTestCase.class, IsolatedTestCase.class, Isolated2TestCase.class); + } + + @Test + void nestedResourceLocksShouldStillWork() { + run(SharedResourceTestCase.class); + } + + @Test + void multiLevelLocks() { + run(ClassLevelTestCase.class); + } + + private static void run(Class... classes) { + EngineTestKit.engine("junit-jupiter") // + .selectors(Arrays.stream(classes).map(DiscoverySelectors::selectClass).toArray(ClassSelector[]::new)) // + .configurationParameter(Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, "true") // + .configurationParameter(Constants.DEFAULT_PARALLEL_EXECUTION_MODE, "concurrent") // + .configurationParameter(Constants.DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME, "concurrent") // + .configurationParameter(Constants.PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME, "fixed") // + .configurationParameter(Constants.PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME, "3") // + .configurationParameter(Constants.PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME, "3") // + .configurationParameter(Constants.PARALLEL_CONFIG_FIXED_SATURATE_PROPERTY_NAME, "false") // + .execute(); + } + + @ExtendWith(StartFinishLogger.class) + static class BaseTestCase { + } + + @SuppressWarnings("JUnitMalformedDeclaration") + @Execution(CONCURRENT) + public static class NonIsolatedTestCase extends BaseTestCase { + + public static CountDownLatch otherThreadRunning = new CountDownLatch(1); + public static CountDownLatch sameThreadFinishing = new CountDownLatch(1); + + @Test + @Execution(CONCURRENT) + void otherThread() throws Exception { + otherThreadRunning.countDown(); + sameThreadFinishing.await(); + Thread.sleep(100); + } + + @Test + @Execution(SAME_THREAD) + void sameThread() throws Exception { + otherThreadRunning.await(); + sameThreadFinishing.countDown(); + } + } + + @SuppressWarnings("JUnitMalformedDeclaration") + @Isolated + public static class IsolatedTestCase extends BaseTestCase { + + @Test + void test() throws Exception { + Thread.sleep(100); + } + } + + static class Isolated2TestCase extends IsolatedTestCase { + } + + @SuppressWarnings("JUnitMalformedDeclaration") + public static class SharedResourceTestCase { + + @Test + @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ) + void customPropertyIsNotSetByDefault() { + } + + @Test + @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE) + void canSetCustomPropertyToApple() { + } + + @Test + @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE) + void canSetCustomPropertyToBanana() { + } + } + + @SuppressWarnings("JUnitMalformedDeclaration") + @ResourceLock(value = "foo", mode = READ_WRITE) + public static class ClassLevelTestCase { + + @Test + @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ) + void customPropertyIsNotSetByDefault() { + } + + @Test + @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE) + void canSetCustomPropertyToApple() { + } + + @Test + @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE) + void canSetCustomPropertyToBanana() { + } + } + + static class StartFinishLogger + implements BeforeTestExecutionCallback, AfterTestExecutionCallback, BeforeAllCallback, AfterAllCallback { + + @Override + public void beforeAll(ExtensionContext context) { + log("starting class " + context.getTestClass().orElseThrow().getSimpleName()); + } + + @Override + public void beforeTestExecution(ExtensionContext context) { + log("starting method " + context.getTestClass().orElseThrow().getSimpleName() + "." + + context.getTestMethod().orElseThrow().getName()); + } + + @Override + public void afterTestExecution(ExtensionContext context) { + log("finishing method " + context.getTestClass().orElseThrow().getSimpleName() + "." + + context.getTestMethod().orElseThrow().getName()); + } + + @Override + public void afterAll(ExtensionContext context) { + log("finishing class " + context.getTestClass().orElseThrow().getSimpleName()); + } + } + + private static void log(String message) { + System.out.println("[" + LocalTime.now() + "] " + Thread.currentThread().getName() + " - " + message); + } +} diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorServiceTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorServiceTests.java index 868386f83899..46c5bcc6cafe 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorServiceTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorServiceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,35 +10,354 @@ package org.junit.platform.engine.support.hierarchical; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.when; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ; +import static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE; +import static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.CONCURRENT; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.api.function.ThrowingConsumer; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.platform.commons.JUnitException; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode; +import org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.TaskEventListener; +import org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService.TestTask; +import org.junit.platform.engine.support.hierarchical.Node.ExecutionMode; -@ExtendWith(MockitoExtension.class) +@Timeout(5) class ForkJoinPoolHierarchicalTestExecutorServiceTests { - @Mock - ParallelExecutionConfiguration configuration; + DummyTaskFactory taskFactory = new DummyTaskFactory(); + LockManager lockManager = new LockManager(); @Test void exceptionsFromInvalidConfigurationAreNotSwallowed() { - when(configuration.getParallelism()).thenReturn(2); - when(configuration.getMaxPoolSize()).thenReturn(1); // invalid, should be > parallelism - when(configuration.getCorePoolSize()).thenReturn(1); - when(configuration.getMinimumRunnable()).thenReturn(1); - when(configuration.getSaturatePredicate()).thenReturn(__ -> true); - when(configuration.getKeepAliveSeconds()).thenReturn(0); - - JUnitException exception = assertThrows(JUnitException.class, - () -> new ForkJoinPoolHierarchicalTestExecutorService(configuration)); + var configuration = new DefaultParallelExecutionConfiguration(2, 1, 1, 1, 0, __ -> true); + + JUnitException exception = assertThrows(JUnitException.class, () -> { + try (var pool = new ForkJoinPoolHierarchicalTestExecutorService(configuration)) { + assertNotNull(pool, "we won't get here"); + } + }); + assertThat(exception).hasMessage("Failed to create ForkJoinPool"); assertThat(exception).rootCause().isInstanceOf(IllegalArgumentException.class); } + static List incompatibleLockCombinations() { + return List.of(// + arguments(// + Set.of(GLOBAL_READ), // + Set.of(GLOBAL_READ_WRITE) // + ), // + arguments(// + Set.of(new ExclusiveResource("a", LockMode.READ)), // + Set.of(new ExclusiveResource("a", LockMode.READ_WRITE)) // + ), // + arguments(// + Set.of(new ExclusiveResource("a", LockMode.READ_WRITE)), // + Set.of(new ExclusiveResource("a", LockMode.READ_WRITE)) // + ), // + arguments(// + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ_WRITE)), // + Set.of(GLOBAL_READ, new ExclusiveResource("b", LockMode.READ_WRITE)) // + ), // + arguments(// + Set.of(new ExclusiveResource("b", LockMode.READ)), // + Set.of(new ExclusiveResource("a", LockMode.READ)) // + ), // + arguments(// + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ_WRITE)), // + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ)) // + ), // + arguments(// + Set.of(GLOBAL_READ_WRITE), // + Set.of(GLOBAL_READ) // + ), // + arguments(// + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ), + new ExclusiveResource("b", LockMode.READ), new ExclusiveResource("d", LockMode.READ)), + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ), + new ExclusiveResource("c", LockMode.READ)) // + )// + ); + } + + @ParameterizedTest + @MethodSource("incompatibleLockCombinations") + void defersTasksWithIncompatibleLocks(Set initialResources, + Set incompatibleResources) throws Throwable { + + var initialLock = lockManager.getLockForResources(initialResources); + var incompatibleLock = lockManager.getLockForResources(incompatibleResources); + + var deferred = new CountDownLatch(1); + var deferredTask = new AtomicReference(); + + TaskEventListener taskEventListener = testTask -> { + deferredTask.set(testTask); + deferred.countDown(); + }; + + var incompatibleTask = taskFactory.create("incompatibleTask", incompatibleLock); + + var tasks = runWithAttemptedWorkStealing(taskEventListener, incompatibleTask, initialLock, + () -> await(deferred, "Interrupted while waiting for task to be deferred")); + + assertEquals(incompatibleTask, deferredTask.get()); + assertEquals(tasks.get("nestedTask").threadName, tasks.get("leafTaskB").threadName); + assertNotEquals(tasks.get("leafTaskA").threadName, tasks.get("leafTaskB").threadName); + } + + static List compatibleLockCombinations() { + return List.of(// + arguments(// + Set.of(GLOBAL_READ), // + Set.of(new ExclusiveResource("a", LockMode.READ)) // + ), // + arguments(// + Set.of(GLOBAL_READ), // + Set.of(new ExclusiveResource("a", LockMode.READ_WRITE)) // + ), // + arguments(// + Set.of(GLOBAL_READ), // + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ_WRITE)) // + ), // + arguments(// + Set.of(GLOBAL_READ), // + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ)) // + ), // + arguments(// + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ)), // + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ), + new ExclusiveResource("b", LockMode.READ), new ExclusiveResource("c", LockMode.READ)) // + ), // + arguments(// + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ)), // + Set.of(GLOBAL_READ, new ExclusiveResource("b", LockMode.READ)) // + ), // + arguments(// + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ)), // + Set.of(new ExclusiveResource("a", LockMode.READ), new ExclusiveResource("b", LockMode.READ), + new ExclusiveResource("c", LockMode.READ)) // + )// + ); + } + + @ParameterizedTest + @MethodSource("compatibleLockCombinations") + void canWorkStealTaskWithCompatibleLocks(Set initialResources, + Set compatibleResources) throws Throwable { + + var initialLock = lockManager.getLockForResources(initialResources); + var compatibleLock = lockManager.getLockForResources(compatibleResources); + + var deferredTask = new AtomicReference(); + + var workStolen = new CountDownLatch(1); + var compatibleTask = taskFactory.create("compatibleTask", compatibleLock, workStolen::countDown); + + var tasks = runWithAttemptedWorkStealing(deferredTask::set, compatibleTask, initialLock, + () -> await(workStolen, "Interrupted while waiting for work to be stolen")); + + assertNull(deferredTask.get()); + assertEquals(tasks.get("nestedTask").threadName, tasks.get("leafTaskB").threadName); + assertNotEquals(tasks.get("leafTaskA").threadName, tasks.get("leafTaskB").threadName); + } + + @Test + void defersTasksWithIncompatibleLocksOnMultipleLevels() throws Throwable { + + var initialLock = lockManager.getLockForResources( + Set.of(GLOBAL_READ, new ExclusiveResource("a", LockMode.READ))); + var incompatibleLock1 = lockManager.getLockForResource(new ExclusiveResource("a", LockMode.READ_WRITE)); + var compatibleLock1 = lockManager.getLockForResource(new ExclusiveResource("b", LockMode.READ)); + var incompatibleLock2 = lockManager.getLockForResource(new ExclusiveResource("b", LockMode.READ_WRITE)); + + var deferred = new ConcurrentHashMap(); + var deferredTasks = new CopyOnWriteArrayList(); + TaskEventListener taskEventListener = testTask -> { + deferredTasks.add(testTask); + deferred.get(testTask).countDown(); + }; + + var incompatibleTask1 = taskFactory.create("incompatibleTask1", incompatibleLock1); + deferred.put(incompatibleTask1, new CountDownLatch(1)); + + var incompatibleTask2 = taskFactory.create("incompatibleTask2", incompatibleLock2); + deferred.put(incompatibleTask2, new CountDownLatch(1)); + + var configuration = new DefaultParallelExecutionConfiguration(2, 2, 2, 2, 1, __1 -> true); + + withForkJoinPoolHierarchicalTestExecutorService(configuration, taskEventListener, service -> { + + var nestedTask2 = createNestedTaskWithTwoConcurrentLeafTasks(service, "2", compatibleLock1, + List.of(incompatibleTask2), // + () -> await(deferred.get(incompatibleTask2), incompatibleTask2.identifier + " to be deferred")); + + var nestedTask1 = createNestedTaskWithTwoConcurrentLeafTasks(service, "1", initialLock, + List.of(incompatibleTask1, nestedTask2), // + () -> { + await(deferred.get(incompatibleTask1), incompatibleTask1.identifier + " to be deferred"); + await(nestedTask2.started, nestedTask2.identifier + " to be started"); + }); + + service.submit(nestedTask1).get(); + }); + + assertThat(deferredTasks) // + .startsWith(incompatibleTask1, incompatibleTask2) // + .containsOnly(incompatibleTask1, incompatibleTask2) // incompatibleTask1 may be deferred multiple times + .containsOnlyOnce(incompatibleTask2); + assertThat(taskFactory.tasks) // + .hasSize(3 + 3 + 2) // + .values().extracting(it -> it.completion.isDone()).containsOnly(true); + assertThat(taskFactory.tasks) // + .values().extracting(it -> it.completion.isCompletedExceptionally()).containsOnly(false); + } + + private Map runWithAttemptedWorkStealing(TaskEventListener taskEventListener, + DummyTestTask taskToBeStolen, ResourceLock initialLock, Runnable waitAction) throws Throwable { + + var configuration = new DefaultParallelExecutionConfiguration(2, 2, 2, 2, 1, __ -> true); + + withForkJoinPoolHierarchicalTestExecutorService(configuration, taskEventListener, service -> { + + var nestedTask = createNestedTaskWithTwoConcurrentLeafTasks(service, "", initialLock, + List.of(taskToBeStolen), waitAction); + + service.submit(nestedTask).get(); + }); + + return taskFactory.tasks; + } + + private DummyTestTask createNestedTaskWithTwoConcurrentLeafTasks( + ForkJoinPoolHierarchicalTestExecutorService service, String identifierSuffix, ResourceLock parentLock, + List tasksToFork, Runnable waitAction) { + + return taskFactory.create("nestedTask" + identifierSuffix, parentLock, () -> { + + var bothLeafTasksAreRunning = new CountDownLatch(2); + + var leafTaskA = taskFactory.create("leafTaskA" + identifierSuffix, NopLock.INSTANCE, () -> { + tasksToFork.forEach(task -> service.new ExclusiveTask(task).fork()); + bothLeafTasksAreRunning.countDown(); + bothLeafTasksAreRunning.await(); + waitAction.run(); + }); + + var leafTaskB = taskFactory.create("leafTaskB" + identifierSuffix, NopLock.INSTANCE, () -> { + bothLeafTasksAreRunning.countDown(); + bothLeafTasksAreRunning.await(); + }); + + service.invokeAll(List.of(leafTaskA, leafTaskB)); + }); + } + + private static void await(CountDownLatch latch, String message) { + try { + latch.await(); + } + catch (InterruptedException e) { + System.out.println("Interrupted while waiting for " + message); + } + } + + private void withForkJoinPoolHierarchicalTestExecutorService(ParallelExecutionConfiguration configuration, + TaskEventListener taskEventListener, ThrowingConsumer action) + throws Throwable { + try (var service = new ForkJoinPoolHierarchicalTestExecutorService(configuration, taskEventListener)) { + + action.accept(service); + + service.forkJoinPool.shutdown(); + assertTrue(service.forkJoinPool.awaitTermination(5, SECONDS), "Pool did not terminate within timeout"); + } + } + + static final class DummyTestTask implements TestTask { + + private final String identifier; + private final ResourceLock resourceLock; + private final Executable action; + + private volatile String threadName; + private final CountDownLatch started = new CountDownLatch(1); + private final CompletableFuture completion = new CompletableFuture<>(); + + DummyTestTask(String identifier, ResourceLock resourceLock, Executable action) { + this.identifier = identifier; + this.resourceLock = resourceLock; + this.action = action; + } + + @Override + public ExecutionMode getExecutionMode() { + return CONCURRENT; + } + + @Override + public ResourceLock getResourceLock() { + return resourceLock; + } + + @Override + public void execute() { + threadName = Thread.currentThread().getName(); + started.countDown(); + try { + action.execute(); + completion.complete(null); + } + catch (Throwable e) { + completion.completeExceptionally(e); + throw new RuntimeException("Action " + identifier + " failed", e); + } + } + + @Override + public String toString() { + return identifier; + } + } + + static final class DummyTaskFactory { + + final Map tasks = new HashMap<>(); + + DummyTestTask create(String identifier, ResourceLock resourceLock) { + return create(identifier, resourceLock, () -> { + }); + } + + DummyTestTask create(String identifier, ResourceLock resourceLock, Executable action) { + DummyTestTask task = new DummyTestTask(identifier, resourceLock, action); + tasks.put(task.identifier, task); + return task; + } + } } diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutorTests.java index 80d46102f08e..11d1842ce4a5 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/LockManagerTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/LockManagerTests.java index 342978f0a62a..a1cb3efd1483 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/LockManagerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/LockManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -32,7 +32,7 @@ */ class LockManagerTests { - private LockManager lockManager = new LockManager(); + private final LockManager lockManager = new LockManager(); @Test void returnsNopLockWithoutExclusiveResources() { @@ -50,7 +50,7 @@ void returnsSingleLockForSingleExclusiveResource() { var locks = getLocks(resources, SingleLock.class); assertThat(locks).hasSize(1); - assertThat(locks.get(0)).isInstanceOf(ReadLock.class); + assertThat(locks.getFirst()).isInstanceOf(ReadLock.class); } @Test @@ -75,7 +75,7 @@ void reusesSameLockForExclusiveResourceWithSameKey() { assertThat(locks1).hasSize(1); assertThat(locks2).hasSize(1); - assertThat(locks1.get(0)).isSameAs(locks2.get(0)); + assertThat(locks1.getFirst()).isSameAs(locks2.getFirst()); } @Test @@ -111,8 +111,26 @@ void globalLockComesFirst(LockMode globalLockMode) { assertThat(locks.get(3)).isEqualTo(getSingleLock("foo", READ_WRITE)); } - private Lock getSingleLock(String globalResourceLockKey, LockMode read) { - return getLocks(Set.of(new ExclusiveResource(globalResourceLockKey, read)), SingleLock.class).get(0); + @Test + void usesSingleInstanceForGlobalReadLock() { + var lock = lockManager.getLockForResources(List.of(ExclusiveResource.GLOBAL_READ)); + + assertThat(lock) // + .isInstanceOf(SingleLock.class) // + .isSameAs(lockManager.getLockForResource(ExclusiveResource.GLOBAL_READ)); + } + + @Test + void usesSingleInstanceForGlobalReadWriteLock() { + var lock = lockManager.getLockForResources(List.of(ExclusiveResource.GLOBAL_READ_WRITE)); + + assertThat(lock) // + .isInstanceOf(SingleLock.class) // + .isSameAs(lockManager.getLockForResource(ExclusiveResource.GLOBAL_READ_WRITE)); + } + + private Lock getSingleLock(String key, LockMode lockMode) { + return getLocks(Set.of(new ExclusiveResource(key, lockMode)), SingleLock.class).getFirst(); } private List getLocks(Collection resources, Class type) { diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/MemoryLeakTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/MemoryLeakTests.java index ef706b918d95..ad97557e5fb6 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/MemoryLeakTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/MemoryLeakTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/NodeTreeWalkerIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/NodeTreeWalkerIntegrationTests.java index e78138018f75..afb9addda85f 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/NodeTreeWalkerIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/NodeTreeWalkerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.ResourceAccessMode; import org.junit.jupiter.api.parallel.ResourceLock; +import org.junit.jupiter.api.parallel.Resources; import org.junit.jupiter.engine.JupiterTestEngine; import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.UniqueId; @@ -161,12 +162,12 @@ void coarsensGlobalLockToEngineDescriptorChild() { var nestedTestClassDescriptor = getOnlyElement(testClassDescriptor.getChildren()); assertThat(advisor.getResourceLock(nestedTestClassDescriptor)).extracting(allLocks()) // - .isEqualTo(List.of(getLock(GLOBAL_READ))); + .isEqualTo(List.of()); assertThat(advisor.getForcedExecutionMode(nestedTestClassDescriptor)).contains(SAME_THREAD); var testMethodDescriptor = getOnlyElement(nestedTestClassDescriptor.getChildren()); assertThat(advisor.getResourceLock(testMethodDescriptor)).extracting(allLocks()) // - .isEqualTo(List.of(getLock(GLOBAL_READ_WRITE))); + .isEqualTo(List.of()); assertThat(advisor.getForcedExecutionMode(testMethodDescriptor)).contains(SAME_THREAD); } @@ -215,6 +216,7 @@ void test() { } } + @ResourceLock(Resources.SYSTEM_PROPERTIES) static class TestCaseWithGlobalLockRequiringChild { @Nested class NestedTestCaseWithResourceLock { diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionIntegrationTests.java index ca7433377bf1..4634c4c94f57 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -18,12 +18,15 @@ import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; +import static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE; import static org.junit.jupiter.engine.Constants.DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME; import static org.junit.jupiter.engine.Constants.DEFAULT_PARALLEL_EXECUTION_MODE; +import static org.junit.jupiter.engine.Constants.PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME; import static org.junit.jupiter.engine.Constants.PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME; import static org.junit.jupiter.engine.Constants.PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME; import static org.junit.jupiter.engine.Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME; -import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; +import static org.junit.platform.commons.util.CollectionUtils.getOnlyElement; +import static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_KEY; import static org.junit.platform.testkit.engine.EventConditions.container; import static org.junit.platform.testkit.engine.EventConditions.event; import static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully; @@ -64,21 +67,27 @@ import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.Isolated; import org.junit.jupiter.api.parallel.ResourceLock; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.discovery.ClassSelector; import org.junit.platform.engine.discovery.DiscoverySelectors; import org.junit.platform.engine.reporting.ReportEntry; +import org.junit.platform.engine.support.descriptor.MethodSource; import org.junit.platform.testkit.engine.EngineExecutionResults; import org.junit.platform.testkit.engine.EngineTestKit; import org.junit.platform.testkit.engine.Event; +import org.junit.platform.testkit.engine.Events; /** * @since 1.3 */ +@SuppressWarnings({ "JUnitMalformedDeclaration", "NewClassNamingConvention" }) class ParallelExecutionIntegrationTests { @Test void successfulParallelTest(TestReporter reporter) { - var events = executeConcurrently(3, SuccessfulParallelTestCase.class); + var events = executeConcurrentlySuccessfully(3, SuccessfulParallelTestCase.class).list(); var startedTimestamps = getTimestampsFor(events, event(test(), started())); var finishedTimestamps = getTimestampsFor(events, event(test(), finishedSuccessfully())); @@ -94,13 +103,13 @@ void successfulParallelTest(TestReporter reporter) { @Test void failingTestWithoutLock() { - var events = executeConcurrently(3, FailingWithoutLockTestCase.class); + var events = executeConcurrently(3, FailingWithoutLockTestCase.class).list(); assertThat(events.stream().filter(event(test(), finishedWithFailure())::matches)).hasSize(2); } @Test void successfulTestWithMethodLock() { - var events = executeConcurrently(3, SuccessfulWithMethodLockTestCase.class); + var events = executeConcurrentlySuccessfully(3, SuccessfulWithMethodLockTestCase.class).list(); assertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(3); assertThat(ThreadReporter.getThreadNames(events)).hasSize(3); @@ -108,7 +117,7 @@ void successfulTestWithMethodLock() { @Test void successfulTestWithClassLock() { - var events = executeConcurrently(3, SuccessfulWithClassLockTestCase.class); + var events = executeConcurrentlySuccessfully(3, SuccessfulWithClassLockTestCase.class).list(); assertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(3); assertThat(ThreadReporter.getThreadNames(events)).hasSize(1); @@ -116,7 +125,7 @@ void successfulTestWithClassLock() { @Test void testCaseWithFactory() { - var events = executeConcurrently(3, TestCaseWithTestFactory.class); + var events = executeConcurrentlySuccessfully(3, TestCaseWithTestFactory.class).list(); assertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(3); assertThat(ThreadReporter.getThreadNames(events)).hasSize(1); @@ -129,7 +138,7 @@ void customContextClassLoader() { var smilingLoader = new URLClassLoader("(-:", new URL[0], ClassLoader.getSystemClassLoader()); currentThread.setContextClassLoader(smilingLoader); try { - var events = executeConcurrently(3, SuccessfulWithMethodLockTestCase.class); + var events = executeConcurrentlySuccessfully(3, SuccessfulWithMethodLockTestCase.class).list(); assertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(3); assertThat(ThreadReporter.getThreadNames(events)).hasSize(3); @@ -142,7 +151,8 @@ void customContextClassLoader() { @RepeatedTest(10) void mixingClassAndMethodLevelLocks() { - var events = executeConcurrently(4, TestCaseWithSortedLocks.class, TestCaseWithUnsortedLocks.class); + var events = executeConcurrentlySuccessfully(4, TestCaseWithSortedLocks.class, + TestCaseWithUnsortedLocks.class).list(); assertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(6); assertThat(ThreadReporter.getThreadNames(events).count()).isLessThanOrEqualTo(2); @@ -150,7 +160,7 @@ void mixingClassAndMethodLevelLocks() { @RepeatedTest(10) void locksOnNestedTests() { - var events = executeConcurrently(3, TestCaseWithNestedLocks.class); + var events = executeConcurrentlySuccessfully(3, TestCaseWithNestedLocks.class).list(); assertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(6); assertThat(ThreadReporter.getThreadNames(events)).hasSize(1); @@ -158,7 +168,7 @@ void locksOnNestedTests() { @Test void afterHooksAreCalledAfterConcurrentDynamicTestsAreFinished() { - var events = executeConcurrently(3, ConcurrentDynamicTestCase.class); + var events = executeConcurrentlySuccessfully(3, ConcurrentDynamicTestCase.class).list(); assertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(1); var timestampedEvents = ConcurrentDynamicTestCase.events; @@ -171,14 +181,14 @@ void afterHooksAreCalledAfterConcurrentDynamicTestsAreFinished() { */ @Test void threadInterruptedByUserCode() { - var events = executeConcurrently(3, InterruptedThreadTestCase.class); + var events = executeConcurrentlySuccessfully(3, InterruptedThreadTestCase.class).list(); assertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(4); } @Test void executesTestTemplatesWithResourceLocksInSameThread() { - var events = executeConcurrently(2, ConcurrentTemplateTestCase.class); + var events = executeConcurrentlySuccessfully(2, ConcurrentTemplateTestCase.class).list(); assertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(10); assertThat(ThreadReporter.getThreadNames(events)).hasSize(1); @@ -224,30 +234,59 @@ void executesMethodsInParallelIfEnabledViaConfigurationParameter() { @Test void canRunTestsIsolatedFromEachOther() { - var events = executeConcurrently(2, IsolatedTestCase.class); - - assertThat(events.stream().filter(event(test(), finishedWithFailure())::matches)).isEmpty(); + executeConcurrentlySuccessfully(2, IsolatedTestCase.class); } @Test void canRunTestsIsolatedFromEachOtherWithNestedCases() { - var events = executeConcurrently(4, NestedIsolatedTestCase.class); - - assertThat(events.stream().filter(event(test(), finishedWithFailure())::matches)).isEmpty(); + executeConcurrentlySuccessfully(4, NestedIsolatedTestCase.class); } @Test void canRunTestsIsolatedFromEachOtherAcrossClasses() { - var events = executeConcurrently(4, IndependentClasses.A.class, IndependentClasses.B.class); - - assertThat(events.stream().filter(event(test(), finishedWithFailure())::matches)).isEmpty(); + executeConcurrentlySuccessfully(4, IndependentClasses.A.class, IndependentClasses.B.class); } @RepeatedTest(10) void canRunTestsIsolatedFromEachOtherAcrossClassesWithOtherResourceLocks() { - var events = executeConcurrently(4, IndependentClasses.B.class, IndependentClasses.C.class); + executeConcurrentlySuccessfully(4, IndependentClasses.B.class, IndependentClasses.C.class); + } + + @Test + void runsIsolatedTestsLastToMaximizeParallelism() { + var configParams = Map.of( // + DEFAULT_PARALLEL_EXECUTION_MODE, "concurrent", // + PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME, "3" // + ); + Class[] testClasses = { IsolatedTestCase.class, SuccessfulParallelTestCase.class }; + var events = executeWithFixedParallelism(3, configParams, testClasses) // + .allEvents() // + .assertStatistics(it -> it.failed(0)); + + List parallelTestMethodEvents = events.reportingEntryPublished() // + .filter(e -> e.getTestDescriptor().getSource() // + .filter(it -> // + it instanceof MethodSource + && SuccessfulParallelTestCase.class.equals(((MethodSource) it).getJavaClass()) // + ).isPresent() // + ) // + .toList(); + assertThat(ThreadReporter.getThreadNames(parallelTestMethodEvents)).hasSize(3); + + var parallelClassFinish = getOnlyElement(getTimestampsFor(events.list(), + event(container(SuccessfulParallelTestCase.class), finishedSuccessfully()))); + var isolatedClassStart = getOnlyElement( + getTimestampsFor(events.list(), event(container(IsolatedTestCase.class), started()))); + assertThat(isolatedClassStart).isAfterOrEqualTo(parallelClassFinish); + } + + @ParameterizedTest + @ValueSource(classes = { IsolatedMethodFirstTestCase.class, IsolatedMethodLastTestCase.class, + IsolatedNestedMethodFirstTestCase.class, IsolatedNestedMethodLastTestCase.class }) + void canRunTestsIsolatedFromEachOtherWhenDeclaredOnMethodLevel(Class testClass) { + List events = executeConcurrentlySuccessfully(1, testClass).list(); - assertThat(events.stream().filter(event(test(), finishedWithFailure())::matches)).isEmpty(); + assertThat(ThreadReporter.getThreadNames(events)).hasSize(1); } @Isolated("testing") @@ -322,6 +361,122 @@ void b() throws Exception { } } + @ExtendWith(ThreadReporter.class) + static class IsolatedMethodFirstTestCase { + + static AtomicInteger sharedResource; + static CountDownLatch countDownLatch; + + @BeforeAll + static void initialize() { + sharedResource = new AtomicInteger(); + countDownLatch = new CountDownLatch(2); + } + + @Test + @ResourceLock(value = GLOBAL_KEY, mode = READ_WRITE) // effectively @Isolated + void test1() throws InterruptedException { + incrementBlockAndCheck(sharedResource, countDownLatch); + } + + @Test + @ResourceLock(value = "b", mode = READ_WRITE) + void test2() throws InterruptedException { + incrementBlockAndCheck(sharedResource, countDownLatch); + } + } + + @ExtendWith(ThreadReporter.class) + static class IsolatedMethodLastTestCase { + + static AtomicInteger sharedResource; + static CountDownLatch countDownLatch; + + @BeforeAll + static void initialize() { + sharedResource = new AtomicInteger(); + countDownLatch = new CountDownLatch(2); + } + + @Test + @ResourceLock(value = "b", mode = READ_WRITE) + void test1() throws InterruptedException { + incrementBlockAndCheck(sharedResource, countDownLatch); + } + + @Test + @ResourceLock(value = GLOBAL_KEY, mode = READ_WRITE) // effectively @Isolated + void test2() throws InterruptedException { + incrementBlockAndCheck(sharedResource, countDownLatch); + } + } + + @ExtendWith(ThreadReporter.class) + static class IsolatedNestedMethodFirstTestCase { + + static AtomicInteger sharedResource; + static CountDownLatch countDownLatch; + + @BeforeAll + static void initialize() { + sharedResource = new AtomicInteger(); + countDownLatch = new CountDownLatch(2); + } + + @Nested + class Test1 { + + @Test + @ResourceLock(value = GLOBAL_KEY, mode = READ_WRITE) // effectively @Isolated + void test1() throws InterruptedException { + incrementBlockAndCheck(sharedResource, countDownLatch); + } + } + + @Nested + class Test2 { + + @Test + @ResourceLock(value = "b", mode = READ_WRITE) + void test2() throws InterruptedException { + incrementBlockAndCheck(sharedResource, countDownLatch); + } + } + } + + @ExtendWith(ThreadReporter.class) + static class IsolatedNestedMethodLastTestCase { + + static AtomicInteger sharedResource; + static CountDownLatch countDownLatch; + + @BeforeAll + static void initialize() { + sharedResource = new AtomicInteger(); + countDownLatch = new CountDownLatch(2); + } + + @Nested + class Test1 { + + @Test + @ResourceLock(value = "b", mode = READ_WRITE) + void test1() throws InterruptedException { + incrementBlockAndCheck(sharedResource, countDownLatch); + } + } + + @Nested + class Test2 { + + @Test + @ResourceLock(value = GLOBAL_KEY, mode = READ_WRITE) // effectively @Isolated + void test2() throws InterruptedException { + incrementBlockAndCheck(sharedResource, countDownLatch); + } + } + } + static class IndependentClasses { static AtomicInteger sharedResource = new AtomicInteger(); static CountDownLatch countDownLatch = new CountDownLatch(4); @@ -383,28 +538,41 @@ private List getTimestampsFor(List events, Condition cond // @formatter:on } - private List executeConcurrently(int parallelism, Class... testClasses) { - return executeWithFixedParallelism(parallelism, Map.of(DEFAULT_PARALLEL_EXECUTION_MODE, "concurrent"), - testClasses).allEvents().list(); + private Events executeConcurrentlySuccessfully(int parallelism, Class... testClasses) { + var events = executeConcurrently(parallelism, testClasses); + try { + return events.assertStatistics(it -> it.failed(0)); + } + catch (AssertionError error) { + events.debug(); + throw error; + } + } + + private Events executeConcurrently(int parallelism, Class... testClasses) { + Map configParams = Map.of(DEFAULT_PARALLEL_EXECUTION_MODE, "concurrent"); + return executeWithFixedParallelism(parallelism, configParams, testClasses) // + .allEvents(); } private EngineExecutionResults executeWithFixedParallelism(int parallelism, Map configParams, Class... testClasses) { - // @formatter:off - var discoveryRequest = request() - .selectors(Arrays.stream(testClasses).map(DiscoverySelectors::selectClass).collect(toList())) - .configurationParameter(PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, String.valueOf(true)) - .configurationParameter(PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME, "fixed") - .configurationParameter(PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME, String.valueOf(parallelism)) - .configurationParameters(configParams) - .build(); - // @formatter:on - return EngineTestKit.execute("junit-jupiter", discoveryRequest); + var classSelectors = Arrays.stream(testClasses) // + .map(DiscoverySelectors::selectClass) // + .toArray(ClassSelector[]::new); + return EngineTestKit.engine("junit-jupiter") // + .selectors(classSelectors) // + .configurationParameter(PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, String.valueOf(true)) // + .configurationParameter(PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME, "fixed") // + .configurationParameter(PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME, String.valueOf(parallelism)) // + .configurationParameters(configParams) // + .execute(); } // ------------------------------------------------------------------------- @ExtendWith(ThreadReporter.class) + @Execution(SAME_THREAD) static class SuccessfulParallelTestCase { static AtomicInteger sharedResource; @@ -417,16 +585,19 @@ static void initialize() { } @Test + @Execution(CONCURRENT) void firstTest() throws Exception { incrementAndBlock(sharedResource, countDownLatch); } @Test + @Execution(CONCURRENT) void secondTest() throws Exception { incrementAndBlock(sharedResource, countDownLatch); } @Test + @Execution(CONCURRENT) void thirdTest() throws Exception { incrementAndBlock(sharedResource, countDownLatch); } @@ -782,6 +953,7 @@ private static void incrementBlockAndCheck(AtomicInteger sharedResource, CountDo assertEquals(value, sharedResource.get()); } + @SuppressWarnings("ResultOfMethodCallIgnored") private static int incrementAndBlock(AtomicInteger sharedResource, CountDownLatch countDownLatch) throws InterruptedException { var value = sharedResource.incrementAndGet(); @@ -790,6 +962,7 @@ private static int incrementAndBlock(AtomicInteger sharedResource, CountDownLatc return value; } + @SuppressWarnings("ResultOfMethodCallIgnored") private static void storeAndBlockAndCheck(AtomicInteger sharedResource, CountDownLatch countDownLatch) throws InterruptedException { var value = sharedResource.get(); @@ -798,7 +971,7 @@ private static void storeAndBlockAndCheck(AtomicInteger sharedResource, CountDow assertEquals(value, sharedResource.get()); } - /** + /* * To simulate tests running in parallel tests will modify a shared * resource, simulate work by waiting, then check if the shared resource was * not modified by any other thread. @@ -806,10 +979,10 @@ private static void storeAndBlockAndCheck(AtomicInteger sharedResource, CountDow * Depending on system performance the simulation of work needs to be longer * on slower systems to ensure tests can run in parallel. * - * Currently CI is known to be slow. + * Currently, CI is known to be slow. */ private static long estimateSimulatedTestDurationInMiliseconds() { - var runningInCi = Boolean.valueOf(System.getenv("CI")); + var runningInCi = Boolean.parseBoolean(System.getenv("CI")); return runningInCi ? 1000 : 100; } diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ResourceLockSupport.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ResourceLockSupport.java index 59f02ffa6e7d..1660eb08f1b6 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ResourceLockSupport.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ResourceLockSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ResourceLockTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ResourceLockTests.java new file mode 100644 index 000000000000..2372ff27de42 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ResourceLockTests.java @@ -0,0 +1,150 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.engine.support.hierarchical; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ; +import static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.junit.jupiter.api.Test; +import org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode; + +class ResourceLockTests { + + @Test + void nopLocks() { + assertCompatible(nopLock(), nopLock()); + assertCompatible(nopLock(), singleLock(anyReadOnlyResource())); + assertCompatible(nopLock(), compositeLock(anyReadOnlyResource())); + } + + @Test + void readOnlySingleLocks() { + ExclusiveResource bR = readOnlyResource("b"); + + assertCompatible(singleLock(bR), nopLock()); + assertCompatible(singleLock(bR), singleLock(bR)); + assertIncompatible(singleLock(bR), singleLock(readWriteResource("b")), "read-write conflict"); + assertIncompatible(singleLock(bR), singleLock(readOnlyResource("a")), "lock acquisition order"); + assertCompatible(singleLock(bR), singleLock(readOnlyResource("c"))); + assertIncompatible(singleLock(bR), singleLock(GLOBAL_READ), "lock acquisition order"); + assertIncompatible(singleLock(bR), singleLock(GLOBAL_READ_WRITE), "lock acquisition order"); + assertCompatible(singleLock(bR), compositeLock(bR, readOnlyResource("c"))); + assertIncompatible(singleLock(bR), compositeLock(readOnlyResource("a1"), readOnlyResource("a2"), bR), + "lock acquisition order"); + } + + @Test + void readWriteSingleLocks() { + ExclusiveResource bRW = readWriteResource("b"); + + assertCompatible(singleLock(bRW), nopLock()); + assertIncompatible(singleLock(bRW), singleLock(bRW), "isolation guarantees"); + assertIncompatible(singleLock(bRW), compositeLock(bRW), "isolation guarantees"); + assertIncompatible(singleLock(bRW), singleLock(readOnlyResource("a")), "lock acquisition order"); + assertIncompatible(singleLock(bRW), singleLock(readOnlyResource("b")), "isolation guarantees"); + assertIncompatible(singleLock(bRW), singleLock(readOnlyResource("c")), "isolation guarantees"); + assertIncompatible(singleLock(bRW), singleLock(GLOBAL_READ), "lock acquisition order"); + assertIncompatible(singleLock(bRW), singleLock(GLOBAL_READ_WRITE), "lock acquisition order"); + assertIncompatible(singleLock(bRW), compositeLock(bRW, readOnlyResource("c")), "isolation guarantees"); + assertIncompatible(singleLock(bRW), compositeLock(readOnlyResource("a1"), readOnlyResource("a2"), bRW), + "lock acquisition order"); + } + + @Test + void globalReadLock() { + assertCompatible(singleLock(GLOBAL_READ), nopLock()); + assertCompatible(singleLock(GLOBAL_READ), singleLock(GLOBAL_READ)); + assertCompatible(singleLock(GLOBAL_READ), singleLock(anyReadOnlyResource())); + assertCompatible(singleLock(GLOBAL_READ), singleLock(anyReadWriteResource())); + assertIncompatible(singleLock(GLOBAL_READ), singleLock(GLOBAL_READ_WRITE), "read-write conflict"); + } + + @Test + void readOnlyCompositeLocks() { + ExclusiveResource bR = readOnlyResource("b"); + + assertCompatible(compositeLock(bR), nopLock()); + assertCompatible(compositeLock(bR), singleLock(bR)); + assertCompatible(compositeLock(bR), compositeLock(bR)); + assertIncompatible(compositeLock(bR), singleLock(GLOBAL_READ), "lock acquisition order"); + assertIncompatible(compositeLock(bR), singleLock(GLOBAL_READ_WRITE), "lock acquisition order"); + assertIncompatible(compositeLock(bR), compositeLock(readOnlyResource("a")), "lock acquisition order"); + assertCompatible(compositeLock(bR), compositeLock(readOnlyResource("c"))); + assertIncompatible(compositeLock(bR), compositeLock(readWriteResource("b")), "read-write conflict"); + assertIncompatible(compositeLock(bR), compositeLock(bR, readWriteResource("b")), "read-write conflict"); + } + + @Test + void readWriteCompositeLocks() { + ExclusiveResource bRW = readWriteResource("b"); + + assertCompatible(compositeLock(bRW), nopLock()); + assertIncompatible(compositeLock(bRW), singleLock(bRW), "isolation guarantees"); + assertIncompatible(compositeLock(bRW), compositeLock(bRW), "isolation guarantees"); + assertIncompatible(compositeLock(bRW), singleLock(readOnlyResource("a")), "lock acquisition order"); + assertIncompatible(compositeLock(bRW), singleLock(readOnlyResource("b")), "isolation guarantees"); + assertIncompatible(compositeLock(bRW), singleLock(readOnlyResource("c")), "isolation guarantees"); + assertIncompatible(compositeLock(bRW), singleLock(GLOBAL_READ), "lock acquisition order"); + assertIncompatible(compositeLock(bRW), singleLock(GLOBAL_READ_WRITE), "lock acquisition order"); + assertIncompatible(compositeLock(bRW), compositeLock(readOnlyResource("a")), "lock acquisition order"); + assertIncompatible(compositeLock(bRW), compositeLock(readOnlyResource("b"), readOnlyResource("c")), + "isolation guarantees"); + } + + private static void assertCompatible(ResourceLock first, ResourceLock second) { + assertTrue(first.isCompatible(second), + "Expected locks to be compatible:\n(1) %s\n(2) %s".formatted(first, second)); + } + + private static void assertIncompatible(ResourceLock first, ResourceLock second, String reason) { + assertFalse(first.isCompatible(second), + "Expected locks to be incompatible due to %s:\n(1) %s\n(2) %s".formatted(reason, first, second)); + } + + private static ResourceLock nopLock() { + return NopLock.INSTANCE; + } + + private static SingleLock singleLock(ExclusiveResource resource) { + return new SingleLock(resource, anyLock()); + } + + private static CompositeLock compositeLock(ExclusiveResource... resources) { + return new CompositeLock(List.of(resources), Arrays.stream(resources).map(__ -> anyLock()).toList()); + } + + private static ExclusiveResource anyReadOnlyResource() { + return readOnlyResource("key"); + } + + private static ExclusiveResource anyReadWriteResource() { + return readWriteResource("key"); + } + + private static ExclusiveResource readOnlyResource(String key) { + return new ExclusiveResource(key, LockMode.READ); + } + + private static ExclusiveResource readWriteResource(String key) { + return new ExclusiveResource(key, LockMode.READ_WRITE); + } + + private static Lock anyLock() { + return new ReentrantLock(); + } +} diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SameThreadExecutionIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SameThreadExecutionIntegrationTests.java index 9a17de1a825b..64b1939cfe5e 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SameThreadExecutionIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SameThreadExecutionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -32,8 +32,7 @@ class SameThreadExecutionIntegrationTests { * @see gh-1688 */ @Test - @TrackLogRecords - void threadInterruptedByUserCode(LogRecordListener listener) { + void threadInterruptedByUserCode(@TrackLogRecords LogRecordListener listener) { EngineTestKit.engine("junit-jupiter")// .selectors(selectClass(InterruptedThreadTestCase.class))// .execute()// diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SingleLockTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SingleLockTests.java index 382a82fc054d..d4a9ef75e0cb 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SingleLockTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SingleLockTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -16,6 +16,7 @@ import java.util.concurrent.locks.ReentrantLock; import org.junit.jupiter.api.Test; +import org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode; /** * @since 1.3 @@ -27,7 +28,7 @@ class SingleLockTests { void acquire() throws Exception { var lock = new ReentrantLock(); - new SingleLock(lock).acquire(); + new SingleLock(anyResource(), lock).acquire(); assertTrue(lock.isLocked()); } @@ -37,9 +38,13 @@ void acquire() throws Exception { void release() throws Exception { var lock = new ReentrantLock(); - new SingleLock(lock).acquire().close(); + new SingleLock(anyResource(), lock).acquire().close(); assertFalse(lock.isLocked()); } + private static ExclusiveResource anyResource() { + return new ExclusiveResource("key", LockMode.READ); + } + } diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SingleTestExecutorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SingleTestExecutorTests.java index 2160099a67be..e80890a1d6ef 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SingleTestExecutorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SingleTestExecutorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ThrowableCollectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ThrowableCollectorTests.java index 868e4804f72d..bf5ca1bffc32 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ThrowableCollectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ThrowableCollectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStoreTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStoreTests.java index 966c8a9f4e65..f234ff11dbe3 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStoreTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStoreTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -11,14 +11,15 @@ package org.junit.platform.engine.support.store; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.platform.commons.test.ConcurrencyTestingUtils.executeConcurrently; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -27,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -171,7 +173,8 @@ void getWithTypeSafetyAndInvalidRequiredTypeThrowsException() { Exception exception = assertThrows(NamespacedHierarchicalStoreException.class, () -> store.get(namespace, key, Number.class)); - assertEquals("Object stored under key [42] is not of required type [java.lang.Number]", + assertEquals( + "Object stored under key [42] is not of required type [java.lang.Number], but was [java.lang.String]: enigma", exception.getMessage()); } @@ -221,7 +224,8 @@ void getOrComputeIfAbsentWithTypeSafetyAndInvalidRequiredTypeThrowsException() { Exception exception = assertThrows(NamespacedHierarchicalStoreException.class, () -> store.getOrComputeIfAbsent(namespace, key, defaultCreator, String.class)); - assertEquals("Object stored under key [pi] is not of required type [java.lang.String]", + assertEquals( + "Object stored under key [pi] is not of required type [java.lang.String], but was [java.lang.Float]: 3.14", exception.getMessage()); } @@ -264,7 +268,8 @@ void removeWithTypeSafetyAndInvalidRequiredTypeThrowsException() { Exception exception = assertThrows(NamespacedHierarchicalStoreException.class, () -> store.remove(namespace, key, Number.class)); - assertEquals("Object stored under key [42] is not of required type [java.lang.Number]", + assertEquals( + "Object stored under key [42] is not of required type [java.lang.Number], but was [java.lang.String]: enigma", exception.getMessage()); } @@ -367,6 +372,11 @@ void additionNamespacePartMakesADifference() { @Nested class CloseActionTests { + @BeforeEach + void prerequisites() { + assertNotClosed(); + } + @Test void callsCloseActionInReverseInsertionOrderWhenClosingStore() throws Throwable { store.put(namespace, "key1", "value1"); @@ -375,10 +385,14 @@ void callsCloseActionInReverseInsertionOrderWhenClosingStore() throws Throwable verifyNoInteractions(closeAction); store.close(); + assertClosed(); + var inOrder = inOrder(closeAction); inOrder.verify(closeAction).close(namespace, "key3", "value3"); inOrder.verify(closeAction).close(namespace, "key2", "value2"); inOrder.verify(closeAction).close(namespace, "key1", "value1"); + + verifyNoMoreInteractions(closeAction); } @Test @@ -387,6 +401,7 @@ void doesNotCallCloseActionForRemovedValues() { store.remove(namespace, key); store.close(); + assertClosed(); verifyNoInteractions(closeAction); } @@ -397,6 +412,7 @@ void doesNotCallCloseActionForReplacedValues() throws Throwable { store.put(namespace, key, "value2"); store.close(); + assertClosed(); verify(closeAction).close(namespace, key, "value2"); verifyNoMoreInteractions(closeAction); @@ -407,34 +423,144 @@ void doesNotCallCloseActionForNullValues() { store.put(namespace, key, null); store.close(); + assertClosed(); verifyNoInteractions(closeAction); } @Test - void ignoresStoredValuesThatThrewExceptionsDuringCleanup() { - assertThrows(RuntimeException.class, () -> store.getOrComputeIfAbsent(namespace, key, __ -> { + void doesNotCallCloseActionForValuesThatThrowExceptionsDuringCleanup() throws Throwable { + store.put(namespace, "key1", "value1"); + assertThrows(RuntimeException.class, () -> store.getOrComputeIfAbsent(namespace, "key2", __ -> { throw new RuntimeException("boom"); })); + store.put(namespace, "key3", "value3"); - assertDoesNotThrow(store::close); + store.close(); + assertClosed(); - verifyNoInteractions(closeAction); + var inOrder = inOrder(closeAction); + inOrder.verify(closeAction).close(namespace, "key3", "value3"); + inOrder.verify(closeAction).close(namespace, "key1", "value1"); + + verifyNoMoreInteractions(closeAction); } @Test - void doesNotIgnoreStoredValuesThatThrewUnrecoverableFailuresDuringCleanup() { - assertThrows(OutOfMemoryError.class, () -> store.getOrComputeIfAbsent(namespace, key, __ -> { - throw new OutOfMemoryError(); + void abortsCloseIfAnyStoredValueThrowsAnUnrecoverableExceptionDuringCleanup() throws Throwable { + store.put(namespace, "key1", "value1"); + assertThrows(OutOfMemoryError.class, () -> store.getOrComputeIfAbsent(namespace, "key2", __ -> { + throw new OutOfMemoryError("boom"); })); + store.put(namespace, "key3", "value3"); + + assertThrows(OutOfMemoryError.class, store::close); + assertClosed(); + + verifyNoInteractions(closeAction); + + store.close(); + assertClosed(); + } + + @Test + void closesStoreEvenIfCloseActionThrowsException() throws Throwable { + store.put(namespace, key, value); + doThrow(IllegalStateException.class).when(closeAction).close(namespace, key, value); + + assertThrows(IllegalStateException.class, store::close); + assertClosed(); + + verify(closeAction).close(namespace, key, value); + verifyNoMoreInteractions(closeAction); + + store.close(); + assertClosed(); + } + + @Test + void closesStoreEvenIfCloseActionThrowsUnrecoverableException() throws Throwable { + store.put(namespace, key, value); + doThrow(OutOfMemoryError.class).when(closeAction).close(namespace, key, value); assertThrows(OutOfMemoryError.class, store::close); + assertClosed(); + + verify(closeAction).close(namespace, key, value); + verifyNoMoreInteractions(closeAction); + + store.close(); + assertClosed(); + } + + @Test + void closesStoreEvenIfNoCloseActionIsConfigured() { + @SuppressWarnings("resource") + var localStore = new NamespacedHierarchicalStore<>(null); + assertThat(localStore.isClosed()).isFalse(); + localStore.close(); + assertThat(localStore.isClosed()).isTrue(); + } + + @Test + void closeIsIdempotent() throws Throwable { + store.put(namespace, key, value); verifyNoInteractions(closeAction); + + store.close(); + assertClosed(); + + verify(closeAction, times(1)).close(namespace, key, value); + + store.close(); + assertClosed(); + + verifyNoMoreInteractions(closeAction); } + + /** + * @see #3944 + */ + @Test + void acceptsQueryAfterClose() { + store.put(namespace, key, value); + store.close(); + assertClosed(); + + assertThat(store.get(namespace, key)).isEqualTo(value); + assertThat(store.get(namespace, key, String.class)).isEqualTo(value); + assertThat(store.getOrComputeIfAbsent(namespace, key, k -> "new")).isEqualTo(value); + assertThat(store.getOrComputeIfAbsent(namespace, key, k -> "new", String.class)).isEqualTo(value); + } + + @Test + void rejectsModificationAfterClose() { + store.close(); + assertClosed(); + + assertThrows(NamespacedHierarchicalStoreException.class, () -> store.put(namespace, key, value)); + assertThrows(NamespacedHierarchicalStoreException.class, () -> store.remove(namespace, key)); + assertThrows(NamespacedHierarchicalStoreException.class, () -> store.remove(namespace, key, int.class)); + + // Since key does not exist, an invocation of getOrComputeIfAbsent(...) will attempt to compute a new value. + assertThrows(NamespacedHierarchicalStoreException.class, + () -> store.getOrComputeIfAbsent(namespace, key, k -> "new")); + assertThrows(NamespacedHierarchicalStoreException.class, + () -> store.getOrComputeIfAbsent(namespace, key, k -> "new", String.class)); + } + + private void assertNotClosed() { + assertThat(store.isClosed()).as("closed").isFalse(); + } + + private void assertClosed() { + assertThat(store.isClosed()).as("closed").isTrue(); + } + } - private Object createObject(final String display) { + private static Object createObject(String display) { return new Object() { @Override diff --git a/platform-tests/src/test/java/org/junit/platform/jfr/FlightRecordingDiscoveryListenerIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/jfr/FlightRecordingDiscoveryListenerIntegrationTests.java index 9dfdc786b147..2767ec89746e 100644 --- a/platform-tests/src/test/java/org/junit/platform/jfr/FlightRecordingDiscoveryListenerIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/jfr/FlightRecordingDiscoveryListenerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -16,6 +16,7 @@ import static org.moditect.jfrunit.JfrEventsAssert.assertThat; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.DisabledOnOpenJ9; import org.junit.jupiter.engine.JupiterTestEngine; import org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly; import org.moditect.jfrunit.EnableEvent; @@ -23,6 +24,7 @@ import org.moditect.jfrunit.JfrEvents; @JfrEventTest +@DisabledOnOpenJ9 public class FlightRecordingDiscoveryListenerIntegrationTests { public JfrEvents jfrEvents = new JfrEvents(); diff --git a/platform-tests/src/test/java/org/junit/platform/jfr/FlightRecordingExecutionListenerIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/jfr/FlightRecordingExecutionListenerIntegrationTests.java index d75732ba8f0b..c209c692907b 100644 --- a/platform-tests/src/test/java/org/junit/platform/jfr/FlightRecordingExecutionListenerIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/jfr/FlightRecordingExecutionListenerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -18,6 +18,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestReporter; +import org.junit.jupiter.api.extension.DisabledOnOpenJ9; import org.junit.jupiter.engine.JupiterTestEngine; import org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly; import org.moditect.jfrunit.EnableEvent; @@ -25,6 +26,7 @@ import org.moditect.jfrunit.JfrEvents; @JfrEventTest +@DisabledOnOpenJ9 public class FlightRecordingExecutionListenerIntegrationTests { public JfrEvents jfrEvents = new JfrEvents(); diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/DiscoveryFilterStub.java b/platform-tests/src/test/java/org/junit/platform/launcher/DiscoveryFilterStub.java index 1ca8e6b475b0..a31de9b54f55 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/DiscoveryFilterStub.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/DiscoveryFilterStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/FilterStub.java b/platform-tests/src/test/java/org/junit/platform/launcher/FilterStub.java index 04c7eab08c43..01771f42889b 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/FilterStub.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/FilterStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/InterceptedTestEngine.java b/platform-tests/src/test/java/org/junit/platform/launcher/InterceptedTestEngine.java index dc57b1ff7a22..ce90b6eb1620 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/InterceptedTestEngine.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/InterceptedTestEngine.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/InterceptorInjectedLauncherSessionListener.java b/platform-tests/src/test/java/org/junit/platform/launcher/InterceptorInjectedLauncherSessionListener.java index bbaed89e2e91..606f6cedef5f 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/InterceptorInjectedLauncherSessionListener.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/InterceptorInjectedLauncherSessionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/PostDiscoveryFilterStub.java b/platform-tests/src/test/java/org/junit/platform/launcher/PostDiscoveryFilterStub.java index d16a7bbdd597..e863f8c56d67 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/PostDiscoveryFilterStub.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/PostDiscoveryFilterStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/TagFilterTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/TagFilterTests.java index 2ac0fab7c2df..0c1b54e9b4d7 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/TagFilterTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/TagFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/TagIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/TagIntegrationTests.java index fd9809c1c558..745550c514f6 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/TagIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/TagIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/TestIdentifierTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/TestIdentifierTests.java index 8d007f4528ed..7ba6716495e6 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/TestIdentifierTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/TestIdentifierTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -72,6 +72,21 @@ void initialVersionCanBeDeserialized() throws Exception { } } + @Test + void identifierWithNoParentCanBeSerializedAndDeserialized() throws Exception { + TestIdentifier originalIdentifier = TestIdentifier.from( + new AbstractTestDescriptor(UniqueId.root("example", "id"), "Example") { + @Override + public Type getType() { + return Type.CONTAINER; + } + }); + + var deserializedIdentifier = (TestIdentifier) deserialize(serialize(originalIdentifier)); + + assertDeepEquals(originalIdentifier, deserializedIdentifier); + } + private static void assertDeepEquals(TestIdentifier first, TestIdentifier second) { assertEquals(first, second); assertEquals(first.getUniqueId(), second.getUniqueId()); diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherDiscoveryListener.java b/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherDiscoveryListener.java index 7a5be0c967a4..93e91f168108 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherDiscoveryListener.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherDiscoveryListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherInterceptor1.java b/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherInterceptor1.java index 6ff0dc7046dd..a077c68afad3 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherInterceptor1.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherInterceptor1.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherInterceptor2.java b/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherInterceptor2.java index 04f9ad3e29a0..6adbd6a56e35 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherInterceptor2.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherInterceptor2.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherSessionListener.java b/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherSessionListener.java index 02a39507d6ac..e239effb560b 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherSessionListener.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherSessionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/TestPlanTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/TestPlanTests.java index 0f193577edd0..e62df473bb38 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/TestPlanTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/TestPlanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/TestPostDiscoveryTagFilter.java b/platform-tests/src/test/java/org/junit/platform/launcher/TestPostDiscoveryTagFilter.java index 5ba0aada6c64..7b79b2e4ab49 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/TestPostDiscoveryTagFilter.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/TestPostDiscoveryTagFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/CompositeEngineExecutionListenerTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/CompositeEngineExecutionListenerTests.java index 4935d038d107..d56669e6eb80 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/CompositeEngineExecutionListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/CompositeEngineExecutionListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/CompositeTestExecutionListenerTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/CompositeTestExecutionListenerTests.java index 009a2ad74c64..cf3004e23966 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/CompositeTestExecutionListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/CompositeTestExecutionListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/DefaultLauncherEngineFilterTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/DefaultLauncherEngineFilterTests.java index 85732038f6de..7f473923ec20 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/DefaultLauncherEngineFilterTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/DefaultLauncherEngineFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -148,8 +148,7 @@ void launcherWillExecuteEnginesHonoringBothIncludeAndExcludeEngineFilters() { } @Test - @TrackLogRecords - void launcherThrowsExceptionWhenNoEngineMatchesIncludeEngineFilter(LogRecordListener log) { + void launcherThrowsExceptionWhenNoEngineMatchesIncludeEngineFilter(@TrackLogRecords LogRecordListener log) { var engine = new DemoHierarchicalTestEngine("first"); TestDescriptor test1 = engine.addTest("test1", noOp); LauncherDiscoveryRequest request = request() // @@ -169,8 +168,7 @@ void launcherThrowsExceptionWhenNoEngineMatchesIncludeEngineFilter(LogRecordList } @Test - @TrackLogRecords - void launcherWillLogWarningWhenAllEnginesWereExcluded(LogRecordListener log) { + void launcherWillLogWarningWhenAllEnginesWereExcluded(@TrackLogRecords LogRecordListener log) { var engine = new DemoHierarchicalTestEngine("first"); TestDescriptor test = engine.addTest("test1", noOp); diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/DefaultLauncherTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/DefaultLauncherTests.java index f5b777b4b71d..0f1a3d50f76d 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/DefaultLauncherTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/DefaultLauncherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -597,8 +597,7 @@ void testPlanThrowsExceptionWhenModified() { } @Test - @TrackLogRecords - void thirdPartyEngineUsingReservedEngineIdPrefixEmitsWarning(LogRecordListener listener) { + void thirdPartyEngineUsingReservedEngineIdPrefixEmitsWarning(@TrackLogRecords LogRecordListener listener) { var id = "junit-using-reserved-prefix"; var launcher = createLauncher(new TestEngineStub(id)); launcher.discover(request().build()); diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/EngineDiscoveryResultValidatorTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/EngineDiscoveryResultValidatorTests.java index 93f4d0b03e84..8744124def16 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/EngineDiscoveryResultValidatorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/EngineDiscoveryResultValidatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/ExecutionListenerAdapterTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/ExecutionListenerAdapterTests.java index c120c5c79349..0d2d5786687d 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/ExecutionListenerAdapterTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/ExecutionListenerAdapterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherConfigTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherConfigTests.java index 63182980a0b5..5471b0214be1 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherConfigTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherConfigurationParametersTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherConfigurationParametersTests.java index 31154cfa322b..3ef5cb275ee4 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherConfigurationParametersTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherConfigurationParametersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -14,7 +14,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; import java.util.Map; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.LogRecord; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -22,8 +30,11 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.TestInstancePostProcessor; +import org.junit.jupiter.api.fixtures.TrackLogRecords; +import org.junit.jupiter.api.io.TempDir; import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.commons.logging.LogRecordListener; import org.junit.platform.engine.ConfigurationParameters; import org.junit.platform.engine.discovery.DiscoverySelectors; import org.junit.platform.launcher.listeners.SummaryGeneratingListener; @@ -190,6 +201,40 @@ void ignoresSystemPropertyAndConfigFileWhenImplicitLookupsAreDisabled() { assertThat(configParams.get(KEY)).isEmpty(); } + @Test + void warnsOnMultiplePropertyResources(@TempDir Path tempDir, @TrackLogRecords LogRecordListener logRecordListener) + throws Exception { + Properties properties = new Properties(); + properties.setProperty(KEY, "from second config file"); + try (var out = Files.newOutputStream(tempDir.resolve(CONFIG_FILE_NAME))) { + properties.store(out, ""); + } + ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); + try (var customClassLoader = new URLClassLoader(new URL[] { tempDir.toUri().toURL() }, originalClassLoader)) { + Thread.currentThread().setContextClassLoader(customClassLoader); + ConfigurationParameters configParams = fromMapAndFile(Map.of(), CONFIG_FILE_NAME); + + assertThat(configParams.get(KEY)).contains(CONFIG_FILE); + + List loggedWarnings = logRecordListener.stream(Level.WARNING) // + .map(LogRecord::getMessage) // + .toList(); + assertThat(loggedWarnings) // + .hasSize(1); + assertThat(loggedWarnings.getFirst()) // + .contains("Discovered 2 '" + CONFIG_FILE_NAME + + "' configuration files on the classpath (see below); only the first (*) will be used.") // + .contains("- " + + Path.of( + "build/resources/test/test-junit-platform.properties").toAbsolutePath().toUri().toURL() + + " (*)") // + .contains("- " + tempDir.resolve(CONFIG_FILE_NAME).toUri().toURL()); + } + finally { + Thread.currentThread().setContextClassLoader(originalClassLoader); + } + } + private static LauncherConfigurationParameters fromMap(Map map) { return LauncherConfigurationParameters.builder().explicitParameters(map).build(); } diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilderTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilderTests.java index f75ee1017fd3..3ec50bc52dc0 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilderTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -313,6 +313,48 @@ void multipleConfigurationParametersAddedByMap_areStoredInDiscoveryRequest() { assertThat(configParams.get("key1")).contains("value1"); assertThat(configParams.get("key2")).contains("value2"); } + + @Test + void configurationParametersResource_areStoredInDiscoveryRequest() { + // @formatter:off + var discoveryRequest = request() + .configurationParametersResources("config-test.properties") + .build(); + // @formatter:on + + var configParams = discoveryRequest.getConfigurationParameters(); + assertThat(configParams.get("com.example.prop.first")).contains("first value"); + assertThat(configParams.get("com.example.prop.second")).contains("second value"); + assertThat(configParams.get("com.example.prop.third")).contains("third value"); + } + + @Test + void configurationParametersResource_explicitConfigParametersOverrideResource() { + // @formatter:off + var discoveryRequest = request() + .configurationParametersResources("config-test.properties") + .configurationParameter("com.example.prop.first", "first value override") + .build(); + // @formatter:on + + var configParams = discoveryRequest.getConfigurationParameters(); + assertThat(configParams.get("com.example.prop.first")).contains("first value override"); + assertThat(configParams.get("com.example.prop.second")).contains("second value"); + } + + @Test + void configurationParametersResource_lastDeclaredResourceFileWins() { + // @formatter:off + var discoveryRequest = request() + .configurationParametersResources("config-test.properties") + .configurationParametersResources("config-test-override.properties") + .build(); + // @formatter:on + + var configParams = discoveryRequest.getConfigurationParameters(); + assertThat(configParams.get("com.example.prop.first")).contains("first value from override file"); + assertThat(configParams.get("com.example.prop.second")).contains("second value"); + } } @Nested diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherFactoryTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherFactoryTests.java index d1432692d6d1..69dba3c41872 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherFactoryTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherSessionTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherSessionTests.java index 761eba797723..95e38a9c733c 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherSessionTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherSessionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/ListenerRegistryTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/ListenerRegistryTests.java index 664076d24abd..2c73ee57e198 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/ListenerRegistryTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/ListenerRegistryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/StreamInterceptingTestExecutionListenerIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/StreamInterceptingTestExecutionListenerIntegrationTests.java index cd70637eed3c..01aacc458db0 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/StreamInterceptingTestExecutionListenerIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/StreamInterceptingTestExecutionListenerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/StreamInterceptorTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/StreamInterceptorTests.java index c3ba0f96604d..d524f5b7d283 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/StreamInterceptorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/StreamInterceptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/AnotherUnusedTestExecutionListener.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/AnotherUnusedTestExecutionListener.java index a0ca73ca6361..7d6dcc2b8326 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/AnotherUnusedTestExecutionListener.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/AnotherUnusedTestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/LoggingListenerTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/LoggingListenerTests.java new file mode 100644 index 000000000000..abe2e5f89f82 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/LoggingListenerTests.java @@ -0,0 +1,127 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.launcher.listeners; + +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.abort; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.junit.platform.launcher.EngineFilter.includeEngines; +import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; + +import java.util.function.BiConsumer; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.platform.launcher.core.LauncherConfig; +import org.junit.platform.launcher.core.LauncherFactory; +import org.mockito.ArgumentMatchers; +import org.opentest4j.AssertionFailedError; +import org.opentest4j.TestAbortedException; + +public class LoggingListenerTests { + + @Test + void logsExecutionEvents() { + BiConsumer logger = mock(); + + executeTestCase(LoggingListener.forBiConsumer((t, m) -> { + System.out.println(m.get()); + logger.accept(t, m.get()); + })); + + var inOrder = inOrder(logger); + inOrder.verify(logger).accept(isNull(), + startsWith("TestPlan Execution Started: org.junit.platform.launcher.TestPlan@")); + inOrder.verify(logger).accept(isNull(), eq("Execution Started: JUnit Jupiter - [engine:junit-jupiter]")); + inOrder.verify(logger).accept(isNull(), eq( + "Execution Started: LoggingListenerTests$TestCase - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]")); + inOrder.verify(logger).accept(isNull(), eq( + "Execution Started: success() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[test-factory:success()]")); + inOrder.verify(logger).accept(isNull(), eq( + "Dynamic Test Registered: dynamic - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[test-factory:success()]/[dynamic-test:#1]")); + inOrder.verify(logger).accept(isNull(), eq( + "Execution Started: dynamic - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[test-factory:success()]/[dynamic-test:#1]")); + inOrder.verify(logger).accept(isNull(), eq( + "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]")); + inOrder.verify(logger).accept(isNull(), eq( + "Execution Finished: success() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[test-factory:success()] - TestExecutionResult [status = SUCCESSFUL, throwable = null]")); + inOrder.verify(logger).accept(isNull(), eq( + "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")); + inOrder.verify(logger).accept(isNull(), eq( + "Execution Started: failed() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[method:failed()]")); + inOrder.verify(logger).accept(ArgumentMatchers.notNull(AssertionFailedError.class), eq( + "Execution Finished: failed() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[method:failed()] - TestExecutionResult [status = FAILED, throwable = org.opentest4j.AssertionFailedError]")); + inOrder.verify(logger).accept(isNull(), eq( + "Execution Started: aborted() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[method:aborted()]")); + inOrder.verify(logger).accept(ArgumentMatchers.notNull(TestAbortedException.class), eq( + "Execution Finished: aborted() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[method:aborted()] - TestExecutionResult [status = ABORTED, throwable = org.opentest4j.TestAbortedException]")); + inOrder.verify(logger).accept(isNull(), eq( + "Execution Finished: LoggingListenerTests$TestCase - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase] - TestExecutionResult [status = SUCCESSFUL, throwable = null]")); + inOrder.verify(logger).accept(isNull(), eq( + "Execution Finished: JUnit Jupiter - [engine:junit-jupiter] - TestExecutionResult [status = SUCCESSFUL, throwable = null]")); + inOrder.verify(logger).accept(isNull(), + startsWith("TestPlan Execution Finished: org.junit.platform.launcher.TestPlan@")); + inOrder.verifyNoMoreInteractions(); + } + + private static void executeTestCase(LoggingListener listener) { + var config = LauncherConfig.builder() // + .enableTestExecutionListenerAutoRegistration(false) // + .addTestExecutionListeners() // + .build(); + var request = request() // + .selectors(selectClass(TestCase.class)) // + .filters(includeEngines("junit-jupiter")).build(); + LauncherFactory.create(config) // + .execute(request, listener); + } + + @SuppressWarnings("JUnitMalformedDeclaration") + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + static class TestCase { + @TestFactory + @Order(1) + Stream success() { + return Stream.of(dynamicTest("dynamic", () -> { + })); + } + + @Test + @Disabled + @Order(2) + void skipped() { + } + + @Test + @Order(3) + void failed() { + fail(); + } + + @Test + @Order(4) + void aborted() { + abort(); + } + } +} diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/NoopTestExecutionListener.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/NoopTestExecutionListener.java index 268dd599c80f..8d21878f1dc2 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/NoopTestExecutionListener.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/NoopTestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/OutputDirTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/OutputDirTests.java index a876c5bf4d2f..ecab5c1b90c5 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/OutputDirTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/OutputDirTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/SummaryGenerationTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/SummaryGenerationTests.java index 7c947bf1d8d7..3eeb506f44f1 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/SummaryGenerationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/SummaryGenerationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListenerIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListenerIntegrationTests.java index 86943a9cc093..9d56bddc5761 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListenerIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListenerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -46,6 +46,7 @@ import org.assertj.core.api.Condition; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; import org.junit.platform.engine.TestDescriptor; @@ -54,6 +55,7 @@ import org.junit.platform.launcher.LauncherDiscoveryRequest; import org.junit.platform.launcher.TestExecutionListener; import org.junit.platform.launcher.TestIdentifier; +import org.junit.platform.launcher.TestPlan; import org.junit.platform.launcher.core.LauncherFactory; import org.junit.platform.testkit.engine.EngineTestKit; import org.junit.platform.testkit.engine.Event; @@ -79,9 +81,10 @@ class UniqueIdTrackingListenerIntegrationTests { private static final String testD = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase3]/[method:testD()]"; private static final String testE = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase4]/[method:testE()]"; private static final String testF = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase4]/[method:testF()]"; + private static final String testG = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$DisabledTestCase]/[nested-class:Inner]/[method:testG()]"; private static final String[] expectedUniqueIds = { passingTest, skippedTest, abortedTest, failingTest, - dynamicTest1, dynamicTest2, testA, testB }; + dynamicTest1, dynamicTest2, testA, testB, testG }; private static final String[] expectedConcurrentUniqueIds = { testA, testB, testC, testD, testE, testF }; @@ -225,11 +228,21 @@ private static List executeTests(Map configurationParame .build(); LauncherFactory.create().execute(request, new TestExecutionListener() { + private TestPlan testPlan; + + @Override + public void testPlanExecutionStarted(TestPlan testPlan) { + this.testPlan = testPlan; + } + @Override public void executionSkipped(TestIdentifier testIdentifier, String reason) { if (testIdentifier.isTest()) { uniqueIds.add(testIdentifier.getUniqueId()); } + else { + this.testPlan.getChildren(testIdentifier).forEach(child -> executionSkipped(child, reason)); + } } @Override @@ -243,7 +256,8 @@ public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult } private static ClassSelector[] selectClasses() { - return new ClassSelector[] { selectClass(TestCase1.class), selectClass(TestCase2.class) }; + return new ClassSelector[] { selectClass(TestCase1.class), selectClass(TestCase2.class), + selectClass(DisabledTestCase.class) }; } private static Stream findFiles(String dir, String prefix) throws IOException { @@ -340,4 +354,16 @@ void testF() { } } + @Disabled + static class DisabledTestCase { + + @Nested + class Inner { + + @Test + void testG() { + } + } + } + } diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UnusedTestExecutionListener.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UnusedTestExecutionListener.java index 53353c2ff3ff..b34399936e4c 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UnusedTestExecutionListener.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UnusedTestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/AbortOnFailureLauncherDiscoveryListenerTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/AbortOnFailureLauncherDiscoveryListenerTests.java index 0ee89659d92a..223421d72da9 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/AbortOnFailureLauncherDiscoveryListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/AbortOnFailureLauncherDiscoveryListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/AbstractLauncherDiscoveryListenerTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/AbstractLauncherDiscoveryListenerTests.java index 1666f66f48d4..060df776f852 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/AbstractLauncherDiscoveryListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/AbstractLauncherDiscoveryListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/CompositeLauncherDiscoveryListenerTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/CompositeLauncherDiscoveryListenerTests.java index 71091aae7b7c..00fe416ec014 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/CompositeLauncherDiscoveryListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/CompositeLauncherDiscoveryListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/LoggingLauncherDiscoveryListenerTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/LoggingLauncherDiscoveryListenerTests.java index dc25e14a76ed..4ec465bfcbf3 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/LoggingLauncherDiscoveryListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/LoggingLauncherDiscoveryListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/session/CompositeLauncherSessionListenerTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/session/CompositeLauncherSessionListenerTests.java index ecfcfa215d4d..13e4ec36b440 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/session/CompositeLauncherSessionListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/session/CompositeLauncherSessionListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/ParserErrorTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/ParserErrorTests.java index 3691dce22f1a..43fb4e25dbda 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/ParserErrorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/ParserErrorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/ParserTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/ParserTests.java index 4b15ba0fb46e..9fbab1299437 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/ParserTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/ParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TagExpressionsTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TagExpressionsTests.java index 852ed6af2f69..8ef18564f46b 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TagExpressionsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TagExpressionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TokenTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TokenTests.java index 387d2986aafa..b8fca58da169 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TokenTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TokenTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TokenizerTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TokenizerTests.java index 94aa30442a61..e882fe92d908 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TokenizerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TokenizerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/LegacyReportingUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/LegacyReportingUtilsTests.java index 2cf36b309f4a..408a85ef9bc9 100644 --- a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/LegacyReportingUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/LegacyReportingUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/IncrementingClock.java b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/IncrementingClock.java index d46dad4a88a3..a167ab629e6a 100644 --- a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/IncrementingClock.java +++ b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/IncrementingClock.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListenerTests.java b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListenerTests.java index 4e4c14cabece..de3b42a79f2f 100644 --- a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportAssertions.java b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportAssertions.java index 22b3d7d08a4b..fd220731d0b6 100644 --- a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportAssertions.java +++ b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportAssertions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportDataTests.java b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportDataTests.java index fb88bc9f872b..8d685654c1fa 100644 --- a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportDataTests.java +++ b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportDataTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java index 8487afb1bceb..b66b09e50e3c 100644 --- a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java +++ b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListenerTests.java b/platform-tests/src/test/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListenerTests.java index 99398dbc31e8..6a285f05c995 100644 --- a/platform-tests/src/test/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -13,6 +13,7 @@ import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.condition.JRE.JAVA_22; import static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId; import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; @@ -26,6 +27,7 @@ import java.nio.file.Path; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledForJreRange; import org.junit.jupiter.api.io.TempDir; import org.junit.platform.engine.TestEngine; import org.junit.platform.engine.UniqueId; @@ -41,6 +43,7 @@ * * @since 1.9 */ +@DisabledForJreRange(min = JAVA_22, disabledReason = "https://github.com/junit-team/junit5/issues/3594") public class OpenTestReportGeneratingListenerTests { @TempDir(cleanup = ON_SUCCESS) diff --git a/platform-tests/src/test/java/org/junit/platform/runner/JUnitPlatformRunnerTests.java b/platform-tests/src/test/java/org/junit/platform/runner/JUnitPlatformRunnerTests.java index a515e6dd2990..b9f54ef9d147 100644 --- a/platform-tests/src/test/java/org/junit/platform/runner/JUnitPlatformRunnerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/runner/JUnitPlatformRunnerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/commons/SuiteLauncherDiscoveryRequestBuilderTests.java b/platform-tests/src/test/java/org/junit/platform/suite/commons/SuiteLauncherDiscoveryRequestBuilderTests.java index 55aa8d6eaa04..5575d4996038 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/commons/SuiteLauncherDiscoveryRequestBuilderTests.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/commons/SuiteLauncherDiscoveryRequestBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -58,6 +58,7 @@ import org.junit.platform.launcher.LauncherDiscoveryRequest; import org.junit.platform.launcher.PostDiscoveryFilter; import org.junit.platform.suite.api.ConfigurationParameter; +import org.junit.platform.suite.api.ConfigurationParametersResource; import org.junit.platform.suite.api.DisableParentConfigurationParameters; import org.junit.platform.suite.api.ExcludeClassNamePatterns; import org.junit.platform.suite.api.ExcludeEngines; @@ -67,6 +68,7 @@ import org.junit.platform.suite.api.IncludeEngines; import org.junit.platform.suite.api.IncludePackages; import org.junit.platform.suite.api.IncludeTags; +import org.junit.platform.suite.api.Select; import org.junit.platform.suite.api.SelectClasses; import org.junit.platform.suite.api.SelectClasspathResource; import org.junit.platform.suite.api.SelectDirectories; @@ -86,12 +88,54 @@ void configurationParameter() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applyConfigurationParametersFromSuite(Suite.class).build(); ConfigurationParameters configuration = request.getConfigurationParameters(); Optional parameter = configuration.get("com.example"); assertEquals(Optional.of("*"), parameter); } + @Test + void configurationParametersResource() { + @ConfigurationParametersResource("config-test.properties") + class Suite { + } + + LauncherDiscoveryRequest request = builder.applyConfigurationParametersFromSuite(Suite.class).build(); + ConfigurationParameters configuration = request.getConfigurationParameters(); + Optional parameter = configuration.get("com.example.prop.first"); + assertEquals(Optional.of("first value"), parameter); + } + + @Test + void configurationParametersResources() { + @ConfigurationParametersResource("config-test.properties") + @ConfigurationParametersResource("config-test-override.properties") + class Suite { + } + + LauncherDiscoveryRequest request = builder.applyConfigurationParametersFromSuite(Suite.class).build(); + ConfigurationParameters configuration = request.getConfigurationParameters(); + Optional parameterOne = configuration.get("com.example.prop.first"); + assertEquals(Optional.of("first value from override file"), parameterOne); + Optional parameterTwo = configuration.get("com.example.prop.second"); + assertEquals(Optional.of("second value"), parameterTwo); + } + + @Test + void configurationParametersResource_explicitParametersTakePrecedence() { + @ConfigurationParametersResource("config-test.properties") + @ConfigurationParameter(key = "com.example.prop.first", value = "first value from explicit parameter") + class Suite { + } + + LauncherDiscoveryRequest request = builder.applyConfigurationParametersFromSuite(Suite.class).build(); + ConfigurationParameters configuration = request.getConfigurationParameters(); + Optional parameterOne = configuration.get("com.example.prop.first"); + assertEquals(Optional.of("first value from explicit parameter"), parameterOne); + Optional parameterTwo = configuration.get("com.example.prop.second"); + assertEquals(Optional.of("second value"), parameterTwo); + } + @Test void excludeClassNamePatterns() { class TestCase { @@ -100,7 +144,7 @@ class TestCase { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List filters = request.getFiltersByType(ClassNameFilter.class); assertTrue(exactlyOne(filters).apply(TestCase.class.getName()).excluded()); } @@ -111,7 +155,7 @@ void excludeEngines() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List filters = request.getEngineFilters(); assertTrue(exactlyOne(filters).apply(new JupiterTestEngine()).excluded()); } @@ -122,7 +166,7 @@ void excludePackages() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List filters = request.getFiltersByType(PackageNameFilter.class); assertTrue(exactlyOne(filters).apply("com.example.testcases").excluded()); } @@ -133,7 +177,7 @@ void excludeTags() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List filters = request.getPostDiscoveryFilters(); TestDescriptor testDescriptor = new StubAbstractTestDescriptor(); assertTrue(exactlyOne(filters).apply(testDescriptor).excluded()); @@ -147,7 +191,7 @@ class TestCase { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List filters = request.getFiltersByType(ClassNameFilter.class); assertTrue(exactlyOne(filters).apply(TestCase.class.getName()).included()); assertTrue(exactlyOne(filters).apply(Suite.class.getName()).excluded()); @@ -158,7 +202,7 @@ void filtersOnStandardClassNamePatternsWhenIncludeClassNamePatternsIsOmitted() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); assertTrue(request.getFiltersByType(ClassNameFilter.class).isEmpty()); } @@ -172,7 +216,7 @@ class Suite { // @formatter:off LauncherDiscoveryRequest request = builder .filterStandardClassNamePatterns(true) - .suite(Suite.class) + .applySelectorsAndFiltersFromSuite(Suite.class) .build(); // @formatter:on List filters = request.getFiltersByType(ClassNameFilter.class); @@ -186,7 +230,7 @@ void includeEngines() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List filters = request.getEngineFilters(); assertTrue(exactlyOne(filters).apply(new JupiterTestEngine()).included()); } @@ -197,7 +241,7 @@ void includePackages() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List filters = request.getFiltersByType(PackageNameFilter.class); assertTrue(exactlyOne(filters).apply("com.example.testcases").included()); } @@ -208,7 +252,7 @@ void includeTags() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List filters = request.getPostDiscoveryFilters(); TestDescriptor testDescriptor = new StubAbstractTestDescriptor(); assertTrue(exactlyOne(filters).apply(testDescriptor).included()); @@ -222,7 +266,7 @@ class TestCase { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(ClassSelector.class); assertFalse(selectors.isEmpty()); assertEquals(TestCase.class, exactlyOne(selectors).getJavaClass()); @@ -234,7 +278,7 @@ void selectClassesByName() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(ClassSelector.class); assertEquals(NonLocalTestCase.class, exactlyOne(selectors).getJavaClass()); } @@ -245,7 +289,8 @@ void selectClassesWithoutReferencesOrNames() { class Suite { } - var e = assertThrows(PreconditionViolationException.class, () -> builder.suite(Suite.class)); + var e = assertThrows(PreconditionViolationException.class, + () -> builder.applySelectorsAndFiltersFromSuite(Suite.class)); assertThat(e).hasMessageMatching( "@SelectClasses on class \\[" + Pattern.quote(SuiteLauncherDiscoveryRequestBuilderTests.class.getName()) @@ -269,7 +314,7 @@ class SuiteC { return Stream.of(SuiteA.class, SuiteB.class, SuiteC.class) // .map(suiteClass -> dynamicTest(suiteClass.getSimpleName(), () -> { - LauncherDiscoveryRequest request = request().suite(suiteClass).build(); + LauncherDiscoveryRequest request = request().applySelectorsAndFiltersFromSuite(suiteClass).build(); List selectors = request.getSelectorsByType(MethodSelector.class); assertEquals(DiscoverySelectors.selectMethod(NoParameterTestCase.class, "testMethod"), exactlyOne(selectors)); @@ -302,7 +347,7 @@ class SuiteE { return Stream.of(SuiteA.class, SuiteB.class, SuiteC.class, SuiteD.class, SuiteE.class) // .map(suiteClass -> dynamicTest(suiteClass.getSimpleName(), () -> { - LauncherDiscoveryRequest request = request().suite(suiteClass).build(); + LauncherDiscoveryRequest request = request().applySelectorsAndFiltersFromSuite(suiteClass).build(); List selectors = request.getSelectorsByType(MethodSelector.class); assertEquals(DiscoverySelectors.selectMethod(OneParameterTestCase.class, "testMethod", "int"), exactlyOne(selectors)); @@ -331,7 +376,7 @@ void secondTestMethod(boolean i, float j) { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(MethodSelector.class); assertEquals(2, selectors.size()); assertEquals(DiscoverySelectors.selectMethod(TestClass.class, "firstTestMethod", "int, java.lang.String"), @@ -387,7 +432,7 @@ class ParameterTypesAndParameterTypeNames { var expectedFailureMessage = entry.getValue(); return dynamicTest(suiteClassName.getSimpleName(), () -> { var ex = assertThrows(PreconditionViolationException.class, - () -> request().suite(suiteClassName)); + () -> request().applySelectorsAndFiltersFromSuite(suiteClassName)); assertEquals( "@SelectMethod on class [" + suiteClassName.getName() + "]: " + expectedFailureMessage, ex.getMessage()); @@ -401,7 +446,7 @@ void selectClasspathResource() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(ClasspathResourceSelector.class); assertEquals("com.example.testcases", exactlyOne(selectors).getClasspathResourceName()); } @@ -413,7 +458,7 @@ void selectClasspathResourcePosition() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(ClasspathResourceSelector.class); assertEquals(Optional.of(FilePosition.from(42)), selectors.get(0).getPosition()); assertEquals(Optional.of(FilePosition.from(14, 15)), selectors.get(1).getPosition()); @@ -427,7 +472,7 @@ void ignoreClasspathResourcePosition() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(ClasspathResourceSelector.class); assertEquals(Optional.empty(), selectors.get(0).getPosition()); assertEquals(Optional.empty(), selectors.get(1).getPosition()); @@ -440,7 +485,7 @@ void selectDirectories() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(DirectorySelector.class); assertEquals(Paths.get("path/to/root"), exactlyOne(selectors).getPath()); } @@ -451,7 +496,7 @@ void selectDirectoriesFiltersEmptyPaths() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); assertTrue(request.getSelectorsByType(DirectorySelector.class).isEmpty()); } @@ -461,7 +506,7 @@ void selectFile() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(FileSelector.class); assertEquals(Paths.get("path/to/root"), exactlyOne(selectors).getPath()); } @@ -473,7 +518,7 @@ void selectFilePosition() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(FileSelector.class); assertEquals(Optional.of(FilePosition.from(42)), selectors.get(0).getPosition()); assertEquals(Optional.of(FilePosition.from(14, 15)), selectors.get(1).getPosition()); @@ -487,7 +532,7 @@ void ignoreInvalidFilePosition() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(FileSelector.class); assertEquals(Optional.empty(), selectors.get(0).getPosition()); assertEquals(Optional.empty(), selectors.get(1).getPosition()); @@ -500,7 +545,7 @@ void selectModules() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(ModuleSelector.class); assertEquals("com.example.testcases", exactlyOne(selectors).getModuleName()); } @@ -511,7 +556,7 @@ void selectUris() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(UriSelector.class); assertEquals(URI.create("path/to/root"), exactlyOne(selectors).getUri()); } @@ -522,7 +567,7 @@ void selectUrisFiltersEmptyUris() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); assertTrue(request.getSelectorsByType(UriSelector.class).isEmpty()); } @@ -532,7 +577,7 @@ void selectPackages() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List selectors = request.getSelectorsByType(PackageSelector.class); assertEquals("com.example.testcases", exactlyOne(selectors).getPackageName()); } @@ -548,7 +593,7 @@ void metaAnnotations() { class Suite { } - LauncherDiscoveryRequest request = builder.suite(Suite.class).build(); + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); List pSelectors = request.getSelectorsByType(PackageSelector.class); assertEquals("com.example.testcases", exactlyOne(pSelectors).getPackageName()); } @@ -560,7 +605,7 @@ class Suite { } // @formatter:off var configuration = new ParentConfigurationParameters("parent", "parent parameters were used"); - var request = builder.suite(Suite.class) + var request = builder.applyConfigurationParametersFromSuite(Suite.class) .parentConfigurationParameters(configuration) .build(); // @formatter:on @@ -576,7 +621,7 @@ class Suite { } // @formatter:off var configuration = new ParentConfigurationParameters("parent", "parent parameters were used"); - var request = builder.suite(Suite.class) + var request = builder.applyConfigurationParametersFromSuite(Suite.class) .parentConfigurationParameters(configuration) .build(); // @formatter:on @@ -584,6 +629,28 @@ class Suite { assertEquals(Optional.empty(), configurationParameters.get("parent")); } + @Test + void selectByIdentifier() { + // @formatter:off + @Select({ + "class:org.junit.platform.suite.commons.SuiteLauncherDiscoveryRequestBuilderTests$NonLocalTestCase", + "method:org.junit.platform.suite.commons.SuiteLauncherDiscoveryRequestBuilderTests$NoParameterTestCase#testMethod" + }) + // @formatter:on + class Suite { + } + + LauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build(); + List classSelectors = request.getSelectorsByType(ClassSelector.class); + assertEquals(NonLocalTestCase.class, exactlyOne(classSelectors).getJavaClass()); + List methodSelectors = request.getSelectorsByType(MethodSelector.class); + // @formatter:off + assertThat(exactlyOne(methodSelectors)) + .extracting(MethodSelector::getJavaClass, MethodSelector::getMethodName) + .containsExactly(NoParameterTestCase.class, "testMethod"); + // @formatter:on + } + private static T exactlyOne(List list) { return CollectionUtils.getOnlyElement(list); } diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/BeforeAndAfterSuiteTests.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/BeforeAndAfterSuiteTests.java new file mode 100644 index 000000000000..c892eee146bb --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/BeforeAndAfterSuiteTests.java @@ -0,0 +1,229 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.engine; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Named.named; +import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.junit.platform.suite.engine.SuiteEngineDescriptor.ENGINE_ID; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.FailingAfterSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.FailingBeforeAndAfterSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.FailingBeforeSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.NonStaticAfterSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.NonStaticBeforeSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.NonVoidAfterSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.NonVoidBeforeSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.ParameterAcceptingAfterSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.ParameterAcceptingBeforeSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.PrivateAfterSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.PrivateBeforeSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.SeveralFailingBeforeAndAfterSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.SubclassWithBeforeAndAfterSuite; +import static org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.SuccessfulBeforeAndAfterSuite; +import static org.junit.platform.testkit.engine.EventConditions.container; +import static org.junit.platform.testkit.engine.EventConditions.event; +import static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully; +import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure; +import static org.junit.platform.testkit.engine.EventConditions.test; +import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf; +import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message; +import static org.junit.platform.testkit.engine.TestExecutionResultConditions.suppressed; + +import java.util.ArrayList; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.platform.commons.JUnitException; +import org.junit.platform.suite.api.AfterSuite; +import org.junit.platform.suite.api.BeforeSuite; +import org.junit.platform.suite.engine.testcases.StatefulTestCase; +import org.junit.platform.testkit.engine.EngineExecutionResults; +import org.junit.platform.testkit.engine.EngineTestKit; + +/** + * Integration tests that verify support for {@link BeforeSuite} and {@link AfterSuite}, + * in the {@link SuiteTestEngine}. + * + * @since 1.11 + */ +public class BeforeAndAfterSuiteTests { + + @BeforeEach + void setUp() { + StatefulTestCase.callSequence = new ArrayList<>(); + } + + @Test + void successfulBeforeAndAfterSuite() { + // @formatter:off + executeSuite(SuccessfulBeforeAndAfterSuite.class) + .allEvents() + .assertStatistics(stats -> stats.started(7).finished(7).succeeded(6).failed(1)) + .assertThatEvents() + .haveExactly(1, event(test(StatefulTestCase.Test1.class.getName()), finishedSuccessfully())) + .haveExactly(1, event(test(StatefulTestCase.Test2.class.getName()), finishedWithFailure())); + + assertThat(StatefulTestCase.callSequence).containsExactly( + "beforeSuiteMethod", + "test1", + "test2", + "afterSuiteMethod" + ); + // @formatter:on + } + + @Test + void beforeAndAfterSuiteInheritance() { + // @formatter:off + executeSuite(SubclassWithBeforeAndAfterSuite.class) + .allEvents() + .assertStatistics(stats -> stats.started(7).finished(7).succeeded(6).failed(1)); + + assertThat(StatefulTestCase.callSequence).containsExactly( + "superclassBeforeSuiteMethod", + "subclassBeforeSuiteMethod", + "test1", + "test2", + "subclassAfterSuiteMethod", + "superclassAfterSuiteMethod" + ); + // @formatter:on + } + + @Test + void failingBeforeSuite() { + // @formatter:off + executeSuite(FailingBeforeSuite.class) + .allEvents() + .assertStatistics(stats -> stats.started(2).finished(2).succeeded(1).failed(1)) + .assertThatEvents() + .haveExactly(1, event( + container(FailingBeforeSuite.class), + finishedWithFailure(instanceOf(RuntimeException.class), + message("Exception thrown by @BeforeSuite method")))); + + assertThat(StatefulTestCase.callSequence).containsExactly( + "beforeSuiteMethod", + "afterSuiteMethod" + ); + // @formatter:on + } + + @Test + void failingAfterSuite() { + // @formatter:off + executeSuite(FailingAfterSuite.class) + .allEvents() + .assertStatistics(stats -> stats.started(7).finished(7).succeeded(5).failed(2)) + .assertThatEvents() + .haveExactly(1, event( + container(FailingAfterSuite.class), + finishedWithFailure(instanceOf(RuntimeException.class), + message("Exception thrown by @AfterSuite method")))); + + assertThat(StatefulTestCase.callSequence).containsExactly( + "beforeSuiteMethod", + "test1", + "test2", + "afterSuiteMethod" + ); + // @formatter:on + } + + @Test + void failingBeforeAndAfterSuite() { + // @formatter:off + executeSuite(FailingBeforeAndAfterSuite.class) + .allEvents() + .assertStatistics(stats -> stats.started(2).finished(2).succeeded(1).failed(1)) + .assertThatEvents() + .haveExactly(1, event( + container(FailingBeforeAndAfterSuite.class), + finishedWithFailure(instanceOf(RuntimeException.class), + message("Exception thrown by @BeforeSuite method"), + suppressed(0, instanceOf(RuntimeException.class), + message("Exception thrown by @AfterSuite method"))))); + + assertThat(StatefulTestCase.callSequence).containsExactly( + "beforeSuiteMethod", + "afterSuiteMethod" + ); + // @formatter:on + } + + @Test + void severalFailingBeforeAndAfterSuite() { + // @formatter:off + executeSuite(SeveralFailingBeforeAndAfterSuite.class) + .allEvents() + .assertStatistics(stats -> stats.started(2).finished(2).succeeded(1).failed(1)) + .assertThatEvents() + .haveExactly(1, event( + container(SeveralFailingBeforeAndAfterSuite.class), + finishedWithFailure(instanceOf(RuntimeException.class), + message("Exception thrown by @BeforeSuite method"), + suppressed(0, instanceOf(RuntimeException.class), + message("Exception thrown by @AfterSuite method")), + suppressed(1, instanceOf(RuntimeException.class), + message("Exception thrown by @AfterSuite method"))))); + + assertThat(StatefulTestCase.callSequence).containsExactly( + "beforeSuiteMethod", + "afterSuiteMethod", + "afterSuiteMethod" + ); + // @formatter:on + } + + @ParameterizedTest(name = "{0}") + @MethodSource + void invalidBeforeOrAfterSuiteMethod(Class testSuiteClass, Predicate failureMessagePredicate) { + // @formatter:off + executeSuite(testSuiteClass) + .allEvents() + .assertThatEvents() + .haveExactly(1, event( + container(testSuiteClass), + finishedWithFailure(instanceOf(JUnitException.class), message(failureMessagePredicate)))); + // @formatter:on + } + + private static Stream invalidBeforeOrAfterSuiteMethod() { + return Stream.of( + invalidBeforeOrAfterSuiteCase(NonVoidBeforeSuite.class, "@BeforeSuite method", "must not return a value."), + invalidBeforeOrAfterSuiteCase(ParameterAcceptingBeforeSuite.class, "@BeforeSuite method", + "must not accept parameters."), + invalidBeforeOrAfterSuiteCase(NonStaticBeforeSuite.class, "@BeforeSuite method", "must be static."), + invalidBeforeOrAfterSuiteCase(PrivateBeforeSuite.class, "@BeforeSuite method", "must not be private."), + invalidBeforeOrAfterSuiteCase(NonVoidAfterSuite.class, "@AfterSuite method", "must not return a value."), + invalidBeforeOrAfterSuiteCase(ParameterAcceptingAfterSuite.class, "@AfterSuite method", + "must not accept parameters."), + invalidBeforeOrAfterSuiteCase(NonStaticAfterSuite.class, "@AfterSuite method", "must be static."), + invalidBeforeOrAfterSuiteCase(PrivateAfterSuite.class, "@AfterSuite method", "must not be private.")); + } + + private static Arguments invalidBeforeOrAfterSuiteCase(Class suiteClass, String failureMessageStart, + String failureMessageEnd) { + return arguments(named(suiteClass.getSimpleName(), suiteClass), + (Predicate) s -> s.startsWith(failureMessageStart) && s.endsWith(failureMessageEnd)); + } + + private static EngineExecutionResults executeSuite(Class suiteClass) { + return EngineTestKit.engine(ENGINE_ID).selectors(selectClass(suiteClass)).execute(); + } + +} diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/SuiteEngineTests.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/SuiteEngineTests.java index 040a71ef28a9..24d683c3397d 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/SuiteEngineTests.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/SuiteEngineTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -34,12 +34,14 @@ import org.junit.platform.launcher.PostDiscoveryFilter; import org.junit.platform.suite.api.SelectClasses; import org.junit.platform.suite.api.Suite; +import org.junit.platform.suite.engine.testcases.ConfigurationSensitiveTestCase; import org.junit.platform.suite.engine.testcases.DynamicTestsTestCase; import org.junit.platform.suite.engine.testcases.JUnit4TestsTestCase; import org.junit.platform.suite.engine.testcases.MultipleTestsTestCase; import org.junit.platform.suite.engine.testcases.SingleTestTestCase; import org.junit.platform.suite.engine.testcases.TaggedTestTestCase; import org.junit.platform.suite.engine.testsuites.AbstractSuite; +import org.junit.platform.suite.engine.testsuites.ConfigurationSuite; import org.junit.platform.suite.engine.testsuites.CyclicSuite; import org.junit.platform.suite.engine.testsuites.DynamicSuite; import org.junit.platform.suite.engine.testsuites.EmptyCyclicSuite; @@ -50,6 +52,7 @@ import org.junit.platform.suite.engine.testsuites.MultiEngineSuite; import org.junit.platform.suite.engine.testsuites.MultipleSuite; import org.junit.platform.suite.engine.testsuites.NestedSuite; +import org.junit.platform.suite.engine.testsuites.SelectByIdentifierSuite; import org.junit.platform.suite.engine.testsuites.SelectClassesSuite; import org.junit.platform.suite.engine.testsuites.SelectMethodsSuite; import org.junit.platform.suite.engine.testsuites.SuiteDisplayNameSuite; @@ -124,12 +127,6 @@ void privateSuiteIsNotExecuted() { // @formatter:on } - @Suite - @SelectClasses(SingleTestTestCase.class) - private static class PrivateSuite { - - } - @Test void innerSuiteIsNotExecuted() { // @formatter:off @@ -142,13 +139,6 @@ void innerSuiteIsNotExecuted() { // @formatter:on } - @SuppressWarnings("InnerClassMayBeStatic") - @Suite - @SelectClasses(names = "org.junit.platform.suite.engine.testcases.SingleTestTestCase") - private class InnerSuite { - - } - @Test void nestedSuiteIsNotExecuted() { // @formatter:off @@ -285,6 +275,34 @@ void selectMethodsInTestPlanByUniqueId() { // @formatter:on } + @Test + void selectConfigurationSensitiveMethodsInTestPlanByUniqueId() { + // @formatter:off + var uniqueId1 = UniqueId.forEngine(ENGINE_ID) + .append(SuiteTestDescriptor.SEGMENT_TYPE, ConfigurationSuite.class.getName()) + .append("engine", JupiterEngineDescriptor.ENGINE_ID) + .append(ClassTestDescriptor.SEGMENT_TYPE, ConfigurationSensitiveTestCase.class.getName()) + .append(TestMethodTestDescriptor.SEGMENT_TYPE, "test1()"); + + var uniqueId2 = UniqueId.forEngine(ENGINE_ID) + .append(SuiteTestDescriptor.SEGMENT_TYPE, ConfigurationSuite.class.getName()) + .append("engine", JupiterEngineDescriptor.ENGINE_ID) + .append(ClassTestDescriptor.SEGMENT_TYPE, ConfigurationSensitiveTestCase.class.getName()) + .append(TestMethodTestDescriptor.SEGMENT_TYPE, "test2()"); + + EngineTestKit.engine(ENGINE_ID) + .selectors( + selectUniqueId(uniqueId1), + selectUniqueId(uniqueId2) + ) + .execute() + .testEvents() + .assertThatEvents() + .haveExactly(1, event(test(ConfigurationSuite.class.getName(), "test1()"), finishedSuccessfully())) + .haveExactly(1, event(test(ConfigurationSuite.class.getName(), "test2()"), finishedSuccessfully())); + // @formatter:on + } + @Test void postDiscoveryCanRemoveTestDescriptorsInSuite() { // @formatter:off @@ -406,4 +424,27 @@ void threePartCyclicSuite() { // @formatter:on } + @Test + void selectByIdentifier() { + // @formatter:off + EngineTestKit.engine(ENGINE_ID) + .selectors(selectClass(SelectByIdentifierSuite.class)) + .execute() + .testEvents() + .assertThatEvents() + .haveExactly(1, event(test(SelectByIdentifierSuite.class.getName()), finishedSuccessfully())) + .haveExactly(1, event(test(SingleTestTestCase.class.getName()), finishedSuccessfully())); + // @formatter:on + } + + @Suite + @SelectClasses(SingleTestTestCase.class) + private static class PrivateSuite { + } + + @Suite + @SelectClasses(names = "org.junit.platform.suite.engine.testcases.SingleTestTestCase") + private class InnerSuite { + } + } diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/SuiteTestDescriptorTests.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/SuiteTestDescriptorTests.java index 627de28d8de6..02ad36b35029 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/SuiteTestDescriptorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/SuiteTestDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,12 +10,9 @@ package org.junit.platform.suite.engine; -import static java.util.Collections.emptySet; -import static java.util.stream.Collectors.toSet; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collections; import java.util.Optional; @@ -37,60 +34,63 @@ * @since 1.8 */ class SuiteTestDescriptorTests { - @Suite - static class TestSuite { - } - UniqueId engineId = UniqueId.forEngine(SuiteEngineDescriptor.ENGINE_ID); - UniqueId suiteId = engineId.append(SuiteTestDescriptor.SEGMENT_TYPE, "test"); - UniqueId jupiterEngineId = suiteId.append("engine", JupiterEngineDescriptor.ENGINE_ID); - UniqueId testClassId = jupiterEngineId.append(ClassTestDescriptor.SEGMENT_TYPE, SingleTestTestCase.class.getName()); - UniqueId methodId = testClassId.append(TestMethodTestDescriptor.SEGMENT_TYPE, "test()"); + final UniqueId engineId = UniqueId.forEngine(SuiteEngineDescriptor.ENGINE_ID); + final UniqueId suiteId = engineId.append(SuiteTestDescriptor.SEGMENT_TYPE, "test"); + final UniqueId jupiterEngineId = suiteId.append("engine", JupiterEngineDescriptor.ENGINE_ID); + final UniqueId testClassId = jupiterEngineId.append(ClassTestDescriptor.SEGMENT_TYPE, + SingleTestTestCase.class.getName()); + final UniqueId methodId = testClassId.append(TestMethodTestDescriptor.SEGMENT_TYPE, "test()"); - ConfigurationParameters configurationParameters = new EmptyConfigurationParameters(); - SuiteTestDescriptor suite = new SuiteTestDescriptor(suiteId, TestSuite.class, configurationParameters); + final ConfigurationParameters configurationParameters = new EmptyConfigurationParameters(); + final SuiteTestDescriptor suite = new SuiteTestDescriptor(suiteId, TestSuite.class, configurationParameters); @Test void suiteIsEmptyBeforeDiscovery() { suite.addDiscoveryRequestFrom(SelectClassesSuite.class); - assertEquals(emptySet(), suite.getChildren()); + + assertThat(suite.getChildren()).isEmpty(); } @Test void suiteDiscoversTestsFromClass() { suite.addDiscoveryRequestFrom(SelectClassesSuite.class); suite.discover(); - assertEquals(Set.of(jupiterEngineId, testClassId, methodId), - suite.getDescendants().stream().map(TestDescriptor::getUniqueId).collect(toSet())); + + assertThat(suite.getDescendants()).map(TestDescriptor::getUniqueId)// + .containsExactly(jupiterEngineId, testClassId, methodId); } @Test void suitDiscoversTestsFromUniqueId() { suite.addDiscoveryRequestFrom(methodId); suite.discover(); - assertEquals(Set.of(jupiterEngineId, testClassId, methodId), - suite.getDescendants().stream().map(TestDescriptor::getUniqueId).collect(toSet())); + + assertThat(suite.getDescendants()).map(TestDescriptor::getUniqueId)// + .containsExactly(jupiterEngineId, testClassId, methodId); } @Test void discoveryPlanCanNotBeModifiedAfterDiscovery() { suite.addDiscoveryRequestFrom(SelectClassesSuite.class); suite.discover(); - assertAll(() -> { - PreconditionViolationException exception = assertThrows(PreconditionViolationException.class, - () -> suite.addDiscoveryRequestFrom(SelectClassesSuite.class)); - assertEquals("discovery request can not be modified after discovery", exception.getMessage()); - - }, () -> { - PreconditionViolationException exception = assertThrows(PreconditionViolationException.class, - () -> suite.addDiscoveryRequestFrom(methodId)); - assertEquals("discovery request can not be modified after discovery", exception.getMessage()); - }); + + assertAll(// + () -> assertThatExceptionOfType(PreconditionViolationException.class)// + .isThrownBy(() -> suite.addDiscoveryRequestFrom(SelectClassesSuite.class))// + .withMessage("discovery request cannot be modified after discovery"), + () -> assertThatExceptionOfType(PreconditionViolationException.class)// + .isThrownBy(() -> suite.addDiscoveryRequestFrom(methodId))// + .withMessage("discovery request cannot be modified after discovery")); } @Test void suiteMayRegisterTests() { - assertTrue(suite.mayRegisterTests()); + assertThat(suite.mayRegisterTests()).isTrue(); + } + + @Suite + static class TestSuite { } private static class EmptyConfigurationParameters implements ConfigurationParameters { diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/ConfigurationSensitiveTestCase.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/ConfigurationSensitiveTestCase.java new file mode 100644 index 000000000000..1219e8c16251 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/ConfigurationSensitiveTestCase.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.engine.testcases; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.MethodOrderer.MethodName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @since 1.11 + */ +@TestMethodOrder(MethodName.class) +public class ConfigurationSensitiveTestCase { + + boolean shared; + + @Test + void test1() { + shared = true; + } + + @Test + void test2() { + // This will fail unless the test instance lifecycle is set to per_class, + // which is configured via @ConfigurationParameter in ConfigurationSuite. + assertTrue(shared); + } + +} diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/DynamicTestsTestCase.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/DynamicTestsTestCase.java index 7972eee2d96f..4d470c5b86e6 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/DynamicTestsTestCase.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/DynamicTestsTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/EmptyDynamicTestsTestCase.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/EmptyDynamicTestsTestCase.java index be17ec51e4f0..5ca4c08b960f 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/EmptyDynamicTestsTestCase.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/EmptyDynamicTestsTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/EmptyTestTestCase.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/EmptyTestTestCase.java index 1251644af9ca..8b43ab0d8b0b 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/EmptyTestTestCase.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/EmptyTestTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/JUnit4TestsTestCase.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/JUnit4TestsTestCase.java index 208d48bb30a5..42348afa6dbd 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/JUnit4TestsTestCase.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/JUnit4TestsTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/MultipleTestsTestCase.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/MultipleTestsTestCase.java index 41ddbbbb4c63..4a0b5a2a7813 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/MultipleTestsTestCase.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/MultipleTestsTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/SingleTestTestCase.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/SingleTestTestCase.java index bb6116ebd210..fb0c0230e18d 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/SingleTestTestCase.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/SingleTestTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/StatefulTestCase.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/StatefulTestCase.java new file mode 100644 index 000000000000..3a2a32230c27 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/StatefulTestCase.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.engine.testcases; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +/** + * @since 1.11 + */ +public class StatefulTestCase { + + public static List callSequence = new ArrayList<>(); + + public static class Test1 { + + @Test + void statefulTest() { + callSequence.add("test1"); + } + + } + + public static class Test2 { + + @Test + void statefulTest() { + callSequence.add("test2"); + fail("This is a failing test"); + } + + } + +} diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/TaggedTestTestCase.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/TaggedTestTestCase.java index 0ecc4d9c347f..5e726a4e8d91 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/TaggedTestTestCase.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/TaggedTestTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/AbstractSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/AbstractSuite.java index c648e904fba7..b99d11bd7471 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/AbstractSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/AbstractSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/ConfigurationSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/ConfigurationSuite.java new file mode 100644 index 000000000000..8e7f6cba21b2 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/ConfigurationSuite.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.engine.testsuites; + +import static org.junit.jupiter.engine.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME; + +import org.junit.platform.suite.api.ConfigurationParameter; +import org.junit.platform.suite.api.SelectClasses; +import org.junit.platform.suite.api.Suite; +import org.junit.platform.suite.engine.testcases.ConfigurationSensitiveTestCase; + +@Suite +@ConfigurationParameter(key = DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME, value = "per_class") +@SelectClasses(ConfigurationSensitiveTestCase.class) +public class ConfigurationSuite { +} diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/CyclicSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/CyclicSuite.java index df2636d58b84..07274993fd56 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/CyclicSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/CyclicSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/DynamicSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/DynamicSuite.java index 525498e44714..37490756e8b9 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/DynamicSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/DynamicSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyCyclicSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyCyclicSuite.java index 38554f36eedf..05d8d4518aa8 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyCyclicSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyCyclicSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyDynamicTestSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyDynamicTestSuite.java index b57904850597..6e547b19d6fb 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyDynamicTestSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyDynamicTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyDynamicTestWithFailIfNoTestFalseSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyDynamicTestWithFailIfNoTestFalseSuite.java index d1f87c65d968..619c98e2a9c3 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyDynamicTestWithFailIfNoTestFalseSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyDynamicTestWithFailIfNoTestFalseSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyTestCaseSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyTestCaseSuite.java index 55b72d396344..8bb833394339 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyTestCaseSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyTestCaseSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyTestCaseWithFailIfNoTestFalseSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyTestCaseWithFailIfNoTestFalseSuite.java index f1d5bf564409..388dec7317c8 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyTestCaseWithFailIfNoTestFalseSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyTestCaseWithFailIfNoTestFalseSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/LifecycleMethodsSuites.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/LifecycleMethodsSuites.java new file mode 100644 index 000000000000..8a5af3587d38 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/LifecycleMethodsSuites.java @@ -0,0 +1,245 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.engine.testsuites; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.platform.suite.api.AfterSuite; +import org.junit.platform.suite.api.BeforeSuite; +import org.junit.platform.suite.api.SelectClasses; +import org.junit.platform.suite.api.Suite; +import org.junit.platform.suite.engine.BeforeAndAfterSuiteTests; +import org.junit.platform.suite.engine.testcases.StatefulTestCase; + +/** + * Test suites used in {@link BeforeAndAfterSuiteTests}. + * + * @since 1.11 + */ +public class LifecycleMethodsSuites { + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + @Suite + @SelectClasses({ StatefulTestCase.Test1.class, StatefulTestCase.Test2.class }) + private @interface TestSuite { + } + + @TestSuite + public static class SuccessfulBeforeAndAfterSuite { + + @BeforeSuite + static void setUp() { + StatefulTestCase.callSequence.add("beforeSuiteMethod"); + } + + @AfterSuite + static void tearDown() { + StatefulTestCase.callSequence.add("afterSuiteMethod"); + } + + } + + @TestSuite + public static class FailingBeforeSuite { + + @BeforeSuite + static void setUp() { + StatefulTestCase.callSequence.add("beforeSuiteMethod"); + throw new RuntimeException("Exception thrown by @BeforeSuite method"); + } + + @AfterSuite + static void tearDown() { + StatefulTestCase.callSequence.add("afterSuiteMethod"); + } + + } + + @TestSuite + public static class FailingAfterSuite { + + @BeforeSuite + static void setUp() { + StatefulTestCase.callSequence.add("beforeSuiteMethod"); + } + + @AfterSuite + static void tearDown() { + StatefulTestCase.callSequence.add("afterSuiteMethod"); + throw new RuntimeException("Exception thrown by @AfterSuite method"); + } + + } + + @TestSuite + public static class FailingBeforeAndAfterSuite { + + @BeforeSuite + static void setUp() { + StatefulTestCase.callSequence.add("beforeSuiteMethod"); + throw new RuntimeException("Exception thrown by @BeforeSuite method"); + } + + @AfterSuite + static void tearDown() { + StatefulTestCase.callSequence.add("afterSuiteMethod"); + throw new RuntimeException("Exception thrown by @AfterSuite method"); + } + + } + + @TestSuite + public static class SeveralFailingBeforeAndAfterSuite { + + @BeforeSuite + static void setUp1() { + StatefulTestCase.callSequence.add("beforeSuiteMethod"); + throw new RuntimeException("Exception thrown by @BeforeSuite method"); + } + + @BeforeSuite + static void setUp2() { + StatefulTestCase.callSequence.add("beforeSuiteMethod"); + throw new RuntimeException("Exception thrown by @BeforeSuite method"); + } + + @AfterSuite + static void tearDown1() { + StatefulTestCase.callSequence.add("afterSuiteMethod"); + throw new RuntimeException("Exception thrown by @AfterSuite method"); + } + + @AfterSuite + static void tearDown2() { + StatefulTestCase.callSequence.add("afterSuiteMethod"); + throw new RuntimeException("Exception thrown by @AfterSuite method"); + } + + } + + @TestSuite + public static class SuperclassWithBeforeAndAfterSuite { + + @BeforeSuite + static void setUp() { + StatefulTestCase.callSequence.add("superclassBeforeSuiteMethod"); + } + + @AfterSuite + static void tearDown() { + StatefulTestCase.callSequence.add("superclassAfterSuiteMethod"); + } + + } + + public static class SubclassWithBeforeAndAfterSuite extends SuperclassWithBeforeAndAfterSuite { + + @BeforeSuite + static void setUp() { + StatefulTestCase.callSequence.add("subclassBeforeSuiteMethod"); + } + + @AfterSuite + static void tearDown() { + StatefulTestCase.callSequence.add("subclassAfterSuiteMethod"); + } + + } + + @TestSuite + public static class NonVoidBeforeSuite { + + @BeforeSuite + static String nonVoidBeforeSuite() { + fail("Should not be called"); + return ""; + } + + } + + @TestSuite + public static class ParameterAcceptingBeforeSuite { + + @BeforeSuite + static void parameterAcceptingBeforeSuite(String param) { + fail("Should not be called"); + } + + } + + @TestSuite + public static class NonStaticBeforeSuite { + + @BeforeSuite + void nonStaticBeforeSuite() { + fail("Should not be called"); + } + + } + + @TestSuite + public static class PrivateBeforeSuite { + + @BeforeSuite + private static void privateBeforeSuite() { + fail("Should not be called"); + } + + } + + @TestSuite + public static class NonVoidAfterSuite { + + @AfterSuite + static String nonVoidAfterSuite() { + fail("Should not be called"); + return ""; + } + + } + + @TestSuite + public static class ParameterAcceptingAfterSuite { + + @AfterSuite + static void parameterAcceptingAfterSuite(String param) { + fail("Should not be called"); + } + + } + + @TestSuite + public static class NonStaticAfterSuite { + + @AfterSuite + void nonStaticAfterSuite() { + fail("Should not be called"); + } + + } + + @TestSuite + public static class PrivateAfterSuite { + + @AfterSuite + private static void privateAfterSuite() { + fail("Should not be called"); + } + + } + +} diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/MultiEngineSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/MultiEngineSuite.java index ac12777b88fa..0546e6df7213 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/MultiEngineSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/MultiEngineSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/MultipleSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/MultipleSuite.java index c05368ddf20e..1570efccb1fa 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/MultipleSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/MultipleSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/NestedSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/NestedSuite.java index 7d07a8661f27..8f15386fb018 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/NestedSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/NestedSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectByIdentifierSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectByIdentifierSuite.java new file mode 100644 index 000000000000..5aad2b329d60 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectByIdentifierSuite.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.suite.engine.testsuites; + +import org.junit.platform.suite.api.IncludeClassNamePatterns; +import org.junit.platform.suite.api.Select; +import org.junit.platform.suite.api.Suite; + +/** + * @since 1.11 + */ +@Suite +@IncludeClassNamePatterns(".*") +@Select("class:org.junit.platform.suite.engine.testcases.SingleTestTestCase") +public class SelectByIdentifierSuite { +} diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectClassesSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectClassesSuite.java index 1d9df39efbaf..a2fac4e257e3 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectClassesSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectClassesSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectMethodsSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectMethodsSuite.java index 49184c267e23..c04b0869ee06 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectMethodsSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectMethodsSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SuiteDisplayNameSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SuiteDisplayNameSuite.java index 436e822d9c9a..baa6db4f6476 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SuiteDisplayNameSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SuiteDisplayNameSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SuiteSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SuiteSuite.java index 205c71c8fce3..282092b8f842 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SuiteSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SuiteSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/ThreePartCyclicSuite.java b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/ThreePartCyclicSuite.java index febbba584991..133330816eb5 100644 --- a/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/ThreePartCyclicSuite.java +++ b/platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/ThreePartCyclicSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/testkit/engine/EngineTestKitTests.java b/platform-tests/src/test/java/org/junit/platform/testkit/engine/EngineTestKitTests.java index 61c18d5e773c..20d71f0c3def 100644 --- a/platform-tests/src/test/java/org/junit/platform/testkit/engine/EngineTestKitTests.java +++ b/platform-tests/src/test/java/org/junit/platform/testkit/engine/EngineTestKitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/testkit/engine/EventsTests.java b/platform-tests/src/test/java/org/junit/platform/testkit/engine/EventsTests.java index aeda1baadbf9..a6133808f8a2 100644 --- a/platform-tests/src/test/java/org/junit/platform/testkit/engine/EventsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/testkit/engine/EventsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/testkit/engine/ExecutionsIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/testkit/engine/ExecutionsIntegrationTests.java index ea609b601039..dcfe20f74881 100644 --- a/platform-tests/src/test/java/org/junit/platform/testkit/engine/ExecutionsIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/testkit/engine/ExecutionsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/testkit/engine/NestedContainerEventConditionTests.java b/platform-tests/src/test/java/org/junit/platform/testkit/engine/NestedContainerEventConditionTests.java index c16b2e264459..e7faa286c983 100644 --- a/platform-tests/src/test/java/org/junit/platform/testkit/engine/NestedContainerEventConditionTests.java +++ b/platform-tests/src/test/java/org/junit/platform/testkit/engine/NestedContainerEventConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tests/src/test/java/org/junit/platform/testkit/engine/TestExecutionResultConditionsTests.java b/platform-tests/src/test/java/org/junit/platform/testkit/engine/TestExecutionResultConditionsTests.java new file mode 100644 index 000000000000..9ea2079aedbf --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/testkit/engine/TestExecutionResultConditionsTests.java @@ -0,0 +1,123 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.testkit.engine; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message; +import static org.junit.platform.testkit.engine.TestExecutionResultConditions.rootCause; + +import org.assertj.core.api.Condition; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.PreconditionViolationException; + +/** + * Tests for {@link TestExecutionResultConditions}. + * + * @since 1.11 + */ +class TestExecutionResultConditionsTests { + + private static final String EXPECTED = "expected"; + + private static final String UNEXPECTED = "unexpected"; + + private static final Condition rootCauseCondition = rootCause(message(EXPECTED)); + + @Test + void rootCauseFailsForNullThrowable() { + assertThatExceptionOfType(PreconditionViolationException.class)// + .isThrownBy(() -> rootCauseCondition.matches(null))// + .withMessage("Throwable must not be null"); + } + + @Test + void rootCauseFailsForThrowableWithoutCause() { + Throwable throwable = new Throwable(); + + assertThatExceptionOfType(PreconditionViolationException.class)// + .isThrownBy(() -> rootCauseCondition.matches(throwable))// + .withMessage("Throwable does not have a cause"); + } + + @Test + void rootCauseMatchesForDirectCauseWithExpectedMessage() { + RuntimeException cause = new RuntimeException(EXPECTED); + Throwable throwable = new Throwable(cause); + + assertThat(rootCauseCondition.matches(throwable)).isTrue(); + } + + @Test + void rootCauseDoesNotMatchForDirectCauseWithDifferentMessage() { + RuntimeException cause = new RuntimeException(UNEXPECTED); + Throwable throwable = new Throwable(cause); + + assertThat(rootCauseCondition.matches(throwable)).isFalse(); + } + + @Test + void rootCauseMatchesForRootCauseWithExpectedMessage() { + RuntimeException rootCause = new RuntimeException(EXPECTED); + RuntimeException intermediateCause = new RuntimeException("intermediate cause", rootCause); + Throwable throwable = new Throwable(intermediateCause); + + assertThat(rootCauseCondition.matches(throwable)).isTrue(); + } + + @Test + void rootCauseDoesNotMatchForRootCauseWithDifferentMessage() { + RuntimeException rootCause = new RuntimeException(UNEXPECTED); + RuntimeException intermediateCause = new RuntimeException("intermediate cause", rootCause); + Throwable throwable = new Throwable(intermediateCause); + + assertThat(rootCauseCondition.matches(throwable)).isFalse(); + } + + @Test + void rootCauseMatchesForRootCauseWithExpectedMessageAndSingleLevelRecursiveCauseChain() { + RuntimeException rootCause = new RuntimeException(EXPECTED); + Throwable throwable = new Throwable(rootCause); + rootCause.initCause(throwable); + + assertThat(rootCauseCondition.matches(throwable)).isTrue(); + } + + @Test + void rootCauseDoesNotMatchForRootCauseWithDifferentMessageAndSingleLevelRecursiveCauseChain() { + RuntimeException rootCause = new RuntimeException(UNEXPECTED); + Throwable throwable = new Throwable(rootCause); + rootCause.initCause(throwable); + + assertThat(rootCauseCondition.matches(throwable)).isFalse(); + } + + @Test + void rootCauseMatchesForRootCauseWithExpectedMessageAndDoubleLevelRecursiveCauseChain() { + RuntimeException rootCause = new RuntimeException(EXPECTED); + Exception intermediateCause = new Exception("intermediate cause", rootCause); + Throwable throwable = new Throwable(intermediateCause); + rootCause.initCause(throwable); + + assertThat(rootCauseCondition.matches(throwable)).isTrue(); + } + + @Test + void rootCauseDoesNotMatchForRootCauseWithDifferentMessageAndDoubleLevelRecursiveCauseChain() { + RuntimeException rootCause = new RuntimeException(UNEXPECTED); + Exception intermediateCause = new Exception("intermediate cause", rootCause); + Throwable throwable = new Throwable(intermediateCause); + rootCause.initCause(throwable); + + assertThat(rootCauseCondition.matches(throwable)).isFalse(); + } + +} diff --git a/platform-tests/src/test/resources/config-test-override.properties b/platform-tests/src/test/resources/config-test-override.properties new file mode 100644 index 000000000000..302230b0d3b5 --- /dev/null +++ b/platform-tests/src/test/resources/config-test-override.properties @@ -0,0 +1,2 @@ +# Used in tests, don't delete me +com.example.prop.first=first value from override file diff --git a/platform-tests/src/test/resources/config-test.properties b/platform-tests/src/test/resources/config-test.properties new file mode 100644 index 000000000000..10f838f11b0e --- /dev/null +++ b/platform-tests/src/test/resources/config-test.properties @@ -0,0 +1,4 @@ +# Used in tests, don't delete me +com.example.prop.first=first value +com.example.prop.second=second value +com.example.prop.third=third value diff --git a/platform-tests/src/test/resources/default-package.resource b/platform-tests/src/test/resources/default-package.resource new file mode 100644 index 000000000000..3f8177ae5d6c --- /dev/null +++ b/platform-tests/src/test/resources/default-package.resource @@ -0,0 +1 @@ +This file was unintentionally left blank. diff --git a/platform-tests/src/test/resources/folder with spaces/jar test with spaces.jar b/platform-tests/src/test/resources/folder with spaces/jar test with spaces.jar index d470846c8745..02f12e1b9d45 100644 Binary files a/platform-tests/src/test/resources/folder with spaces/jar test with spaces.jar and b/platform-tests/src/test/resources/folder with spaces/jar test with spaces.jar differ diff --git a/platform-tests/src/test/resources/jartest-shadowed.jar b/platform-tests/src/test/resources/jartest-shadowed.jar new file mode 100644 index 000000000000..8a13258b5759 Binary files /dev/null and b/platform-tests/src/test/resources/jartest-shadowed.jar differ diff --git a/platform-tests/src/test/resources/jartest.jar b/platform-tests/src/test/resources/jartest.jar index d470846c8745..02f12e1b9d45 100644 Binary files a/platform-tests/src/test/resources/jartest.jar and b/platform-tests/src/test/resources/jartest.jar differ diff --git a/platform-tests/src/test/resources/org/junit/platform/commons/example.resource b/platform-tests/src/test/resources/org/junit/platform/commons/example.resource new file mode 100644 index 000000000000..3f8177ae5d6c --- /dev/null +++ b/platform-tests/src/test/resources/org/junit/platform/commons/example.resource @@ -0,0 +1 @@ +This file was unintentionally left blank. diff --git a/platform-tests/src/test/resources/org/junit/platform/commons/other-example.resource b/platform-tests/src/test/resources/org/junit/platform/commons/other-example.resource new file mode 100644 index 000000000000..3f8177ae5d6c --- /dev/null +++ b/platform-tests/src/test/resources/org/junit/platform/commons/other-example.resource @@ -0,0 +1 @@ +This file was unintentionally left blank. diff --git a/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts b/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts index 28f5d7d69d49..9b7d6c805f1b 100644 --- a/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts +++ b/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts @@ -1,7 +1,8 @@ -import com.gradle.enterprise.gradleplugin.testdistribution.internal.TestDistributionExtensionInternal +import com.gradle.develocity.agent.gradle.internal.test.TestDistributionConfigurationInternal +import junitbuild.extensions.capitalized import org.gradle.api.tasks.PathSensitivity.RELATIVE -import org.gradle.configurationcache.extensions.capitalized import org.gradle.jvm.toolchain.internal.NoToolchainAvailableException +import org.gradle.kotlin.dsl.support.listFilesOrdered import java.time.Duration plugins { @@ -28,9 +29,18 @@ spotless { } } -val thirdPartyJars by configurations.creatingResolvable -val antJars by configurations.creatingResolvable -val mavenDistribution by configurations.creatingResolvable +val thirdPartyJars = configurations.dependencyScope("thirdPartyJars") +val thirdPartyJarsClasspath = configurations.resolvable("thirdPartyJarsClasspath") { + extendsFrom(thirdPartyJars.get()) +} +val antJars = configurations.dependencyScope("antJars") +val antJarsClasspath = configurations.resolvable("antJarsClasspath") { + extendsFrom(antJars.get()) +} +val mavenDistribution = configurations.dependencyScope("mavenDistribution") +val mavenDistributionClasspath = configurations.resolvable("mavenDistributionClasspath") { + extendsFrom(mavenDistribution.get()) +} dependencies { implementation(libs.bartholdy) { @@ -59,6 +69,7 @@ dependencies { because("we reference Ant's main class") } testImplementation(libs.bundles.xmlunit) + testImplementation(testFixtures(projects.junitJupiterApi)) thirdPartyJars(libs.junit4) thirdPartyJars(libs.assertj) @@ -82,9 +93,11 @@ dependencies { } } +val mavenDistributionDir = layout.buildDirectory.dir("maven-distribution") + val unzipMavenDistribution by tasks.registering(Sync::class) { - from(zipTree(mavenDistribution.elements.map { it.single() })) - into(layout.buildDirectory.dir("maven-distribution")) + from(zipTree(mavenDistributionClasspath.flatMap { d -> d.elements.map { e -> e.single() } })) + into(mavenDistributionDir) } val normalizeMavenRepo by tasks.registering(Sync::class) { @@ -125,9 +138,9 @@ tasks.test { jvmArgumentProviders += MavenRepo(project, normalizeMavenRepo.map { it.destinationDir }) } - jvmArgumentProviders += JarPath(project, thirdPartyJars) - jvmArgumentProviders += JarPath(project, antJars) - jvmArgumentProviders += MavenDistribution(project, unzipMavenDistribution) + jvmArgumentProviders += JarPath(project, thirdPartyJarsClasspath.get(), "thirdPartyJars") + jvmArgumentProviders += JarPath(project, antJarsClasspath.get(), "antJars") + jvmArgumentProviders += MavenDistribution(project, unzipMavenDistribution, mavenDistributionDir) (options as JUnitPlatformOptions).apply { includeEngines("archunit") @@ -144,12 +157,14 @@ tasks.test { dir("${rootDir}/documentation/src/test").withPathSensitivity(RELATIVE) } - distribution { - requirements.add("jdk=8") - this as TestDistributionExtensionInternal - preferredMaxDuration = Duration.ofMillis(500) + develocity { + testDistribution { + requirements.add("jdk=8") + this as TestDistributionConfigurationInternal + preferredMaxDuration = Duration.ofMillis(500) + } } - jvmArgumentProviders += JavaHomeDir(project, 8, distribution.enabled) + jvmArgumentProviders += JavaHomeDir(project, 8, develocity.testDistribution.enabled) } class MavenRepo(project: Project, @get:Internal val repoDir: Provider) : CommandLineArgumentProvider { @@ -206,11 +221,11 @@ class JarPath(project: Project, configuration: Configuration, @Input val key: St override fun asArguments() = listOf("-D${key}=${files.asPath}") } -class MavenDistribution(project: Project, sourceTask: TaskProvider<*>) : CommandLineArgumentProvider { +class MavenDistribution(project: Project, sourceTask: TaskProvider<*>, distributionDir: Provider) : CommandLineArgumentProvider { @InputDirectory @PathSensitive(RELATIVE) val mavenDistribution: DirectoryProperty = project.objects.directoryProperty() - .value(project.layout.dir(sourceTask.map { it.outputs.files.singleFile.listFiles()!!.single() })) + .fileProvider(project.files(distributionDir).builtBy(sourceTask).elements.map { it.single().asFile.listFilesOrdered().single() }) override fun asArguments() = listOf("-DmavenDistribution=${mavenDistribution.get().asFile.absolutePath}") } diff --git a/platform-tooling-support-tests/projects/ant-starter/src/main/java/com/example/project/Calculator.java b/platform-tooling-support-tests/projects/ant-starter/src/main/java/com/example/project/Calculator.java index 6feca6bfc04b..c0540f6b3e95 100644 --- a/platform-tooling-support-tests/projects/ant-starter/src/main/java/com/example/project/Calculator.java +++ b/platform-tooling-support-tests/projects/ant-starter/src/main/java/com/example/project/Calculator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/ant-starter/src/test/java/com/example/project/CalculatorTests.java b/platform-tooling-support-tests/projects/ant-starter/src/test/java/com/example/project/CalculatorTests.java index 695282baccc8..0b0a25ab7fdc 100644 --- a/platform-tooling-support-tests/projects/ant-starter/src/test/java/com/example/project/CalculatorTests.java +++ b/platform-tooling-support-tests/projects/ant-starter/src/test/java/com/example/project/CalculatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/graalvm-starter/build.gradle.kts b/platform-tooling-support-tests/projects/graalvm-starter/build.gradle.kts index 26f76f8b874a..c83d5d840ada 100644 --- a/platform-tooling-support-tests/projects/graalvm-starter/build.gradle.kts +++ b/platform-tooling-support-tests/projects/graalvm-starter/build.gradle.kts @@ -31,8 +31,6 @@ tasks.test { graalvmNative { binaries { named("test") { - buildArgs.add("--initialize-at-build-time=org.junit.platform.launcher.core.LauncherConfig") - buildArgs.add("--initialize-at-build-time=org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter") buildArgs.add("-H:+ReportExceptionStackTraces") } } diff --git a/platform-tooling-support-tests/projects/graalvm-starter/gradle.properties b/platform-tooling-support-tests/projects/graalvm-starter/gradle.properties deleted file mode 100644 index 83d0ce01114f..000000000000 --- a/platform-tooling-support-tests/projects/graalvm-starter/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -org.gradle.java.installations.fromEnv=GRAALVM_HOME diff --git a/platform-tooling-support-tests/projects/graalvm-starter/settings.gradle.kts b/platform-tooling-support-tests/projects/graalvm-starter/settings.gradle.kts index c928064e495a..1399f18e3201 100644 --- a/platform-tooling-support-tests/projects/graalvm-starter/settings.gradle.kts +++ b/platform-tooling-support-tests/projects/graalvm-starter/settings.gradle.kts @@ -1,6 +1,6 @@ pluginManagement { plugins { - id("org.graalvm.buildtools.native") version "0.9.13" + id("org.graalvm.buildtools.native") version "0.10.1" } repositories { mavenCentral() diff --git a/platform-tooling-support-tests/projects/graalvm-starter/src/main/java/com/example/project/Calculator.java b/platform-tooling-support-tests/projects/graalvm-starter/src/main/java/com/example/project/Calculator.java index 6feca6bfc04b..c0540f6b3e95 100644 --- a/platform-tooling-support-tests/projects/graalvm-starter/src/main/java/com/example/project/Calculator.java +++ b/platform-tooling-support-tests/projects/graalvm-starter/src/main/java/com/example/project/Calculator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/CalculatorTests.java b/platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/CalculatorTests.java index 695282baccc8..0b0a25ab7fdc 100644 --- a/platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/CalculatorTests.java +++ b/platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/CalculatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/ClassLevelAnnotationTests.java b/platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/ClassLevelAnnotationTests.java new file mode 100644 index 000000000000..582b46f7cde1 --- /dev/null +++ b/platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/ClassLevelAnnotationTests.java @@ -0,0 +1,25 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package com.example.project; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledInNativeImage; + +@EnabledInNativeImage +class ClassLevelAnnotationTests { + @Nested + class Inner { + @Test + void test() { + } + } +} diff --git a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/build.gradle.kts b/platform-tooling-support-tests/projects/gradle-kotlin-extensions/build.gradle.kts index c79b5ff77427..51a6ffd269ad 100644 --- a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/build.gradle.kts +++ b/platform-tooling-support-tests/projects/gradle-kotlin-extensions/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "1.2.71" + kotlin("jvm") version "1.9.0" } repositories { @@ -20,8 +20,8 @@ dependencies { tasks.withType().configureEach { kotlinOptions { jvmTarget = "1.8" - apiVersion = "1.1" - languageVersion = "1.1" + apiVersion = "1.6" + languageVersion = "1.6" } } diff --git a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradle/wrapper/gradle-wrapper.jar b/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 5c2d1cf016b3..000000000000 Binary files a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradle/wrapper/gradle-wrapper.properties b/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 5028f28f8e47..000000000000 --- a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradlew b/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradlew deleted file mode 100755 index 83f2acfdc319..000000000000 --- a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradlew +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradlew.bat b/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradlew.bat deleted file mode 100644 index 9618d8d9607c..000000000000 --- a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradlew.bat +++ /dev/null @@ -1,100 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/src/test/kotlin/com/example/project/ExtensionFunctionsTests.kt b/platform-tooling-support-tests/projects/gradle-kotlin-extensions/src/test/kotlin/com/example/project/ExtensionFunctionsTests.kt index 0de75b1f9116..d60205669fc3 100644 --- a/platform-tooling-support-tests/projects/gradle-kotlin-extensions/src/test/kotlin/com/example/project/ExtensionFunctionsTests.kt +++ b/platform-tooling-support-tests/projects/gradle-kotlin-extensions/src/test/kotlin/com/example/project/ExtensionFunctionsTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -22,7 +22,6 @@ import org.junit.jupiter.params.provider.CsvSource import org.opentest4j.AssertionFailedError class ExtensionFunctionsTests { - @Test fun `assertDoesNotThrow() and assertAll`() { assertDoesNotThrow { diff --git a/platform-tooling-support-tests/projects/gradle-missing-engine/src/test/java/FooTests.java b/platform-tooling-support-tests/projects/gradle-missing-engine/src/test/java/FooTests.java index 86d922c20f4f..97be110987fc 100644 --- a/platform-tooling-support-tests/projects/gradle-missing-engine/src/test/java/FooTests.java +++ b/platform-tooling-support-tests/projects/gradle-missing-engine/src/test/java/FooTests.java @@ -1,6 +1,6 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/gradle-starter/src/main/java/com/example/project/Calculator.java b/platform-tooling-support-tests/projects/gradle-starter/src/main/java/com/example/project/Calculator.java index 6feca6bfc04b..c0540f6b3e95 100644 --- a/platform-tooling-support-tests/projects/gradle-starter/src/main/java/com/example/project/Calculator.java +++ b/platform-tooling-support-tests/projects/gradle-starter/src/main/java/com/example/project/Calculator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/gradle-starter/src/test/java/com/example/project/CalculatorTests.java b/platform-tooling-support-tests/projects/gradle-starter/src/test/java/com/example/project/CalculatorTests.java index 695282baccc8..0b0a25ab7fdc 100644 --- a/platform-tooling-support-tests/projects/gradle-starter/src/test/java/com/example/project/CalculatorTests.java +++ b/platform-tooling-support-tests/projects/gradle-starter/src/test/java/com/example/project/CalculatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-jupiter-api.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-jupiter-api.expected.txt index 0b4810a6cc60..26719afdbe79 100644 --- a/platform-tooling-support-tests/projects/jar-describe-module/junit-jupiter-api.expected.txt +++ b/platform-tooling-support-tests/projects/jar-describe-module/junit-jupiter-api.expected.txt @@ -2,6 +2,7 @@ org.junit.jupiter.api@${jupiterVersion} jar:file:.+/junit-jupiter-api-\d.+\.jar. exports org.junit.jupiter.api exports org.junit.jupiter.api.condition exports org.junit.jupiter.api.extension +exports org.junit.jupiter.api.extension.support exports org.junit.jupiter.api.function exports org.junit.jupiter.api.io exports org.junit.jupiter.api.parallel diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-commons.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-commons.expected.txt index 5c0c9b44e4ef..cb3eb72ae947 100644 --- a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-commons.expected.txt +++ b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-commons.expected.txt @@ -3,6 +3,7 @@ exports org.junit.platform.commons exports org.junit.platform.commons.annotation exports org.junit.platform.commons.function exports org.junit.platform.commons.support +exports org.junit.platform.commons.support.conversion requires java.base mandated requires java.logging requires java.management diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-engine.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-engine.expected.txt index 956d780949b2..04812758a63f 100644 --- a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-engine.expected.txt +++ b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-engine.expected.txt @@ -12,3 +12,5 @@ requires java.base mandated requires org.apiguardian.api static transitive requires org.junit.platform.commons transitive requires org.opentest4j transitive +uses org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser +provides 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 diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-launcher.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-launcher.expected.txt index 1461169b0fee..41bb2afac024 100644 --- a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-launcher.expected.txt +++ b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-launcher.expected.txt @@ -10,6 +10,7 @@ requires org.junit.platform.commons transitive requires org.junit.platform.engine transitive uses org.junit.platform.engine.TestEngine uses org.junit.platform.launcher.LauncherDiscoveryListener +uses org.junit.platform.launcher.LauncherInterceptor uses org.junit.platform.launcher.LauncherSessionListener uses org.junit.platform.launcher.PostDiscoveryFilter uses org.junit.platform.launcher.TestExecutionListener diff --git a/platform-tooling-support-tests/projects/java-versions/src/test/java/JUnitPlatformCommonsTests.java b/platform-tooling-support-tests/projects/java-versions/src/test/java/JUnitPlatformCommonsTests.java index 53a15919c631..1d76fa7b2ed4 100644 --- a/platform-tooling-support-tests/projects/java-versions/src/test/java/JUnitPlatformCommonsTests.java +++ b/platform-tooling-support-tests/projects/java-versions/src/test/java/JUnitPlatformCommonsTests.java @@ -1,6 +1,6 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/maven-starter/src/main/java/com/example/project/Calculator.java b/platform-tooling-support-tests/projects/maven-starter/src/main/java/com/example/project/Calculator.java index 6feca6bfc04b..c0540f6b3e95 100644 --- a/platform-tooling-support-tests/projects/maven-starter/src/main/java/com/example/project/Calculator.java +++ b/platform-tooling-support-tests/projects/maven-starter/src/main/java/com/example/project/Calculator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/maven-starter/src/test/java/com/example/project/CalculatorTests.java b/platform-tooling-support-tests/projects/maven-starter/src/test/java/com/example/project/CalculatorTests.java index 695282baccc8..0b0a25ab7fdc 100644 --- a/platform-tooling-support-tests/projects/maven-starter/src/test/java/com/example/project/CalculatorTests.java +++ b/platform-tooling-support-tests/projects/maven-starter/src/test/java/com/example/project/CalculatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/maven-surefire-compatibility/src/test/java/com/example/project/DummyTests.java b/platform-tooling-support-tests/projects/maven-surefire-compatibility/src/test/java/com/example/project/DummyTests.java index a695ded160b5..b9f8a24f46b0 100644 --- a/platform-tooling-support-tests/projects/maven-surefire-compatibility/src/test/java/com/example/project/DummyTests.java +++ b/platform-tooling-support-tests/projects/maven-surefire-compatibility/src/test/java/com/example/project/DummyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/multi-release-jar/default/src/test/java/integration/integration/JupiterIntegrationTests.java b/platform-tooling-support-tests/projects/multi-release-jar/default/src/test/java/integration/integration/JupiterIntegrationTests.java index 311153970b7c..d3664cae5998 100644 --- a/platform-tooling-support-tests/projects/multi-release-jar/default/src/test/java/integration/integration/JupiterIntegrationTests.java +++ b/platform-tooling-support-tests/projects/multi-release-jar/default/src/test/java/integration/integration/JupiterIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/multi-release-jar/default/src/test/java/integration/integration/ModuleUtilsTests.java b/platform-tooling-support-tests/projects/multi-release-jar/default/src/test/java/integration/integration/ModuleUtilsTests.java index 50cecc69625f..d4d961dcc251 100644 --- a/platform-tooling-support-tests/projects/multi-release-jar/default/src/test/java/integration/integration/ModuleUtilsTests.java +++ b/platform-tooling-support-tests/projects/multi-release-jar/default/src/test/java/integration/integration/ModuleUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/reflection-tests/build.gradle.kts b/platform-tooling-support-tests/projects/reflection-tests/build.gradle.kts new file mode 100644 index 000000000000..653257ae1042 --- /dev/null +++ b/platform-tooling-support-tests/projects/reflection-tests/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + java +} + +// grab jupiter version from system environment +val jupiterVersion: String = System.getenv("JUNIT_JUPITER_VERSION") +val vintageVersion: String = System.getenv("JUNIT_VINTAGE_VERSION") +val platformVersion: String = System.getenv("JUNIT_PLATFORM_VERSION") + +repositories { + maven { url = uri(file(System.getProperty("maven.repo"))) } + mavenCentral() +} + +dependencies { + testImplementation("org.junit.jupiter:junit-jupiter:$jupiterVersion") + testImplementation("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion") + testRuntimeOnly("org.junit.platform:junit-platform-reporting:$platformVersion") +} + +tasks.test { + useJUnitPlatform() + + testLogging { + events("failed") + } + + reports { + html.required = true + } + + val outputDir = reports.junitXml.outputLocation + jvmArgumentProviders += CommandLineArgumentProvider { + listOf( + "-Djunit.platform.reporting.open.xml.enabled=true", + "-Djunit.platform.reporting.output.dir=${outputDir.get().asFile.absolutePath}" + ) + } + + doFirst { + println("Using Java version: ${JavaVersion.current()}") + } +} diff --git a/platform-tooling-support-tests/projects/reflection-tests/settings.gradle.kts b/platform-tooling-support-tests/projects/reflection-tests/settings.gradle.kts new file mode 100644 index 000000000000..af17e8f41649 --- /dev/null +++ b/platform-tooling-support-tests/projects/reflection-tests/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "reflection-tests" diff --git a/platform-tooling-support-tests/projects/reflection-tests/src/test/java/ReflectionTestCase.java b/platform-tooling-support-tests/projects/reflection-tests/src/test/java/ReflectionTestCase.java new file mode 100644 index 000000000000..0692db43426b --- /dev/null +++ b/platform-tooling-support-tests/projects/reflection-tests/src/test/java/ReflectionTestCase.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package standalone; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +import java.util.Arrays; +import java.util.stream.Stream; + +import org.junit.jupiter.api.DynamicNode; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor; +import org.junit.jupiter.engine.descriptor.ClassTestDescriptor; +import org.junit.jupiter.engine.descriptor.JupiterTestDescriptor; +import org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor; +import org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor; +import org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor; +import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor; +import org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor; +import org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor; + +class ReflectionTestCase { + + @TestFactory + Stream canReadParameters() { + return Stream.of(JupiterTestDescriptor.class, ClassBasedTestDescriptor.class, ClassTestDescriptor.class, + MethodBasedTestDescriptor.class, TestMethodTestDescriptor.class, TestTemplateTestDescriptor.class, + TestTemplateInvocationTestDescriptor.class, TestFactoryTestDescriptor.class, + NestedClassTestDescriptor.class) // + .map(descriptorClass -> dynamicContainer(descriptorClass.getSimpleName(), + Arrays.stream(descriptorClass.getDeclaredMethods()) // + .map(method -> dynamicTest(method.getName(), + () -> assertDoesNotThrow(method::getParameters))))); + } +} diff --git a/platform-tooling-support-tests/projects/standalone/expected-err.txt b/platform-tooling-support-tests/projects/standalone/expected-err.txt index cc4b8df06ddd..de1899fd021a 100644 --- a/platform-tooling-support-tests/projects/standalone/expected-err.txt +++ b/platform-tooling-support-tests/projects/standalone/expected-err.txt @@ -4,9 +4,9 @@ .+ Loaded LauncherSessionListener instances: .. .+ org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines .+ Discovered TestEngines: +- junit-platform-suite .+ - junit-jupiter .+ - junit-vintage .+ -- junit-platform-suite .+ .+ org.junit.platform.launcher.core.ServiceLoaderRegistry load .+ Loaded PostDiscoveryFilter instances: .. .+ org.junit.platform.launcher.core.ServiceLoaderRegistry load @@ -15,6 +15,6 @@ .+ Loaded TestExecutionListener instances: .+ .+ org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines .+ Discovered TestEngines: +- junit-platform-suite .+ - junit-jupiter .+ - junit-vintage .+ -- junit-platform-suite .+ diff --git a/platform-tooling-support-tests/projects/standalone/src/standalone/JupiterIntegration.java b/platform-tooling-support-tests/projects/standalone/src/standalone/JupiterIntegration.java index 04505d934cca..718927809b9c 100644 --- a/platform-tooling-support-tests/projects/standalone/src/standalone/JupiterIntegration.java +++ b/platform-tooling-support-tests/projects/standalone/src/standalone/JupiterIntegration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/standalone/src/standalone/JupiterParamsIntegration.java b/platform-tooling-support-tests/projects/standalone/src/standalone/JupiterParamsIntegration.java index 270b9a163a5c..c2e632a83a1e 100644 --- a/platform-tooling-support-tests/projects/standalone/src/standalone/JupiterParamsIntegration.java +++ b/platform-tooling-support-tests/projects/standalone/src/standalone/JupiterParamsIntegration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/standalone/src/standalone/SuiteIntegration.java b/platform-tooling-support-tests/projects/standalone/src/standalone/SuiteIntegration.java index a06fc90fbb99..3719ebd097af 100644 --- a/platform-tooling-support-tests/projects/standalone/src/standalone/SuiteIntegration.java +++ b/platform-tooling-support-tests/projects/standalone/src/standalone/SuiteIntegration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/standalone/src/standalone/VintageIntegration.java b/platform-tooling-support-tests/projects/standalone/src/standalone/VintageIntegration.java index 3fb2886391c6..151460a6f0cc 100644 --- a/platform-tooling-support-tests/projects/standalone/src/standalone/VintageIntegration.java +++ b/platform-tooling-support-tests/projects/standalone/src/standalone/VintageIntegration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/projects/vintage/src/test/java/DefaultPackageTest.java b/platform-tooling-support-tests/projects/vintage/src/test/java/DefaultPackageTest.java new file mode 100644 index 000000000000..beebfdd5fd54 --- /dev/null +++ b/platform-tooling-support-tests/projects/vintage/src/test/java/DefaultPackageTest.java @@ -0,0 +1,22 @@ + +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ +import com.example.vintage.VintageTest; + +import org.junit.Ignore; + +/** + * Reproducer for https://github.com/junit-team/junit5/issues/4076 + */ +@Ignore +public class DefaultPackageTest extends VintageTest { + void packagePrivateMethod() { + } +} diff --git a/platform-tooling-support-tests/projects/vintage/src/test/java/com/example/vintage/VintageTest.java b/platform-tooling-support-tests/projects/vintage/src/test/java/com/example/vintage/VintageTest.java index 186bbf2eff6a..a6efb8990791 100644 --- a/platform-tooling-support-tests/projects/vintage/src/test/java/com/example/vintage/VintageTest.java +++ b/platform-tooling-support-tests/projects/vintage/src/test/java/com/example/vintage/VintageTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -15,6 +15,9 @@ import org.junit.Test; public class VintageTest { + void packagePrivateMethod() { + } + @Test public void success() { // pass diff --git a/platform-tooling-support-tests/src/main/java/platform/tooling/support/Helper.java b/platform-tooling-support-tests/src/main/java/platform/tooling/support/Helper.java index 517e4e5fe175..4bb2c6c73ad6 100644 --- a/platform-tooling-support-tests/src/main/java/platform/tooling/support/Helper.java +++ b/platform-tooling-support-tests/src/main/java/platform/tooling/support/Helper.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/src/main/java/platform/tooling/support/MavenRepo.java b/platform-tooling-support-tests/src/main/java/platform/tooling/support/MavenRepo.java index 8764aea0b3c1..2ba22ebeb8cc 100644 --- a/platform-tooling-support-tests/src/main/java/platform/tooling/support/MavenRepo.java +++ b/platform-tooling-support-tests/src/main/java/platform/tooling/support/MavenRepo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/src/main/java/platform/tooling/support/Request.java b/platform-tooling-support-tests/src/main/java/platform/tooling/support/Request.java index 03e2cf13691a..3ccaac91edee 100644 --- a/platform-tooling-support-tests/src/main/java/platform/tooling/support/Request.java +++ b/platform-tooling-support-tests/src/main/java/platform/tooling/support/Request.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/src/main/java/platform/tooling/support/ThirdPartyJars.java b/platform-tooling-support-tests/src/main/java/platform/tooling/support/ThirdPartyJars.java index 02b256e4551e..04ff5084b0dd 100644 --- a/platform-tooling-support-tests/src/main/java/platform/tooling/support/ThirdPartyJars.java +++ b/platform-tooling-support-tests/src/main/java/platform/tooling/support/ThirdPartyJars.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -23,11 +23,15 @@ private ThirdPartyJars() { } public static void copy(Path targetDir, String group, String artifact) throws Exception { - Path source = Stream.of(System.getProperty("thirdPartyJars").split(File.pathSeparator)) // + Path source = find(group, artifact); + Files.copy(source, targetDir.resolve(source.getFileName()), REPLACE_EXISTING); + } + + public static Path find(String group, String artifact) { + return Stream.of(System.getProperty("thirdPartyJars").split(File.pathSeparator)) // .filter(it -> it.replace(File.separator, "/").contains("/" + group + "/" + artifact + "/")) // .map(Path::of) // .findFirst() // .orElseThrow(() -> new AssertionError("Failed to find JAR file for " + group + ":" + artifact)); - Files.copy(source, targetDir.resolve(source.getFileName()), REPLACE_EXISTING); } } diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java index fad952bf6253..15fe8d5e4ecd 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -20,10 +20,12 @@ import java.util.List; import java.util.Optional; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +@Order(Integer.MAX_VALUE) class HelperTests { @Test diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/AntStarterTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/AntStarterTests.java index d891716c51d3..cfcda9130c05 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/AntStarterTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/AntStarterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -21,6 +21,7 @@ import org.apache.tools.ant.Main; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.ResourceLock; import platform.tooling.support.Request; @@ -29,11 +30,12 @@ */ class AntStarterTests { + @ResourceLock(Projects.ANT_STARTER) @Test void ant_starter() { var request = Request.builder() // .setTool(new Java()) // - .setProject("ant-starter") // + .setProject(Projects.ANT_STARTER) // .addArguments("-cp", System.getProperty("antJars"), Main.class.getName()) // .addArguments("-verbose") // .build(); diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ArchUnitTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ArchUnitTests.java index 2b26d8897cb2..1ecbc4764582 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ArchUnitTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ArchUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -21,6 +21,7 @@ import static com.tngtech.archunit.core.domain.properties.HasModifiers.Predicates.modifier; import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name; import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameContaining; +import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameStartingWith; import static com.tngtech.archunit.lang.conditions.ArchPredicates.are; import static com.tngtech.archunit.lang.conditions.ArchPredicates.have; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; @@ -28,14 +29,23 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static platform.tooling.support.Helper.loadJarFiles; +import java.lang.annotation.Annotation; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.Arrays; import java.util.Set; +import java.util.function.BiPredicate; import java.util.stream.Collectors; +import com.tngtech.archunit.base.DescribedPredicate; +import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.importer.Location; import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.ArchTest; import com.tngtech.archunit.junit.LocationProvider; +import com.tngtech.archunit.lang.ArchCondition; import com.tngtech.archunit.lang.ArchRule; import com.tngtech.archunit.library.GeneralCodingRules; @@ -44,6 +54,7 @@ @AnalyzeClasses(locations = ArchUnitTests.AllJars.class) class ArchUnitTests { + @SuppressWarnings("unused") @ArchTest private final ArchRule allPublicTopLevelTypesHaveApiAnnotations = classes() // .that(have(modifier(PUBLIC))) // @@ -53,6 +64,15 @@ class ArchUnitTests { .and(not(describe("are shadowed", resideInAnyPackage("..shadow..")))) // .should().beAnnotatedWith(API.class); + @SuppressWarnings("unused") + @ArchTest // Consistency of @Documented and @Inherited is checked by the compiler but not for @Retention and @Target + private final ArchRule repeatableAnnotationsShouldHaveMatchingContainerAnnotations = classes() // + .that(nameStartingWith("org.junit.")) // + .and().areAnnotations() // + .and().areAnnotatedWith(Repeatable.class) // + .should(haveContainerAnnotationWithSameRetentionPolicy()) // + .andShould(haveContainerAnnotationWithSameTargetTypes()); + @ArchTest void allAreIn(JavaClasses classes) { // about 928 classes found in all jars @@ -92,6 +112,16 @@ void avoidAccessingStandardStreams(JavaClasses classes) { GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS.check(subset); } + private static ArchCondition haveContainerAnnotationWithSameRetentionPolicy() { + return ArchCondition.from(new RepeatableAnnotationPredicate<>(Retention.class, + (expectedTarget, actualTarget) -> expectedTarget.value() == actualTarget.value())); + } + + private static ArchCondition haveContainerAnnotationWithSameTargetTypes() { + return ArchCondition.from(new RepeatableAnnotationPredicate<>(Target.class, + (expectedTarget, actualTarget) -> Arrays.equals(expectedTarget.value(), actualTarget.value()))); + } + static class AllJars implements LocationProvider { @Override @@ -101,4 +131,27 @@ public Set get(Class testClass) { } + private static class RepeatableAnnotationPredicate extends DescribedPredicate { + + private final Class annotationType; + private final BiPredicate predicate; + + public RepeatableAnnotationPredicate(Class annotationType, BiPredicate predicate) { + super("have identical @%s annotation as container annotation", annotationType.getSimpleName()); + this.annotationType = annotationType; + this.predicate = predicate; + } + + @Override + public boolean test(JavaClass annotationClass) { + var containerAnnotationClass = (JavaClass) annotationClass.getAnnotationOfType( + Repeatable.class.getName()).get("value").orElseThrow(); + var expectedAnnotation = annotationClass.tryGetAnnotationOfType(annotationType); + var actualAnnotation = containerAnnotationClass.tryGetAnnotationOfType(annotationType); + return expectedAnnotation.map(expectedTarget -> actualAnnotation // + .map(actualTarget -> predicate.test(expectedTarget, actualTarget)) // + .orElse(false)) // + .orElse(actualAnnotation.isEmpty()); + } + } } diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GraalVmStarterTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GraalVmStarterTests.java index 53b5742aa4ee..349d5d520b4d 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GraalVmStarterTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GraalVmStarterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,20 +10,20 @@ package platform.tooling.support.tests; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeFalse; -import static platform.tooling.support.tests.XmlAssertions.verifyContainsExpectedStartedOpenTestReport; import java.nio.file.Paths; import java.time.Duration; import de.sormuras.bartholdy.tool.GradleWrapper; -import com.gradle.enterprise.testing.annotations.LocalOnly; - +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.junit.jupiter.api.extension.DisabledOnOpenJ9; +import org.junit.jupiter.api.parallel.ResourceLock; import platform.tooling.support.MavenRepo; import platform.tooling.support.Request; @@ -31,31 +31,31 @@ /** * @since 1.9.1 */ -@LocalOnly(because = "GraalVM is not installed on Test Distribution agents") +@Order(Integer.MIN_VALUE) +@DisabledOnOpenJ9 +@EnabledIfEnvironmentVariable(named = "GRAALVM_HOME", matches = ".+") class GraalVmStarterTests { + @ResourceLock(Projects.GRAALVM_STARTER) @Test void runsTestsInNativeImage() { var request = Request.builder() // .setTool(new GradleWrapper(Paths.get(".."))) // - .setProject("graalvm-starter") // + .setProject(Projects.GRAALVM_STARTER) // .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // - .addArguments("javaToolchains", "nativeTest", "--no-daemon", "--stacktrace") // - .setTimeout(Duration.ofMinutes(5)) // + .addArguments("javaToolchains", "nativeTest", "--no-daemon", "--stacktrace", "--no-build-cache") // + .setTimeout(Duration.ofMinutes(10)) // .build(); var result = request.run(); assertFalse(result.isTimedOut(), () -> "tool timed out: " + result); - assumeFalse( - result.getOutputLines("err").stream().anyMatch(line -> line.contains("No matching toolchains found")), - "Abort test if GraalVM is not installed"); - assertEquals(0, result.getExitCode()); - assertTrue(result.getOutputLines("out").stream().anyMatch(line -> line.contains("BUILD SUCCESSFUL"))); - - var testResultsDir = Request.WORKSPACE.resolve(request.getWorkspace()).resolve("build/test-results/test"); - verifyContainsExpectedStartedOpenTestReport(testResultsDir); + assertThat(result.getOutputLines("out")) // + .anyMatch(line -> line.contains("CalculatorTests > 1 + 1 = 2 SUCCESSFUL")) // + .anyMatch(line -> line.contains("CalculatorTests > 1 + 100 = 101 SUCCESSFUL")) // + .anyMatch(line -> line.contains("ClassLevelAnnotationTests$Inner > test() SUCCESSFUL")) // + .anyMatch(line -> line.contains("BUILD SUCCESSFUL")); } } diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleKotlinExtensionsTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleKotlinExtensionsTests.java index 2b5411be11b9..fee5e16082d0 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleKotlinExtensionsTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleKotlinExtensionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -15,9 +15,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static platform.tooling.support.Helper.TOOL_TIMEOUT; +import java.nio.file.Paths; + import de.sormuras.bartholdy.tool.GradleWrapper; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.ResourceLock; import org.opentest4j.TestAbortedException; import platform.tooling.support.Helper; @@ -29,13 +32,14 @@ */ class GradleKotlinExtensionsTests { + @ResourceLock(Projects.GRADLE_KOTLIN_EXTENSIONS) @Test void gradle_wrapper() { var result = Request.builder() // - .setTool(new GradleWrapper(Request.PROJECTS.resolve("gradle-kotlin-extensions"))) // - .setProject("gradle-kotlin-extensions") // + .setTool(new GradleWrapper(Paths.get(".."))) // + .setProject(Projects.GRADLE_KOTLIN_EXTENSIONS) // .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // - .addArguments("build", "--no-daemon", "--stacktrace") // + .addArguments("build", "--no-daemon", "--stacktrace", "--no-build-cache") // .setTimeout(TOOL_TIMEOUT) // .setJavaHome(Helper.getJavaHome("8").orElseThrow(TestAbortedException::new)) // .build() // diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleMissingEngineTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleMissingEngineTests.java index c82360af1be6..69204b5a2a46 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleMissingEngineTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleMissingEngineTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -22,6 +22,7 @@ import de.sormuras.bartholdy.tool.GradleWrapper; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.ResourceLock; import org.opentest4j.TestAbortedException; import platform.tooling.support.Helper; @@ -33,19 +34,18 @@ */ class GradleMissingEngineTests { + @ResourceLock(Projects.GRADLE_MISSING_ENGINE) @Test void gradle_wrapper() { test(new GradleWrapper(Paths.get(".."))); } private void test(Tool gradle) { - var project = "gradle-missing-engine"; var result = Request.builder() // - .setProject(project) // - .setWorkspace(project + "-wrapper") // + .setProject(Projects.GRADLE_MISSING_ENGINE) // .setTool(gradle) // .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // - .addArguments("build", "--no-daemon", "--debug", "--stacktrace") // + .addArguments("build", "--no-daemon", "--debug", "--stacktrace", "--no-build-cache") // .setJavaHome(Helper.getJavaHome("8").orElseThrow(TestAbortedException::new)) // .setTimeout(TOOL_TIMEOUT).build() // .run(); diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleModuleFileTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleModuleFileTests.java index 8c72f4ee63e3..e3fd94a7f67b 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleModuleFileTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleModuleFileTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -15,6 +15,7 @@ import java.nio.file.Files; import java.util.List; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import platform.tooling.support.MavenRepo; @@ -22,6 +23,7 @@ /** * @since 1.6 */ +@Order(Integer.MAX_VALUE) class GradleModuleFileTests { @Test diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleStarterTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleStarterTests.java index 69d884cc706d..38c400ed79e8 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleStarterTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleStarterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -22,6 +22,7 @@ import de.sormuras.bartholdy.tool.GradleWrapper; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.ResourceLock; import org.opentest4j.TestAbortedException; import platform.tooling.support.Helper; @@ -33,13 +34,14 @@ */ class GradleStarterTests { + @ResourceLock(Projects.GRADLE_STARTER) @Test void gradle_wrapper() { var request = Request.builder() // .setTool(new GradleWrapper(Paths.get(".."))) // - .setProject("gradle-starter") // + .setProject(Projects.GRADLE_STARTER) // .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // - .addArguments("build", "--no-daemon", "--stacktrace") // + .addArguments("build", "--no-daemon", "--stacktrace", "--no-build-cache") // .setTimeout(TOOL_TIMEOUT) // .setJavaHome(Helper.getJavaHome("8").orElseThrow(TestAbortedException::new)) // .build(); diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JarContainsManifestFirstTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JarContainsManifestFirstTests.java index 05c824ced1e6..aac342d5d92f 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JarContainsManifestFirstTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JarContainsManifestFirstTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -17,6 +17,7 @@ import java.nio.file.Files; import java.util.jar.JarInputStream; +import org.junit.jupiter.api.Order; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -25,6 +26,7 @@ /** * @since 1.8 */ +@Order(Integer.MAX_VALUE) class JarContainsManifestFirstTests { @ParameterizedTest diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JarDescribeModuleTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JarDescribeModuleTests.java index aeb3f7bd2447..a38c353cda4d 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JarDescribeModuleTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JarDescribeModuleTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -24,6 +24,8 @@ import de.sormuras.bartholdy.jdk.Jar; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.parallel.ResourceLock; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -34,15 +36,17 @@ /** * @since 1.3 */ +@Order(Integer.MAX_VALUE) class JarDescribeModuleTests { + @ResourceLock(Projects.JAR_DESCRIBE_MODULE) @ParameterizedTest @MethodSource("platform.tooling.support.Helper#loadModuleDirectoryNames") void describeModule(String module) throws Exception { var modulePath = MavenRepo.jar(module); var result = Request.builder() // .setTool(new Jar()) // - .setProject("jar-describe-module") // + .setProject(Projects.JAR_DESCRIBE_MODULE) // .setProjectToWorkspaceCopyFileFilter(file -> file.getName().startsWith(module)) // .setWorkspace("jar-describe-module/" + module) // .addArguments("--describe-module", "--file", modulePath) // diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JavaVersionsTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JavaVersionsTests.java index dce1cfd6e41f..d7785cd6e1ad 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JavaVersionsTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JavaVersionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -18,6 +18,7 @@ import java.nio.file.Path; import java.util.List; +import java.util.Map; import de.sormuras.bartholdy.tool.Java; @@ -36,28 +37,31 @@ class JavaVersionsTests { void java_8() { var java8Home = Helper.getJavaHome("8"); assumeTrue(java8Home.isPresent(), "Java 8 installation directory not found!"); - var actualLines = execute("8", java8Home.get()); + var actualLines = execute("8", java8Home.get(), Map.of()); assertTrue(actualLines.contains("[WARNING] Tests run: 2, Failures: 0, Errors: 0, Skipped: 1")); } @Test void java_default() { - var actualLines = execute("default", new Java().getHome()); + var actualLines = execute("default", new Java().getHome(), MavenEnvVars.FOR_JDK24_AND_LATER); assertTrue(actualLines.contains("[WARNING] Tests run: 2, Failures: 0, Errors: 0, Skipped: 1")); } - List execute(String version, Path javaHome) { - var result = Request.builder() // + List execute(String version, Path javaHome, Map environmentVars) { + var builder = Request.builder() // .setTool(Request.maven()) // - .setProject("java-versions") // + .setProject(Projects.JAVA_VERSIONS) // .setWorkspace("java-versions-" + version) // .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // .addArguments("--update-snapshots", "--batch-mode", "verify") // .setTimeout(TOOL_TIMEOUT) // - .setJavaHome(javaHome) // - .build().run(); + .setJavaHome(javaHome); + environmentVars.forEach(builder::putEnvironment); + + var result = builder.build().run(); + assertFalse(result.isTimedOut(), () -> "tool timed out: " + result); assertEquals(0, result.getExitCode()); assertEquals("", result.getOutput("err")); diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ManifestTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ManifestTests.java index b09a64bc8bc6..5f507ea1d1f9 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ManifestTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ManifestTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -21,6 +21,7 @@ import aQute.bnd.osgi.Jar; import aQute.bnd.version.MavenVersion; +import org.junit.jupiter.api.Order; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.osgi.framework.Version; @@ -32,6 +33,7 @@ /** * @since 1.5 */ +@Order(Integer.MAX_VALUE) class ManifestTests { @ParameterizedTest @@ -56,8 +58,8 @@ void manifestEntriesAdhereToConventions(String module) throws Exception { MavenVersion.parseMavenString(version).getOSGiVersion().toString()); switch (module) { case "junit-platform-commons" -> assertValue(attributes, "Multi-Release", "true"); - case "junit-platform-console" -> - assertValue(attributes, "Main-Class", "org.junit.platform.console.ConsoleLauncher"); + case "junit-platform-console" -> assertValue(attributes, "Main-Class", + "org.junit.platform.console.ConsoleLauncher"); } var domain = Domain.domain(manifest); domain.getExportPackage().forEach((pkg, attrs) -> { diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenEnvVars.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenEnvVars.java new file mode 100644 index 000000000000..bcec609010a0 --- /dev/null +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenEnvVars.java @@ -0,0 +1,27 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package platform.tooling.support.tests; + +import java.util.Map; + +import org.junit.jupiter.api.condition.JRE; + +final class MavenEnvVars { + + // https://issues.apache.org/jira/browse/MNG-8248 + static final Map FOR_JDK24_AND_LATER = JRE.currentVersion().compareTo(JRE.JAVA_24) >= 0 // + ? Map.of("MAVEN_OPTS", "--enable-native-access=ALL-UNNAMED") // + : Map.of(); + + private MavenEnvVars() { + } + +} diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenPomFileTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenPomFileTests.java index db91462c78f7..e7e4fd4881b8 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenPomFileTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenPomFileTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -15,6 +15,7 @@ import java.nio.file.Files; import java.util.List; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import platform.tooling.support.MavenRepo; @@ -22,6 +23,7 @@ /** * @since 1.4 */ +@Order(Integer.MAX_VALUE) class MavenPomFileTests { @Test diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenStarterTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenStarterTests.java index 26a7d371cfd3..bf5b8a581437 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenStarterTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenStarterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -18,6 +18,7 @@ import static platform.tooling.support.tests.XmlAssertions.verifyContainsExpectedStartedOpenTestReport; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.ResourceLock; import org.opentest4j.TestAbortedException; import platform.tooling.support.Helper; @@ -29,11 +30,12 @@ */ class MavenStarterTests { + @ResourceLock(Projects.MAVEN_STARTER) @Test void verifyMavenStarterProject() { var request = Request.builder() // .setTool(Request.maven()) // - .setProject("maven-starter") // + .setProject(Projects.MAVEN_STARTER) // .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // .addArguments("--update-snapshots", "--batch-mode", "verify") // .setTimeout(TOOL_TIMEOUT) // diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenSurefireCompatibilityTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenSurefireCompatibilityTests.java index f346969b1d6c..d0e20e1c450e 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenSurefireCompatibilityTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenSurefireCompatibilityTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -19,6 +19,7 @@ import java.io.IOException; import java.nio.file.Files; +import org.junit.jupiter.api.parallel.ResourceLock; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.opentest4j.TestAbortedException; @@ -32,6 +33,7 @@ */ class MavenSurefireCompatibilityTests { + @ResourceLock(Projects.MAVEN_SUREFIRE_COMPATIBILITY) @ParameterizedTest @CsvSource(delimiter = '|', nullValues = "", textBlock = """ 2.22.2 | --activate-profiles=manual-platform-dependency @@ -41,7 +43,7 @@ void testMavenSurefireCompatibilityProject(String surefireVersion, String extraA var extraArgs = extraArg == null ? new Object[0] : new Object[] { extraArg }; var request = Request.builder() // .setTool(Request.maven()) // - .setProject("maven-surefire-compatibility") // + .setProject(Projects.MAVEN_SUREFIRE_COMPATIBILITY) // .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // .addArguments("-Dsurefire.version=" + surefireVersion) // .addArguments("--update-snapshots", "--batch-mode", "test") // diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java index 1369eb94366d..2427bc41a08a 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -27,6 +27,7 @@ import org.codehaus.groovy.runtime.ProcessGroovyMethods; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.DisabledOnOpenJ9; import org.junit.jupiter.api.io.TempDir; import platform.tooling.support.Helper; @@ -35,6 +36,7 @@ /** * @since 1.5 */ +@DisabledOnOpenJ9 class ModularUserGuideTests { private static final String DOCUMENTATION_MODULE_DESCRIPTOR = """ diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MultiReleaseJarTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MultiReleaseJarTests.java index 438ca769d65d..33105f0983f4 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MultiReleaseJarTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MultiReleaseJarTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -19,10 +19,12 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.Map; import de.sormuras.bartholdy.Result; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.ResourceLock; import platform.tooling.support.MavenRepo; import platform.tooling.support.Request; @@ -32,6 +34,7 @@ */ class MultiReleaseJarTests { + @ResourceLock(Projects.MULTI_RELEASE_JAR) @Test void checkDefault() throws Exception { var variant = "default"; @@ -80,15 +83,18 @@ void checkDefault() throws Exception { } private Result mvn(String variant) { - var result = Request.builder() // + Map environmentVars = MavenEnvVars.FOR_JDK24_AND_LATER; + + var builder = Request.builder() // .setTool(Request.maven()) // - .setProject("multi-release-jar") // + .setProject(Projects.MULTI_RELEASE_JAR) // .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // .addArguments("--update-snapshots", "--show-version", "--errors", "--batch-mode", "--file", variant, "test") // - .setTimeout(TOOL_TIMEOUT) // - .build() // - .run(); + .setTimeout(TOOL_TIMEOUT); + environmentVars.forEach(builder::putEnvironment); + + var result = builder.build().run(); assertFalse(result.isTimedOut(), () -> "tool timed out: " + result); diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/PackageCyclesDetectionTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/PackageCyclesDetectionTests.java index 78f35146f372..9b2126bec212 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/PackageCyclesDetectionTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/PackageCyclesDetectionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java new file mode 100644 index 000000000000..87f8920d16a6 --- /dev/null +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java @@ -0,0 +1,31 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package platform.tooling.support.tests; + +public class Projects { + + public static final String ANT_STARTER = "ant-starter"; + public static final String GRAALVM_STARTER = "graalvm-starter"; + public static final String GRADLE_KOTLIN_EXTENSIONS = "gradle-kotlin-extensions"; + public static final String GRADLE_MISSING_ENGINE = "gradle-missing-engine"; + public static final String GRADLE_STARTER = "gradle-starter"; + public static final String JAR_DESCRIBE_MODULE = "jar-describe-module"; + public static final String JAVA_VERSIONS = "java-versions"; + public static final String MAVEN_STARTER = "maven-starter"; + public static final String MAVEN_SUREFIRE_COMPATIBILITY = "maven-surefire-compatibility"; + public static final String MULTI_RELEASE_JAR = "multi-release-jar"; + public static final String REFLECTION_TESTS = "reflection-tests"; + public static final String STANDALONE = "standalone"; + public static final String VINTAGE = "vintage"; + + private Projects() { + } +} diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ReflectionCompatibilityTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ReflectionCompatibilityTests.java new file mode 100644 index 000000000000..273a9a107111 --- /dev/null +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ReflectionCompatibilityTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package platform.tooling.support.tests; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static platform.tooling.support.Helper.TOOL_TIMEOUT; + +import java.nio.file.Paths; + +import de.sormuras.bartholdy.tool.GradleWrapper; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.ResourceLock; +import org.opentest4j.TestAbortedException; + +import platform.tooling.support.Helper; +import platform.tooling.support.MavenRepo; +import platform.tooling.support.Request; + +/** + * @since 1.11 + */ +class ReflectionCompatibilityTests { + + @ResourceLock(Projects.REFLECTION_TESTS) + @Test + void gradle_wrapper() { + var request = Request.builder() // + .setTool(new GradleWrapper(Paths.get(".."))) // + .setProject(Projects.REFLECTION_TESTS) // + .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // + .addArguments("build", "--no-daemon", "--stacktrace", "--no-build-cache") // + .setTimeout(TOOL_TIMEOUT) // + .setJavaHome(Helper.getJavaHome("8").orElseThrow(TestAbortedException::new)) // + .build(); + + var result = request.run(); + + assertFalse(result.isTimedOut(), () -> "tool timed out: " + result); + + assertEquals(0, result.getExitCode()); + assertTrue(result.getOutputLines("out").stream().anyMatch(line -> line.contains("BUILD SUCCESSFUL"))); + assertThat(result.getOutput("out")).contains("Using Java version: 1.8"); + } +} diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/StandaloneTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/StandaloneTests.java index eb8eb20254f2..e033b3e62e97 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/StandaloneTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/StandaloneTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -10,6 +10,7 @@ package platform.tooling.support.tests; +import static java.util.stream.Collectors.joining; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertLinesMatch; @@ -21,6 +22,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; import de.sormuras.bartholdy.Result; import de.sormuras.bartholdy.jdk.Jar; @@ -32,11 +35,13 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.parallel.ResourceLock; import org.opentest4j.TestAbortedException; import platform.tooling.support.Helper; import platform.tooling.support.MavenRepo; import platform.tooling.support.Request; +import platform.tooling.support.ThirdPartyJars; /** * @since 1.4 @@ -44,6 +49,7 @@ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class StandaloneTests { + @ResourceLock(Projects.STANDALONE) @Test void jarFileWithoutCompiledModuleDescriptorClass() throws Exception { var jar = MavenRepo.jar("junit-platform-console-standalone"); @@ -60,16 +66,17 @@ void jarFileWithoutCompiledModuleDescriptorClass() throws Exception { assertTrue(found.isEmpty(), jar + " must not contain any " + name + " files: " + found); } + @ResourceLock(Projects.STANDALONE) @Test void listAllObservableEngines() { var result = Request.builder() // .setTool(new Java()) // - .setProject("standalone") // + .setProject(Projects.STANDALONE) // .addArguments("-jar", MavenRepo.jar("junit-platform-console-standalone")) // - .addArguments("engines", "--disable-banner").build() // + .addArguments("engines", "--disable-ansi-colors", "--disable-banner").build() // .run(false); - assertEquals(0, result.getExitCode(), String.join("\n", result.getOutputLines("out"))); + assertEquals(0, result.getExitCode(), () -> getExitCodeMessage(result)); var jupiterVersion = Helper.version("junit-jupiter-engine"); var suiteVersion = Helper.version("junit-platform-suite-engine"); @@ -82,13 +89,70 @@ void listAllObservableEngines() { result.getOutput("out").lines()); } + @ResourceLock(Projects.STANDALONE) + @Test + void printVersionViaJar() { + var result = Request.builder() // + .setTool(new Java()) // + .setProject(Projects.STANDALONE) // + .addArguments("-jar", MavenRepo.jar("junit-platform-console-standalone")) // + .addArguments("--version", "--disable-ansi-colors") // + .putEnvironment("CLICOLOR_FORCE", "1") // enable ANSI colors by default (see https://picocli.info/#_heuristics_for_enabling_ansi) + .build() // + .run(); + + assertEquals(0, result.getExitCode(), () -> getExitCodeMessage(result)); + + var version = Helper.version("junit-platform-console"); + assertLinesMatch(""" + JUnit Platform Console Launcher %s + JVM: .* + OS: .* + """.formatted(version).lines(), // + result.getOutputLines("out").stream()); + } + + @ResourceLock(Projects.STANDALONE) + @Test + void printVersionViaModule() { + var junitJars = Stream.of("junit-platform-console", "junit-platform-reporting", "junit-platform-engine", + "junit-platform-launcher", "junit-platform-commons") // + .map(MavenRepo::jar); + var thirdPartyJars = Stream.of(ThirdPartyJars.find("org.opentest4j", "opentest4j")); + var modulePath = Stream.concat(junitJars, thirdPartyJars) // + .map(String::valueOf) // + .collect(joining(File.pathSeparator)); + var result = Request.builder() // + .setTool(new Java()) // + .setProject(Projects.STANDALONE) // + .addArguments("--module-path", modulePath) // + .addArguments("--module", "org.junit.platform.console") // + .addArguments("--version", "--disable-ansi-colors") // + .putEnvironment("CLICOLOR_FORCE", "1") // enable ANSI colors by default (see https://picocli.info/#_heuristics_for_enabling_ansi) + .build() // + .run(); + + assertEquals(0, result.getExitCode(), () -> getExitCodeMessage(result)); + + var version = Helper.version("junit-platform-console"); + assertLinesMatch(""" + JUnit Platform Console Launcher %s + JVM: .* + OS: .* + """.formatted(version).lines(), // + result.getOutputLines("out").stream()); + } + + @ResourceLock(Projects.STANDALONE) @Test @Order(1) void compile() throws Exception { - var workspace = Request.WORKSPACE.resolve("standalone"); + var workspace = Request.WORKSPACE.resolve(Projects.STANDALONE); var result = Request.builder() // .setTool(new Javac()) // - .setProject("standalone") // + .setProject(Projects.STANDALONE) // + .addArguments("-Xlint:-options") // + .addArguments("--release", "8") // .addArguments("-proc:none") // .addArguments("-d", workspace.resolve("bin")) // .addArguments("--class-path", MavenRepo.jar("junit-platform-console-standalone")) // @@ -98,7 +162,7 @@ void compile() throws Exception { .addArguments(workspace.resolve("src/standalone/VintageIntegration.java")).build() // .run(); - assertEquals(0, result.getExitCode(), result.getOutput("out") + result.getOutput("err")); + assertEquals(0, result.getExitCode(), () -> getExitCodeMessage(result)); assertTrue(result.getOutput("out").isEmpty()); assertTrue(result.getOutput("err").isEmpty()); @@ -106,7 +170,7 @@ void compile() throws Exception { var jarFolder = Files.createDirectories(workspace.resolve("jar")); var jarResult = Request.builder() // .setTool(new Jar()) // - .setProject("standalone") // + .setProject(Projects.STANDALONE) // .addArguments("--create") // .addArguments("--file", jarFolder.resolve("tests.jar")) // .addArguments("-C", workspace.resolve("bin"), ".") // @@ -114,6 +178,7 @@ void compile() throws Exception { assertEquals(0, jarResult.getExitCode(), String.join("\n", jarResult.getOutputLines("out"))); } + @ResourceLock(Projects.STANDALONE) @Test @Order(2) void discoverTree() { @@ -121,6 +186,11 @@ void discoverTree() { var expected = """ . + +-- JUnit Platform Suite + | '-- SuiteIntegration + | '-- JUnit Jupiter + | '-- SuiteIntegration$SingleTestContainer + | '-- successful() +-- JUnit Jupiter | +-- JupiterIntegration | | +-- successful() @@ -131,16 +201,11 @@ void discoverTree() { | | '-- successful() | '-- JupiterParamsIntegration | '-- parameterizedTest(String) - +-- JUnit Vintage - | '-- VintageIntegration - | +-- f4il - | +-- ignored - | '-- succ3ssful - '-- JUnit Platform Suite - '-- SuiteIntegration - '-- JUnit Jupiter - '-- SuiteIntegration$SingleTestContainer - '-- successful() + '-- JUnit Vintage + '-- VintageIntegration + +-- f4il + +-- ignored + '-- succ3ssful [ 11 containers found ] [ 9 tests found ] @@ -149,12 +214,18 @@ void discoverTree() { assertLinesMatch(expected.lines(), result.getOutputLines("out").stream()); } + @ResourceLock(Projects.STANDALONE) @Test @Order(2) void discoverFlat() { Result result = discover("--details=flat"); var expected = """ + JUnit Platform Suite ([engine:junit-platform-suite]) + SuiteIntegration ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]) + JUnit Jupiter ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]) + SuiteIntegration$SingleTestContainer ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]) + successful() ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]/[method:successful()]) JUnit Jupiter ([engine:junit-jupiter]) JupiterIntegration ([engine:junit-jupiter]/[class:standalone.JupiterIntegration]) successful() ([engine:junit-jupiter]/[class:standalone.JupiterIntegration]/[method:successful()]) @@ -170,11 +241,6 @@ JUnit Vintage ([engine:junit-vintage]) f4il ([engine:junit-vintage]/[runner:standalone.VintageIntegration]/[test:f4il(standalone.VintageIntegration)]) ignored ([engine:junit-vintage]/[runner:standalone.VintageIntegration]/[test:ignored(standalone.VintageIntegration)]) succ3ssful ([engine:junit-vintage]/[runner:standalone.VintageIntegration]/[test:succ3ssful(standalone.VintageIntegration)]) - JUnit Platform Suite ([engine:junit-platform-suite]) - SuiteIntegration ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]) - JUnit Jupiter ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]) - SuiteIntegration$SingleTestContainer ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]) - successful() ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]/[method:successful()]) [ 11 containers found ] [ 9 tests found ] @@ -189,6 +255,19 @@ void discoverVerbose() { Result result = discover("--details=verbose", "--details-theme=ascii"); var expected = """ + +-- JUnit Platform Suite + | +-- SuiteIntegration + | | +-- JUnit Jupiter + | | | +-- SuiteIntegration$SingleTestContainer + | | | | +-- successful() + | | | | | tags: [] + | | | | | uniqueId: [engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]/[method:successful()] + | | | | | parent: [engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer] + | | | | | source: MethodSource [className = 'standalone.SuiteIntegration$SingleTestContainer', methodName = 'successful', methodParameterTypes = ''] + | | | '-- SuiteIntegration$SingleTestContainer + | | '-- JUnit Jupiter + | '-- SuiteIntegration + '-- JUnit Platform Suite +-- JUnit Jupiter | +-- JupiterIntegration | | +-- successful() @@ -246,19 +325,6 @@ void discoverVerbose() { | | | source: MethodSource [className = 'standalone.VintageIntegration', methodName = 'succ3ssful', methodParameterTypes = ''] | '-- VintageIntegration '-- JUnit Vintage - +-- JUnit Platform Suite - | +-- SuiteIntegration - | | +-- JUnit Jupiter - | | | +-- SuiteIntegration$SingleTestContainer - | | | | +-- successful() - | | | | | tags: [] - | | | | | uniqueId: [engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]/[method:successful()] - | | | | | parent: [engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer] - | | | | | source: MethodSource [className = 'standalone.SuiteIntegration$SingleTestContainer', methodName = 'successful', methodParameterTypes = ''] - | | | '-- SuiteIntegration$SingleTestContainer - | | '-- JUnit Jupiter - | '-- SuiteIntegration - '-- JUnit Platform Suite [ 11 containers found ] [ 9 tests found ] @@ -267,6 +333,7 @@ void discoverVerbose() { assertLinesMatch(expected.lines(), result.getOutputLines("out").stream()); } + @ResourceLock(Projects.STANDALONE) @Test @Order(2) void discoverNone() { @@ -275,6 +342,7 @@ void discoverNone() { assertThat(result.getOutputLines("out")).isEmpty(); } + @ResourceLock(Projects.STANDALONE) @Test @Order(2) void discoverSummary() { @@ -289,12 +357,13 @@ void discoverSummary() { assertLinesMatch(expected.lines(), result.getOutputLines("out").stream()); } + @ResourceLock(Projects.STANDALONE) @Test @Order(2) void discoverTestFeed() { Result result = discover("--details=testfeed"); - var expected = """ + JUnit Platform Suite > SuiteIntegration > JUnit Jupiter > SuiteIntegration$SingleTestContainer > successful() JUnit Jupiter > JupiterIntegration > successful() JUnit Jupiter > JupiterIntegration > fail() JUnit Jupiter > JupiterIntegration > abort() @@ -303,40 +372,42 @@ JUnit Jupiter > JupiterIntegration > disabled() JUnit Vintage > VintageIntegration > f4il JUnit Vintage > VintageIntegration > ignored JUnit Vintage > VintageIntegration > succ3ssful - JUnit Platform Suite > SuiteIntegration > JUnit Jupiter > SuiteIntegration$SingleTestContainer > successful() [ 11 containers found ] [ 9 tests found ] """.stripIndent(); + assertLinesMatch(expected.lines(), result.getOutputLines("out").stream()); } private static Result discover(String... args) { var result = Request.builder() // + .putEnvironment("NO_COLOR", "1") // --disable-ansi-colors .setTool(new Java()) // - .setProject("standalone") // + .setProject(Projects.STANDALONE) // .addArguments("-jar", MavenRepo.jar("junit-platform-console-standalone")) // .addArguments("discover") // .addArguments("--scan-class-path") // .addArguments("--disable-banner") // - .addArguments("--disable-ansi-colors") // .addArguments("--include-classname", "standalone.*") // .addArguments("--classpath", "bin") // .addArguments((Object[]) args) // .build() // .run(false); - assertEquals(0, result.getExitCode(), String.join("\n", result.getOutputLines("out"))); + assertEquals(0, result.getExitCode(), () -> getExitCodeMessage(result)); return result; } + @ResourceLock(Projects.STANDALONE) @Test @Order(3) void execute() throws IOException { var result = Request.builder() // + .putEnvironment("NO_COLOR", "1") // --disable-ansi-colors .setTool(new Java()) // - .setProject("standalone") // + .setProject(Projects.STANDALONE) // .addArguments("--show-version") // .addArguments("-enableassertions") // .addArguments("-Djava.util.logging.config.file=logging.properties") // @@ -349,13 +420,19 @@ void execute() throws IOException { .addArguments("--classpath", "bin").build() // .run(false); - assertEquals(1, result.getExitCode(), String.join("\n", result.getOutputLines("out"))); + assertEquals(1, result.getExitCode(), () -> getExitCodeMessage(result)); - var workspace = Request.WORKSPACE.resolve("standalone"); + var workspace = Request.WORKSPACE.resolve(Projects.STANDALONE); var expectedOutLines = Files.readAllLines(workspace.resolve("expected-out.txt")); var expectedErrLines = Files.readAllLines(workspace.resolve("expected-err.txt")); - assertLinesMatch(expectedOutLines, result.getOutputLines("out"), result.getOutput("out")); - assertLinesMatch(expectedErrLines, result.getOutputLines("err"), result.getOutput("err")); + assertLinesMatch(expectedOutLines, result.getOutputLines("out")); + List actualErrLines = result.getOutputLines("err"); + if (actualErrLines.getFirst().contains("stty: /dev/tty: No such device or address")) { + // Happens intermittently on GitHub Actions on Windows + actualErrLines = new ArrayList<>(actualErrLines); + actualErrLines.removeFirst(); + } + assertLinesMatch(expectedErrLines, actualErrLines); var jupiterVersion = Helper.version("junit-jupiter-engine"); var vintageVersion = Helper.version("junit-vintage-engine"); @@ -365,14 +442,16 @@ void execute() throws IOException { + " (group ID: org.junit.vintage, artifact ID: junit-vintage-engine, version: " + vintageVersion)); } + @ResourceLock(Projects.STANDALONE) @Test @Order(4) void executeOnJava8() throws IOException { + Java java8 = getJava8(); var result = Request.builder() // - .setTool(new Java()) // - .setJavaHome(Helper.getJavaHome("8").orElseThrow(TestAbortedException::new)) // - .setProject("standalone") // - .addArguments("--show-version") // + .setTool(java8) // + .setJavaHome(java8.getHome()) // + .setProject(Projects.STANDALONE) // + .addArguments("-showversion") // .addArguments("-enableassertions") // .addArguments("-Djava.util.logging.config.file=logging.properties") // .addArguments("-Djunit.platform.launcher.interceptors.enabled=true") // @@ -384,11 +463,11 @@ void executeOnJava8() throws IOException { .addArguments("--classpath", "bin").build() // .run(false); - assertEquals(1, result.getExitCode(), String.join("\n", result.getOutputLines("out"))); + assertEquals(1, result.getExitCode(), () -> getExitCodeMessage(result)); - var workspace = Request.WORKSPACE.resolve("standalone"); + var workspace = Request.WORKSPACE.resolve(Projects.STANDALONE); var expectedOutLines = Files.readAllLines(workspace.resolve("expected-out.txt")); - var expectedErrLines = Files.readAllLines(workspace.resolve("expected-err.txt")); + var expectedErrLines = getExpectedErrLinesOnJava8(workspace); assertLinesMatch(expectedOutLines, result.getOutputLines("out")); assertLinesMatch(expectedErrLines, result.getOutputLines("err")); @@ -400,31 +479,33 @@ void executeOnJava8() throws IOException { + " (group ID: org.junit.vintage, artifact ID: junit-vintage-engine, version: " + vintageVersion)); } + @ResourceLock(Projects.STANDALONE) @Test @Order(5) // https://github.com/junit-team/junit5/issues/2600 void executeOnJava8SelectPackage() throws IOException { + Java java8 = getJava8(); var result = Request.builder() // - .setTool(new Java()) // - .setJavaHome(Helper.getJavaHome("8").orElseThrow(TestAbortedException::new)) // - .setProject("standalone") // - .addArguments("--show-version") // + .setTool(java8) // + .setJavaHome(java8.getHome()) // + .setProject(Projects.STANDALONE) // + .addArguments("-showversion") // .addArguments("-enableassertions") // .addArguments("-Djava.util.logging.config.file=logging.properties") // .addArguments("-Djunit.platform.launcher.interceptors.enabled=true") // .addArguments("-jar", MavenRepo.jar("junit-platform-console-standalone")) // .addArguments("execute") // - .addArguments("--select-package", "standalone") // + .addArguments("--select-package", Projects.STANDALONE) // .addArguments("--disable-banner") // .addArguments("--include-classname", "standalone.*") // .addArguments("--classpath", "bin").build() // .run(false); - assertEquals(1, result.getExitCode(), String.join("\n", result.getOutputLines("out"))); + assertEquals(1, result.getExitCode(), () -> getExitCodeMessage(result)); - var workspace = Request.WORKSPACE.resolve("standalone"); + var workspace = Request.WORKSPACE.resolve(Projects.STANDALONE); var expectedOutLines = Files.readAllLines(workspace.resolve("expected-out.txt")); - var expectedErrLines = Files.readAllLines(workspace.resolve("expected-err.txt")); + var expectedErrLines = getExpectedErrLinesOnJava8(workspace); assertLinesMatch(expectedOutLines, result.getOutputLines("out")); assertLinesMatch(expectedErrLines, result.getOutputLines("err")); @@ -436,6 +517,14 @@ void executeOnJava8SelectPackage() throws IOException { + " (group ID: org.junit.vintage, artifact ID: junit-vintage-engine, version: " + vintageVersion)); } + private static List getExpectedErrLinesOnJava8(Path workspace) throws IOException { + var expectedErrLines = new ArrayList(); + expectedErrLines.add(">> JAVA VERSION >>"); + expectedErrLines.addAll(Files.readAllLines(workspace.resolve("expected-err.txt"))); + return expectedErrLines; + } + + @ResourceLock(Projects.STANDALONE) @Test @Order(6) @Disabled("https://github.com/junit-team/junit5/issues/1724") @@ -447,7 +536,7 @@ void executeWithJarredTestClasses() { path.add(jar.toString()); var result = Request.builder() // .setTool(new Java()) // - .setProject("standalone") // + .setProject(Projects.STANDALONE) // .addArguments("--show-version") // .addArguments("-enableassertions") // .addArguments("-Djava.util.logging.config.file=logging.properties") // @@ -461,6 +550,29 @@ void executeWithJarredTestClasses() { .build() // .run(false); - assertEquals(1, result.getExitCode(), String.join("\n", result.getOutputLines("out"))); + assertEquals(1, result.getExitCode(), () -> getExitCodeMessage(result)); + } + + private static String getExitCodeMessage(Result result) { + return "Exit codes don't match. Stdout:\n" + result.getOutput("out") + // + "\n\nStderr:\n" + result.getOutput("err") + "\n"; + } + + /** + * Special override of class {@link Java} to resolve against a different {@code JAVA_HOME}. + */ + private static Java getJava8() { + Path java8Home = Helper.getJavaHome("8").orElseThrow(TestAbortedException::new); + return new Java() { + @Override + public Path getHome() { + return java8Home; + } + + @Override + public String getVersion() { + return "8"; + } + }; } } diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ToolProviderTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ToolProviderTests.java index 33243c0a90cd..d99ab0164ab4 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ToolProviderTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ToolProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -35,7 +35,9 @@ import java.util.stream.StreamSupport; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.DisabledOnOpenJ9; import platform.tooling.support.Helper; import platform.tooling.support.MavenRepo; @@ -45,6 +47,7 @@ /** * @since 1.6 */ +@Order(Integer.MAX_VALUE) class ToolProviderTests { private static final Path LIB = Request.WORKSPACE.resolve("tool-provider-tests/lib"); @@ -87,6 +90,7 @@ void findAndRunJUnitOnTheClassPath() { } @Test + @DisabledOnOpenJ9 void findAndRunJUnitOnTheModulePath() { var finder = ModuleFinder.of(LIB); var modules = finder.findAll().stream() // diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/VintageGradleIntegrationTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/VintageGradleIntegrationTests.java index 2a1fad0ed689..b655753eaa10 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/VintageGradleIntegrationTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/VintageGradleIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -58,9 +58,9 @@ private Result run(String version) { var result = Request.builder() // .setTool(new GradleWrapper(Paths.get(".."))) // .setJavaHome(Helper.getJavaHome("8").orElseThrow(TestAbortedException::new)) // - .setProject("vintage") // + .setProject(Projects.VINTAGE) // .setWorkspace("vintage-gradle-" + version) // - .addArguments("build", "--no-daemon", "--stacktrace") // + .addArguments("build", "--no-daemon", "--stacktrace", "--no-build-cache") // .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // .addArguments("-Djunit4Version=" + version) // .setTimeout(TOOL_TIMEOUT) // diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/VintageMavenIntegrationTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/VintageMavenIntegrationTests.java index 624e239cf49a..3358881d600f 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/VintageMavenIntegrationTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/VintageMavenIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which @@ -57,7 +57,7 @@ private Result run(String version) { var result = Request.builder() // .setTool(Request.maven()) // .setJavaHome(Helper.getJavaHome("8").orElseThrow(TestAbortedException::new)) // - .setProject("vintage") // + .setProject(Projects.VINTAGE) // .setWorkspace("vintage-maven-" + version) // .addArguments("clean", "test", "--update-snapshots", "--batch-mode") // .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/XmlAssertions.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/XmlAssertions.java index 312cdacafcdd..b233d6c0e9eb 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/XmlAssertions.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/XmlAssertions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the original author or authors. + * Copyright 2015-2024 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v2.0 which diff --git a/platform-tooling-support-tests/src/test/resources/junit-platform.properties b/platform-tooling-support-tests/src/test/resources/junit-platform.properties new file mode 100644 index 000000000000..1ddeedc6a798 --- /dev/null +++ b/platform-tooling-support-tests/src/test/resources/junit-platform.properties @@ -0,0 +1,7 @@ +junit.jupiter.execution.parallel.enabled=true +junit.jupiter.execution.parallel.mode.default=concurrent +junit.jupiter.execution.parallel.config.strategy=dynamic +junit.jupiter.execution.parallel.config.dynamic.factor=0.25 + +junit.jupiter.testclass.order.default = \ + org.junit.jupiter.api.ClassOrderer$OrderAnnotation diff --git a/settings.gradle.kts b/settings.gradle.kts index 8ae205fbcbeb..9f5eba43d998 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,4 @@ import buildparameters.BuildParametersExtension -import com.gradle.enterprise.gradleplugin.internal.extension.BuildScanExtensionWithHiddenFeatures pluginManagement { includeBuild("gradle/plugins") @@ -25,20 +24,19 @@ dependencyResolutionManagement { } val buildParameters = the() -val gradleEnterpriseServer = "https://ge.junit.org" +val develocityServer = "https://ge.junit.org" +val useDevelocityInstance = !gradle.startParameter.isBuildScan -gradleEnterprise { +develocity { + if (useDevelocityInstance) { + // Publish to scans.gradle.com when `--scan` is used explicitly + server = develocityServer + } buildScan { - capture.isTaskInputFiles = true - isUploadInBackground = !buildParameters.ci - - publishAlways() + uploadInBackground = !buildParameters.ci - // Publish to scans.gradle.com when `--scan` is used explicitly - if (!gradle.startParameter.isBuildScan) { - server = gradleEnterpriseServer - this as BuildScanExtensionWithHiddenFeatures - publishIfAuthenticated() + publishing { + onlyIf { it.isAuthenticated } } obfuscation { @@ -50,7 +48,7 @@ gradleEnterprise { } } - if (buildParameters.enterprise.testDistribution.enabled) { + if (buildParameters.junit.develocity.testDistribution.enabled) { tag("test-distribution") } } @@ -60,18 +58,22 @@ buildCache { local { isEnabled = !buildParameters.ci } - remote { - url = uri(buildParameters.buildCache.url.getOrElse("$gradleEnterpriseServer/cache/")) - val buildCacheUsername = buildParameters.buildCache.username.orNull?.ifBlank { null } - val buildCachePassword = buildParameters.buildCache.password.orNull?.ifBlank { null } - isPush = buildParameters.ci && buildCacheUsername != null && buildCachePassword != null - credentials { - username = buildCacheUsername - password = buildCachePassword + val buildCacheServer = buildParameters.junit.develocity.buildCache.server + if (useDevelocityInstance) { + remote(develocity.buildCache) { + server = buildCacheServer.orNull + val authenticated = System.getenv("DEVELOCITY_ACCESS_KEY") != null + isPush = buildParameters.ci && authenticated + } + } else { + remote { + url = uri(buildCacheServer.getOrElse(develocityServer)).resolve("/cache/") } } } +includeBuild("gradle/base") + rootProject.name = "junit5" include("documentation") @@ -94,6 +96,7 @@ include("junit-platform-suite-commons") include("junit-platform-suite-engine") include("junit-platform-testkit") include("junit-vintage-engine") +include("jupiter-tests") include("platform-tests") include("platform-tooling-support-tests") include("junit-bom")