diff --git a/.codecov.yml b/.codecov.yml
index d724eb3c1d0b..04dae7644042 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -1,6 +1,8 @@
+comment: false
coverage:
status:
project:
default:
threshold: 1
+ informational: true
patch: off
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000000..2a4ad4ec1b55
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,6 @@
+[*.{kt,kts}]
+ij_kotlin_allow_trailing_comma = false
+ij_kotlin_allow_trailing_comma_on_call_site = false
+
+[*.kts]
+indent_style = tab
diff --git a/.gitattributes b/.gitattributes
index 252299c5609a..d836e4264a62 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -4,3 +4,4 @@
*.key binary
*.jar binary
*.ttf binary
+release-notes-* merge=union
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 1bdc52cd3615..90d0d4bdaed2 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -16,4 +16,3 @@ I hereby agree to the terms of the [JUnit Contributor License Agreement](https:/
- [ ] Change is covered by [automated tests](https://github.com/junit-team/junit5/blob/HEAD/CONTRIBUTING.md#tests) including corner cases, errors, and exception handling
- [ ] Public API has [Javadoc](https://github.com/junit-team/junit5/blob/HEAD/CONTRIBUTING.md#javadoc) and [`@API` annotations](https://apiguardian-team.github.io/apiguardian/docs/current/api/org/apiguardian/api/API.html)
- [ ] Change is documented in the [User Guide](https://junit.org/junit5/docs/snapshot/user-guide/) and [Release Notes](https://junit.org/junit5/docs/snapshot/user-guide/#release-notes)
-- [ ] All [continuous integration builds](https://github.com/junit-team/junit5#continuous-integration-builds) pass
diff --git a/.github/actions/main-build/action.yml b/.github/actions/main-build/action.yml
new file mode 100644
index 000000000000..a7817cd314f0
--- /dev/null
+++ b/.github/actions/main-build/action.yml
@@ -0,0 +1,19 @@
+name: Main build
+description: Sets up JDKs and runs Gradle
+inputs:
+ arguments:
+ required: true
+ description: Gradle arguments
+ default: build
+runs:
+ using: "composite"
+ steps:
+ - uses: ./.github/actions/setup-test-jdk
+ - 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
new file mode 100644
index 000000000000..a4b1bc129f6c
--- /dev/null
+++ b/.github/actions/run-gradle/action.yml
@@ -0,0 +1,25 @@
+name: Run Gradle
+description: Sets up Gradle JDKs and runs Gradle
+inputs:
+ arguments:
+ required: true
+ description: Gradle arguments
+ default: build
+runs:
+ using: "composite"
+ steps:
+ - uses: actions/setup-java@v3
+ id: setup-gradle-jdk
+ with:
+ distribution: temurin
+ java-version: 17
+ - uses: gradle/gradle-build-action@v2
+ 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 }}
diff --git a/.github/actions/setup-test-jdk/action.yml b/.github/actions/setup-test-jdk/action.yml
new file mode 100644
index 000000000000..4e8c96266c69
--- /dev/null
+++ b/.github/actions/setup-test-jdk/action.yml
@@ -0,0 +1,11 @@
+name: Set up Test JDK
+description: Sets up the JDK required to run platform-tooling-support-tests
+runs:
+ using: "composite"
+ steps:
+ - uses: actions/setup-java@v3
+ with:
+ distribution: temurin
+ java-version: 8
+ - shell: bash
+ run: echo "JDK8=$JAVA_HOME" >> $GITHUB_ENV
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000000..5c19f255d870
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,35 @@
+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
new file mode 100644
index 000000000000..ab8bb97a6f28
--- /dev/null
+++ b/.github/workflows/close-inactive-issues.yml
@@ -0,0 +1,31 @@
+name: Close inactive issues and PRs
+on:
+ schedule:
+ - cron: "30 1 * * *"
+ workflow_dispatch:
+jobs:
+ close-issues:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ steps:
+ - uses: actions/stale@v8
+ with:
+ only-labels: "status: waiting-for-feedback"
+ days-before-stale: 14
+ days-before-close: 21
+ stale-issue-label: "status: stale"
+ stale-pr-label: "status: stale"
+ stale-issue-message: >
+ If you would like us to be able to process this issue, please provide the requested information.
+ If the information is not provided within the next 3 weeks, we will be unable to proceed and this issue will be closed.
+ close-issue-message: >
+ Closing due to lack of requested feedback.
+ If you would like to proceed with your contribution, please provide the requested information and we will re-open this issue.
+ stale-pr-message: >
+ If you would like us to be able to process this pull request, please provide the requested information or make the requested changes.
+ If the information is not provided or the requested changes are not made within the next 3 weeks, we will be unable to proceed and this pull request will be closed.
+ close-pr-message: >
+ Closing due to lack of requested feedback.
+ If you would like to proceed with your contribution, please provide the requested information or make the requested changes, and we will re-open this pull request.
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 000000000000..63b648c7b424
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,47 @@
+name: "CodeQL"
+
+on:
+ push:
+ branches:
+ - main
+ - 'releases/**'
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches:
+ - main
+ - 'releases/**'
+ schedule:
+ - cron: '0 19 * * 3'
+
+env:
+ GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ matrix:
+ language:
+ - java
+ - javascript
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v3
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+ tools: latest
+ - name: Build
+ uses: ./.github/actions/run-gradle
+ with:
+ arguments: |
+ --no-build-cache
+ -Dscan.tag.CodeQL
+ allMainClasses
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/combine-prs.yml b/.github/workflows/combine-prs.yml
new file mode 100644
index 000000000000..826472911980
--- /dev/null
+++ b/.github/workflows/combine-prs.yml
@@ -0,0 +1,16 @@
+name: Combine PRs
+
+on:
+ schedule:
+ - cron: '0 0 * * *' # Every day at 00:00 UTC
+ workflow_dispatch:
+
+jobs:
+ combine-prs:
+ if: github.repository == 'junit-team/junit5'
+ runs-on: ubuntu-latest
+ steps:
+ - name: combine-prs
+ uses: github.amrom.workers.devbine-prs@v3.1.1
+ with:
+ github_token: ${{ secrets.GH_TOKEN }}
diff --git a/.github/workflows/cross-version.yml b/.github/workflows/cross-version.yml
index 05e5a3d5c08e..8480a68a49c7 100644
--- a/.github/workflows/cross-version.yml
+++ b/.github/workflows/cross-version.yml
@@ -4,46 +4,50 @@ on:
push:
branches:
- main
- - 'releases/*'
+ - 'releases/**'
pull_request:
branches:
- '*'
env:
- ORG_GRADLE_PROJECT_junitBuildCacheUsername: ${{ secrets.BUILD_CACHE_USERNAME }}
- ORG_GRADLE_PROJECT_junitBuildCachePassword: ${{ secrets.BUILD_CACHE_PASSWORD }}
+ 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 }}
jobs:
openjdk:
strategy:
+ fail-fast: false
matrix:
- jdk: [14, 15, 16]
+ jdk: [20, 21, 22]
name: "OpenJDK ${{ matrix.jdk }}"
runs-on: ubuntu-latest
- container: "junitteam/build:${{ matrix.jdk }}"
steps:
- - uses: actions/checkout@v2
- - name: Cache Gradle wrapper and dependencies
- uses: actions/cache@v2
+ - name: Check out repository
+ uses: actions/checkout@v3
with:
- path: |
- /root/.gradle/caches/
- /root/.gradle/wrapper/dists
- key: test-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/*.gradle.kts', '**/gradle.properties', 'gradle/**', 'buildSrc/src/main/**') }}
- restore-keys: |
- test-${{ runner.os }}-gradle-
- - name: Cache local Maven repository
- uses: actions/cache@v2
+ 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
with:
- path: /root/.m2/repository
- key: test-${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- test-${{ runner.os }}-maven-
- - name: Prepare Gradle Enterprise credentials
- run: |
- mkdir -p /root/.gradle/enterprise/
- echo "${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}" > /root/.gradle/enterprise/keys.properties
- - name: Test
- run: |
- ./gradlew --version
- ./gradlew --scan --no-parallel --warning-mode=all -Dplatform.tooling.support.tests.enabled=true -PjavaHome=$ADDITIONAL_JDK build "-Dscan.tag.JDK_${{ matrix.jdk }}"
+ website: jdk.java.net
+ release: ${{ matrix.jdk }}
+ version: latest
+ - name: 'Prepare JDK${{ matrix.jdk }} env var'
+ shell: bash
+ run: echo "JDK${{ matrix.jdk }}=$JAVA_HOME" >> $GITHUB_ENV
+ - name: Build
+ 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'
diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml
index 405a2b306592..de5d0346a9f6 100644
--- a/.github/workflows/gradle-wrapper-validation.yml
+++ b/.github/workflows/gradle-wrapper-validation.yml
@@ -1,10 +1,22 @@
name: "Validate Gradle Wrapper"
-on: [push, pull_request]
+
+on:
+ push:
+ branches:
+ - main
+ - 'releases/**'
+ pull_request:
+ branches:
+ - '*'
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: gradle/wrapper-validation-action@v1
+ - 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
new file mode 100644
index 000000000000..763dfe889565
--- /dev/null
+++ b/.github/workflows/issue-labels.yml
@@ -0,0 +1,20 @@
+name: Label new issues
+on:
+ issues:
+ types:
+ - opened
+jobs:
+ label_issues:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ steps:
+ - uses: actions/github-script@v6
+ with:
+ script: |
+ github.rest.issues.addLabels({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ labels: ["status: new"]
+ })
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 2a4b5da0d7e0..676ec1a2f87a 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -4,159 +4,66 @@ on:
push:
branches:
- main
- - 'releases/*'
+ - 'releases/**'
pull_request:
branches:
- '*'
env:
- ORG_GRADLE_PROJECT_junitBuildCacheUsername: ${{ secrets.BUILD_CACHE_USERNAME }}
- ORG_GRADLE_PROJECT_junitBuildCachePassword: ${{ secrets.BUILD_CACHE_PASSWORD }}
+ 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 }}
jobs:
- linux:
- name: 'Linux'
+ Linux:
runs-on: ubuntu-latest
- container: junitteam/build:latest
steps:
- - uses: actions/checkout@v2
- - name: Cache Gradle wrapper and dependencies
- uses: actions/cache@v2
+ - name: Check out repository
+ uses: actions/checkout@v3
with:
- path: |
- /root/.gradle/caches/
- /root/.gradle/wrapper/dists
- key: test-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/*.gradle.kts', '**/gradle.properties', 'gradle/**', 'buildSrc/src/main/**') }}
- restore-keys: |
- test-${{ runner.os }}-gradle-
- - name: Cache local Maven repository
- uses: actions/cache@v2
- with:
- path: /root/.m2/repository
- key: test-${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- test-${{ runner.os }}-maven-
- - name: Prepare Gradle Enterprise credentials
- run: |
- mkdir -p /root/.gradle/enterprise/
- echo "${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}" > /root/.gradle/enterprise/keys.properties
- - name: 'Test'
+ fetch-depth: 1
+ - name: Install Graphviz
run: |
- ./gradlew --version
- ./gradlew --scan --no-parallel --warning-mode=all -Dplatform.tooling.support.tests.enabled=true build
-
- windows:
- name: 'Windows'
- runs-on: windows-latest
- steps:
- - uses: actions/checkout@v2
- - name: Cache Gradle wrapper and dependencies
- uses: actions/cache@v2
- with:
- path: |
- ~/.gradle/caches/
- ~/.gradle/wrapper/dists
- key: test-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/*.gradle.kts', '**/gradle.properties', 'gradle/**', 'buildSrc/src/main/**') }}
- restore-keys: |
- test-${{ runner.os }}-gradle-
- - name: Cache local Maven repository
- uses: actions/cache@v2
+ sudo apt-get update
+ sudo apt-get install graphviz
+ - name: Install GraalVM
+ uses: graalvm/setup-graalvm@v1
with:
- path: ~/.m2/repository
- key: test-${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- test-${{ runner.os }}-maven-
- - name: 'Set up JDK 11'
- uses: actions/setup-java@v1
+ version: 'latest'
+ java-version: '17'
+ components: 'native-image'
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ - name: Build
+ uses: ./.github/actions/main-build
with:
- java-version: 11
- - name: Prepare Gradle Enterprise credentials
- shell: bash
- run: |
- mkdir -p $HOME/.gradle/enterprise/
- echo "${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}" > $HOME/.gradle/enterprise/keys.properties
- - name: 'Test'
- shell: bash
- run: |
- ./gradlew --version
- ./gradlew --scan --no-parallel --warning-mode=all -Dplatform.tooling.support.tests.enabled=true build
- ./gradlew --stop
- mac:
- name: 'Mac OS'
- runs-on: macos-latest
+ arguments: |
+ -Ptesting.enableJaCoCo
+ build
+ jacocoRootReport
+ prepareDocsForUploadToGhPages
+ - name: Upload to Codecov.io
+ uses: codecov/codecov-action@v3
+
+ Windows:
+ runs-on: windows-latest
steps:
- - uses: actions/checkout@v2
- - name: Cache Gradle wrapper and dependencies
- uses: actions/cache@v2
+ - name: Check out repository
+ uses: actions/checkout@v3
with:
- path: |
- ~/.gradle/caches/
- ~/.gradle/wrapper/dists
- key: test-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/*.gradle.kts', '**/gradle.properties', 'gradle/**', 'buildSrc/src/main/**') }}
- restore-keys: |
- test-${{ runner.os }}-gradle-
- - name: Cache local Maven repository
- uses: actions/cache@v2
- with:
- path: ~/.m2/repository
- key: test-${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- test-${{ runner.os }}-maven-
- - name: 'Set up JDK 11'
- uses: actions/setup-java@v1
- with:
- java-version: 11
- - name: Prepare Gradle Enterprise credentials
- run: |
- mkdir -p $HOME/.gradle/enterprise/
- echo "${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}" > $HOME/.gradle/enterprise/keys.properties
- - name: 'Test'
- run: |
- ./gradlew --version
- ./gradlew --scan --no-parallel --warning-mode=all -Dplatform.tooling.support.tests.enabled=true build
+ fetch-depth: 1
+ - name: Build
+ uses: ./.github/actions/main-build
- coverage:
- name: 'Coverage'
- needs: linux
- runs-on: ubuntu-latest
- container: junitteam/build:latest
+ macOS:
+ runs-on: macos-latest
steps:
- - uses: actions/checkout@v2
- - name: Cache Gradle wrapper and dependencies
- uses: actions/cache@v2
- with:
- path: |
- /root/.gradle/caches/
- /root/.gradle/wrapper/dists
- key: coverage-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/*.gradle.kts', '**/gradle.properties', 'gradle/**', 'buildSrc/src/main/**') }}
- restore-keys: |
- coverage-${{ runner.os }}-gradle-
- - name: Cache local Maven repository
- uses: actions/cache@v2
- with:
- path: /root/.m2/repository
- key: coverage-${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- coverage-${{ runner.os }}-maven-
- - name: 'Set up JDK 11'
- uses: actions/setup-java@v1
+ - name: Check out repository
+ uses: actions/checkout@v3
with:
- java-version: 11
- - name: Prepare Gradle Enterprise credentials
- run: |
- mkdir -p /root/.gradle/enterprise/
- echo "${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}" > /root/.gradle/enterprise/keys.properties
- - name: 'Run tests with JaCoCo'
- shell: bash
- run: |
- ./gradlew --version
- ./gradlew --scan --no-parallel --stacktrace --warning-mode=all -PenableJaCoCo build jacocoRootReport
- - name: Upload to Codecov.io
- shell: bash
- env:
- CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- run: |
- bash <(curl -s https://codecov.io/bash)
+ fetch-depth: 1
+ - name: Build
+ uses: ./.github/actions/main-build
publish_artifacts:
name: Publish Snapshot Artifacts
@@ -164,51 +71,40 @@ jobs:
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.repository == 'junit-team/junit5' && (startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/main')
steps:
- - uses: actions/checkout@v2
- - name: Cache Gradle wrapper and dependencies
- uses: actions/cache@v2
- with:
- path: |
- ~/.gradle/caches/
- ~/.gradle/wrapper/dists
- key: assemble-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/*.gradle.kts', '**/gradle.properties', 'gradle/**', 'buildSrc/src/main/**') }}
- restore-keys: |
- assemble-${{ runner.os }}-gradle-
- - name: 'Set up JDK 11'
- uses: actions/setup-java@v1
+ - name: Check out repository
+ uses: actions/checkout@v3
with:
- java-version: 11
- - name: 'Publish'
+ fetch-depth: 1
+ - name: Publish
+ uses: ./.github/actions/run-gradle
env:
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
- GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
- run: ./gradlew --scan publish -x check
+ with:
+ arguments: publish -x check
update_documentation:
name: Update Snapshot Documentation
- needs: linux
+ 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:
- - uses: actions/checkout@v2
- - name: Cache Gradle wrapper and dependencies
- uses: actions/cache@v2
+ - name: Check out repository
+ uses: actions/checkout@v3
with:
- path: |
- ~/.gradle/caches/
- ~/.gradle/wrapper/dists
- key: assemble-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/*.gradle.kts', '**/gradle.properties', 'gradle/**', 'buildSrc/src/main/**') }}
- restore-keys: |
- assemble-${{ runner.os }}-gradle-
- - name: 'Set up JDK 11'
- uses: actions/setup-java@v1
+ fetch-depth: 1
+ - name: Install Graphviz
+ run: |
+ sudo apt-get update
+ sudo apt-get install graphviz
+ - name: Restore Gradle cache and display toolchains
+ uses: ./.github/actions/run-gradle
with:
- java-version: 11
- - name: 'Upload Documentation'
+ arguments: --quiet
+ - name: Upload Documentation
env:
GRGIT_USER: ${{ secrets.GH_TOKEN }}
- GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
- run: |
- sudo apt-get install graphviz
- ./src/publishDocumentationSnapshotOnlyIfNecessary.sh
+ run: ./gradle/scripts/publishDocumentationSnapshotOnlyIfNecessary.sh
diff --git a/.github/workflows/reproducible-build.yml b/.github/workflows/reproducible-build.yml
index e48b0c5ff1b2..a32a51ac0002 100644
--- a/.github/workflows/reproducible-build.yml
+++ b/.github/workflows/reproducible-build.yml
@@ -3,37 +3,29 @@ name: Reproducible build
on:
push:
branches:
- - main
- - 'releases/*'
+ - main
+ - 'releases/**'
pull_request:
branches:
- - '*'
+ - '*'
+
+env:
+ GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
jobs:
check_build_reproducibility:
name: 'Check build reproducibility'
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - name: Cache Gradle wrapper and dependencies
- uses: actions/cache@v2
+ - name: Check out repository
+ uses: actions/checkout@v3
with:
- path: |
- ~/.gradle/caches/
- ~/.gradle/wrapper/dists
- key: assemble-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/*.gradle.kts', '**/gradle.properties', 'gradle/**', 'buildSrc/src/main/**') }}
- restore-keys: |
- assemble-${{ runner.os }}-gradle-
- - name: 'Set up JDK 11'
- uses: actions/setup-java@v1
+ fetch-depth: 1
+ - name: Restore Gradle cache and display toolchains
+ uses: ./.github/actions/run-gradle
with:
- java-version: 11
- - name: Prepare Gradle Enterprise credentials
- shell: bash
- run: |
- mkdir -p $HOME/.gradle/enterprise/
- echo "${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}" > $HOME/.gradle/enterprise/keys.properties
+ arguments: --quiet
- name: Build and compare checksums
shell: bash
run: |
- ./src/checkBuildReproducibility.sh
+ ./gradle/scripts/checkBuildReproducibility.sh
diff --git a/.gitignore b/.gitignore
index edd18cb0c5e2..6d276115b4be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
# Gradle
.gradle
-/build/
-/*/build/
+build
# Ignore Gradle GUI config
gradle-app.setting
@@ -29,5 +28,6 @@ gradle-app.setting
*.graphml
coverage.db*
.metadata
+/.sdkmanrc
checksums*
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index f77f7d2313e4..178ac9238035 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,5 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.jitpack.yml b/.jitpack.yml
deleted file mode 100644
index 72eb09e6aa7f..000000000000
--- a/.jitpack.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-install:
- - wget https://github.com/sormuras/bach/raw/HEAD/install-jdk.sh
- - source ./install-jdk.sh --feature 11
- - ./gradlew --version
- - ./gradlew publishToMavenLocal -x test
diff --git a/.lgtm.yml b/.lgtm.yml
deleted file mode 100644
index eb5f4c9b7a25..000000000000
--- a/.lgtm.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-extraction:
- java:
- index:
- java_version: 11
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 18e6158b17ac..2313cecda3ff 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -28,10 +28,10 @@ Issue: #999
## Pull Requests
Our [Definition of Done](https://github.com/junit-team/junit5/wiki/Definition-of-Done)
-offers some guidelines on what we expect from a pull request.
+(DoD) offers some guidelines on what we expect from a pull request.
Feel free to open a pull request that does not fulfill all criteria, e.g. to discuss
a certain change before polishing it, but please be aware that we will only merge it
-in case the DoD is met.
+once the DoD is met.
Please add the following lines to your pull request description:
@@ -71,8 +71,8 @@ Code formatting is enforced using the [Spotless](https://github.com/diffplug/spo
Gradle plugin. You can use `gradle spotlessApply` to format new code and add missing
license headers to source files. Formatter and import order settings for Eclipse are
available in the repository under
-[src/eclipse/junit-eclipse-formatter-settings.xml](src/eclipse/junit-eclipse-formatter-settings.xml)
-and [src/eclipse/junit-eclipse.importorder](src/eclipse/junit-eclipse.importorder),
+[junit-eclipse-formatter-settings.xml](gradle/config/eclipse/junit-eclipse-formatter-settings.xml)
+and [junit-eclipse.importorder](gradle/config/eclipse/junit-eclipse.importorder),
respectively. For IntelliJ IDEA there's a
[plugin](https://plugins.jetbrains.com/plugin/6546) you can use in conjunction with the
Eclipse settings.
@@ -87,19 +87,23 @@ possible.
In multi-line bullet point entries, subsequent lines should be indented.
+### Spelling
+
+Use American English spelling rules when writing documentation as well as for
+code -- class names, method names, variable names, etc.
+
### Javadoc
- Javadoc comments should be wrapped after 80 characters whenever possible.
-- This first paragraph must be a single, concise sentence that ends with a period (".").
-- Place `
` on the same line as the first line in a new paragraph and precede `
` with a blank line.
+- This first paragraph must be a single, concise sentence that ends with a period (`.`).
+- Place `
` on the same line as the first line of a new paragraph and precede `
` with a blank line.
- Insert a blank line before at-clauses/tags.
- Favor `{@code foo}` over `foo`.
- Favor literals (e.g., `{@literal @}`) over HTML entities.
-- New classes and methods should have `@since ...` annotation.
-- Use `@since 5.0` instead of `@since 5.0.0`.
-- Do not use `@author` tags. Instead, contributors are listed on [GitHub](https://github.com/junit-team/junit5/graphs/contributors).
-- Do not use verbs in third person form (e.g. use "Discover tests..." instead of "Discovers tests...")
- in the first sentence describing a method.
+- New classes and methods should declare a `@since ...` tag.
+- Use `@since 5.10` instead of `@since 5.10.0`.
+- Do not use `@author` tags. Instead, contributors are listed on the [GitHub](https://github.com/junit-team/junit5/graphs/contributors) page.
+- Do not use verbs in third-person form in the first sentence of the Javadoc for a method -- for example, use "Discover tests..." instead of "Discovers tests...".
#### Examples
@@ -116,19 +120,19 @@ See [`ExtensionContext`](junit-jupiter-api/src/main/java/org/junit/jupiter/api/e
#### Assertions
-- Use `org.junit.jupiter.api.Assertions` wherever possible.
+- Use `org.junit.jupiter.api.Assertions` for simple assertions.
- Use AssertJ when richer assertions are needed.
- Do not use `org.junit.Assert` or `junit.framework.Assert`.
-#### Mocking
+#### Mocking and Stubbing
- Use either [Mockito](https://github.com/mockito/mockito) or hand-written test doubles.
### Logging
- In general, logging should be used sparingly.
-- All logging must be performed via the internal `Logger` façade provided via the JUnit [LoggerFactory](https://junit.org/junit5/docs/current/api/org/junit/platform/commons/logging/LoggerFactory.html).
-- Levels defined in JUnit's [Logger](https://junit.org/junit5/docs/current/api/org/junit/platform/commons/logging/Logger.html) façade.
+- All logging must be performed via the internal `Logger` façade provided via the JUnit [LoggerFactory](https://github.com/junit-team/junit5/blob/main/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LoggerFactory.java).
+- Levels defined in JUnit's [Logger](https://github.com/junit-team/junit5/blob/main/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/Logger.java) façade, which delegates to Java Util Logging (JUL) for the actual logging.
- _error_ (JUL: `SEVERE`, Log4J: `ERROR`): extra information (in addition to an Exception) about errors that will halt execution
- _warn_ (JUL: `WARNING`, Log4J: `WARN`): potential usage or configuration errors that should not halt execution
- _info_ (JUL: `INFO`, Log4J: `INFO`): information the users might want to know but not by default
@@ -138,10 +142,11 @@ See [`ExtensionContext`](junit-jupiter-api/src/main/java/org/junit/jupiter/api/e
### Deprecation
-Publicly available interfaces, classes and methods have a defined lifecycle
+The JUnit 5 project uses the `@API` annotation from [API Guardian](https://github.com/apiguardian-team/apiguardian).
+Publicly available interfaces, classes, and methods have a defined lifecycle
which is described in detail in the [User Guide](https://junit.org/junit5/docs/current/user-guide/#api-evolution).
-This process is using the `@API` annotation from [API Guardian](https://github.com/apiguardian-team/apiguardian).
-It also describes the deprecation process followed for API items.
+
+That following describes the deprecation process followed for API items.
To deprecate an item:
- Update the `@API.status` to `DEPRECATED`.
diff --git a/README.md b/README.md
index 8b6a8761c39d..43f900ade772 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
#
JUnit 5
-This repository is the home of the next generation of JUnit, _JUnit 5_.
+This repository is the home of _JUnit 5_.
[](https://junit.org/sponsoring)
## Latest Releases
-- General Availability (GA): [JUnit 5.7.0](https://github.com/junit-team/junit5/releases/tag/r5.7.0) (September 13, 2020)
-- Preview (Milestone/Release Candidate): n/a
+- General Availability (GA): [JUnit 5.10.0](https://github.com/junit-team/junit5/releases/tag/r5.10.0) (July 23, 2023)
+- Preview (Milestone/Release Candidate): N/A
## Documentation
@@ -28,7 +28,7 @@ label are specifically targeted for community contributions.
## Getting Help
-Ask JUnit 5 related questions on [StackOverflow] or chat with the team and the community on [Gitter].
+Ask JUnit 5 related questions on [StackOverflow] or chat with the community on [Gitter].
## Continuous Integration Builds
@@ -43,95 +43,62 @@ builds of the next OpenJDK.
Code coverage using [JaCoCo] for the latest build is available on [Codecov].
A code coverage report can also be generated locally via the [Gradle Wrapper] by
-executing `gradlew -PenableJaCoCo clean jacocoRootReport`. The results will be available
+executing `./gradlew -Ptesting.enableJaCoCo clean jacocoRootReport`. The results will be available
in `build/reports/jacoco/jacocoRootReport/html/index.html`.
-## Gradle Build Scans and Build Caching
+## Gradle Enterprise
-JUnit 5 utilizes [Gradle Enterprise](https://gradle.com/) for _Build Scans_ and the
-_Remote Build Cache_. An example build scan for JUnit 5 can be viewed
-[here](https://ge.junit.org/s/2vwrn4rn67dky). Currently, only core team members can
-publish build scans. The remote build cache, however, is enabled by default for everyone
-so that local builds can reuse task outputs from previous CI builds.
+[](https://ge.junit.org/scans)
-## Building from Source
+JUnit 5 utilizes [Gradle Enterprise](https://gradle.com/) for _Build Scans_, _Build Cache_, and _Test Distribution_.
-You need [JDK 11] to build JUnit 5.
+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.
+You can, however, publish a Build Scan to [scans.gradle.com](https://scans.gradle.com/) by
+using the `--scan` parameter explicitly.
-All modules can be _built_ with the [Gradle Wrapper] using the following command.
+The remote Build Cache is enabled by default for everyone so that local builds can reuse
+task outputs from previous CI builds.
-`gradlew clean assemble`
+## Building from Source
-All modules can be _tested_ with the [Gradle Wrapper] using the following command.
+You need [JDK 17] to build JUnit 5. [Gradle toolchains] are used to detect and
+potentially download additional JDKs for compilation and test execution.
-`gradlew clean test`
+All modules can be _built_ and _tested_ with the [Gradle Wrapper] using the following command.
-Since Gradle has excellent incremental build support, you can usually omit executing the
-`clean` task.
+`./gradlew build`
## Installing in Local Maven Repository
All modules can be _installed_ with the [Gradle Wrapper] in a local Maven repository for
consumption in other projects via the following command.
-`gradlew clean publishToMavenLocal`
+`./gradlew publishToMavenLocal`
## Dependency Metadata
-The following sections list the dependency metadata for the JUnit Platform, JUnit
-Jupiter, and JUnit Vintage.
-
-See also for releases and for snapshots.
-
-### JUnit Platform
-
-- **Group ID**: `org.junit.platform`
-- **Version**: `1.7.0` or `1.8.0-SNAPSHOT`
-- **Artifact IDs** and Java **module** name:
- - `junit-platform-commons` (`org.junit.platform.commons`)
- - `junit-platform-console` (`org.junit.platform.console`)
- - `junit-platform-console-standalone` (*N/A*)
- - `junit-platform-engine` (`org.junit.platform.engine`)
- - `junit-platform-jfr` (`org.junit.platform.jfr`)
- - `junit-platform-launcher` (`org.junit.platform.launcher`)
- - `junit-platform-reporting` (`org.junit.platform.reporting`)
- - `junit-platform-runner` (`org.junit.platform.runner`)
- - `junit-platform-suite-api` (`org.junit.platform.suite.api`)
- - `junit-platform-testkit` (`org.junit.platform.testkit`)
-
-### JUnit Jupiter
+[](https://central.sonatype.com/search?namespace=org.junit.jupiter)
+[](https://central.sonatype.com/search?namespace=org.junit.vintage)
+[](https://central.sonatype.com/search?namespace=org.junit.platform)
-- **Group ID**: `org.junit.jupiter`
-- **Version**: `5.7.0` or `5.8.0-SNAPSHOT`
-- **Artifact IDs** and Java **module** name:
- - `junit-jupiter` (`org.junit.jupiter`)
- - `junit-jupiter-api` (`org.junit.jupiter.api`)
- - `junit-jupiter-engine` (`org.junit.jupiter.engine`)
- - `junit-jupiter-migrationsupport` (`org.junit.jupiter.migrationsupport`)
- - `junit-jupiter-params` (`org.junit.jupiter.params`)
+Consult the [Dependency Metadata] section of the [User Guide] for a list of all artifacts
+of the JUnit Platform, JUnit Jupiter, and JUnit Vintage.
-### JUnit Vintage
-
-- **Group ID**: `org.junit.vintage`
-- **Version**: `5.7.0` or `5.8.0-SNAPSHOT`
-- **Artifact ID** and Java **module** name:
- - `junit-vintage-engine` (`org.junit.vintage.engine`)
-
-### Bill of Materials (BOM)
-
-- **Group ID**: `org.junit`
-- **Artifact ID** `junit-bom`
-- **Version**: `5.7.0` or `5.8.0-SNAPSHOT`
+See also for releases and
+ for snapshots.
[Codecov]: https://codecov.io/gh/junit-team/junit5
[CONTRIBUTING.md]: https://github.com/junit-team/junit5/blob/HEAD/CONTRIBUTING.md
+[Dependency Metadata]: https://junit.org/junit5/docs/current/user-guide/#dependency-metadata
[Gitter]: https://gitter.im/junit-team/junit5
+[Gradle toolchains]: https://docs.gradle.org/current/userguide/toolchains.html
[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 11]: https://jdk.java.net/11/
+[JDK 17]: https://foojay.io/almanac/java-17/
[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
[User Guide]: https://junit.org/junit5/docs/current/user-guide/
-[Samples]: https://github.com/junit-team/junit5-samples
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 000000000000..fca52da512fa
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,12 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported |
+| ------- | ------------------ |
+| 5.9.x | :white_check_mark: |
+| < 5.9 | :x: |
+
+## Reporting a Vulnerability
+
+To report a security vulnerability, please send an email to security@junit.org.
diff --git a/build.gradle.kts b/build.gradle.kts
index 41cba6dc5c6b..048d466a4546 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,262 +1,69 @@
-import java.time.OffsetDateTime
-import java.time.Instant
-import java.time.ZoneOffset
-import java.time.format.DateTimeFormatter
-
plugins {
- id("net.nemerosa.versioning")
- id("com.github.ben-manes.versions") // gradle dependencyUpdates
- id("com.diffplug.spotless")
- id("io.spring.nohttp")
-}
-
-apply(from = "gradle/build-scan-user-data.gradle")
-
-buildScan {
- if (project.hasProperty("javaHome")) {
- value("Custom Java home", project.property("javaHome") as String)
- }
+ alias(libs.plugins.nohttp)
+ alias(libs.plugins.nexusPublish)
+ id("junitbuild.base-conventions")
+ id("junitbuild.build-metadata")
+ id("junitbuild.dependency-update-check")
+ id("junitbuild.jacoco-aggregation-conventions")
+ id("junitbuild.temp-maven-repo")
}
-val buildTimeAndDate by extra {
-
- // 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/
-
- if (System.getenv().containsKey("SOURCE_DATE_EPOCH")) {
-
- val sourceDateEpoch = System.getenv("SOURCE_DATE_EPOCH").toLong()
-
- Instant.ofEpochSecond(sourceDateEpoch).atOffset(ZoneOffset.UTC)
-
- } else {
- OffsetDateTime.now()
- }
-}
+description = "JUnit 5"
-val buildDate by extra { DateTimeFormatter.ISO_LOCAL_DATE.format(buildTimeAndDate) }
-val buildTime by extra { DateTimeFormatter.ofPattern("HH:mm:ss.SSSZ").format(buildTimeAndDate) }
-val buildRevision by extra { versioning.info.commit }
-val builtByValue by extra { project.findProperty("builtBy") ?: project.property("defaultBuiltBy") }
+val license by extra(License(
+ name = "Eclipse Public License v2.0",
+ url = uri("https://www.eclipse.org/legal/epl-v20.html"),
+ headerFile = layout.projectDirectory.file("gradle/config/spotless/eclipse-public-license-2.0.java")
+))
val platformProjects by extra(listOf(
- project(":junit-platform-commons"),
- project(":junit-platform-console"),
- project(":junit-platform-console-standalone"),
- project(":junit-platform-engine"),
- project(":junit-platform-jfr"),
- project(":junit-platform-launcher"),
- project(":junit-platform-reporting"),
- project(":junit-platform-runner"),
- project(":junit-platform-suite-api"),
- project(":junit-platform-testkit")
-))
+ projects.junitPlatformCommons,
+ projects.junitPlatformConsole,
+ projects.junitPlatformConsoleStandalone,
+ projects.junitPlatformEngine,
+ projects.junitPlatformJfr,
+ projects.junitPlatformLauncher,
+ projects.junitPlatformReporting,
+ projects.junitPlatformRunner,
+ projects.junitPlatformSuite,
+ projects.junitPlatformSuiteApi,
+ projects.junitPlatformSuiteCommons,
+ projects.junitPlatformSuiteEngine,
+ projects.junitPlatformTestkit
+).map { it.dependencyProject })
val jupiterProjects by extra(listOf(
- project(":junit-jupiter"),
- project(":junit-jupiter-api"),
- project(":junit-jupiter-engine"),
- project(":junit-jupiter-migrationsupport"),
- project(":junit-jupiter-params")
-))
+ projects.junitJupiter,
+ projects.junitJupiterApi,
+ projects.junitJupiterEngine,
+ projects.junitJupiterMigrationsupport,
+ projects.junitJupiterParams
+).map { it.dependencyProject })
val vintageProjects by extra(listOf(
- project(":junit-vintage-engine")
+ projects.junitVintageEngine.dependencyProject
))
val mavenizedProjects by extra(platformProjects + jupiterProjects + vintageProjects)
-val modularProjects by extra(mavenizedProjects - listOf(project(":junit-platform-console-standalone")))
-
-val license by extra(License(
- name = "Eclipse Public License v2.0",
- url = uri("https://www.eclipse.org/legal/epl-v20.html"),
- headerFile = file("src/spotless/eclipse-public-license-2.0.java")
-))
-
-val tempRepoName by extra("temp")
-val tempRepoDir by extra(file("$buildDir/repo"))
-
-val enableJaCoCo = project.hasProperty("enableJaCoCo")
-val jacocoTestProjects = listOf(
- project(":junit-jupiter-engine"),
- project(":junit-jupiter-migrationsupport"),
- project(":junit-jupiter-params"),
- project(":junit-platform-runner"),
- project(":junit-vintage-engine"),
- project(":platform-tests")
-)
-val jacocoCoveredProjects = modularProjects
-val jacocoClassesDir = file("$buildDir/jacoco/classes")
-
-allprojects {
-
- apply(plugin = "eclipse")
- apply(plugin = "idea")
- apply(plugin = "com.diffplug.spotless")
+val modularProjects by extra(mavenizedProjects - listOf(projects.junitPlatformConsoleStandalone.dependencyProject))
- if (enableJaCoCo) {
- apply(plugin = "jacoco")
- configure {
- toolVersion = versions["jacoco"]
- }
+dependencies {
+ (modularProjects + listOf(projects.platformTests.dependencyProject)).forEach {
+ jacocoAggregation(project(it.path))
}
+}
+nexusPublishing {
+ packageGroup = "org.junit"
repositories {
- // mavenLocal()
- mavenCentral()
- maven(url = "https://oss.sonatype.org/content/repositories/snapshots") {
- mavenContent {
- snapshotsOnly()
- }
- }
+ sonatype()
}
}
-subprojects {
-
- if (project in jupiterProjects) {
- group = property("jupiterGroup")!!
- }
- else if (project in platformProjects) {
- group = property("platformGroup")!!
- version = property("platformVersion")!!
- }
- else if (project in vintageProjects) {
- group = property("vintageGroup")!!
- version = property("vintageVersion")!!
- }
-
- tasks.withType().configureEach {
- isPreserveFileTimestamps = false
- isReproducibleFileOrder = true
- dirMode = Integer.parseInt("0755", 8)
- fileMode = Integer.parseInt("0644", 8)
- }
-
- pluginManager.withPlugin("java") {
-
- spotless {
- val headerFile = license.headerFile
- val importOrderConfigFile = rootProject.file("src/eclipse/junit-eclipse.importorder")
- val javaFormatterConfigFile = rootProject.file("src/eclipse/junit-eclipse-formatter-settings.xml")
-
- java {
- licenseHeaderFile(headerFile, "(package|import|open|module) ")
- importOrderFile(importOrderConfigFile)
- eclipse().configFile(javaFormatterConfigFile)
- removeUnusedImports()
- trimTrailingWhitespace()
- endWithNewline()
- }
-
- kotlin {
- ktlint(versions["ktlint"])
- licenseHeaderFile(headerFile)
- trimTrailingWhitespace()
- endWithNewline()
- }
- }
-
- afterEvaluate {
- if (enableJaCoCo && project in jacocoCoveredProjects) {
- val jarTask = (tasks.findByName("shadowJar") ?: tasks["jar"]) as Jar
- val extractJar by tasks.registering(Copy::class) {
- from(zipTree(jarTask.archivePath))
- into(jacocoClassesDir)
- include("**/*.class")
- // don't report coverage for shadowed classes
- exclude("**/shadow/**")
- // don't version-specific classes of MR JARs
- exclude("META-INF/versions/**")
- includeEmptyDirs = false
- onlyIf { jarTask.enabled }
- }
- jarTask.finalizedBy(extractJar)
- }
- }
- }
-
- pluginManager.withPlugin("maven-publish") {
- configure {
- repositories {
- repositories {
- maven {
- name = tempRepoName
- url = uri(tempRepoDir)
- }
- }
- }
- }
- }
+nohttp {
+ source.exclude("**/.gradle/**", "gradle/plugins/**/build/**", "buildSrc/build/**")
}
-rootProject.apply {
- description = "JUnit 5"
-
- spotless {
- format("misc") {
- target("**/*.gradle", "**/*.gradle.kts", "**/*.gitignore")
- targetExclude("**/build/**")
- indentWithTabs()
- trimTrailingWhitespace()
- endWithNewline()
- }
- format("documentation") {
- target("**/*.adoc", "**/*.md")
- trimTrailingWhitespace()
- endWithNewline()
- }
- }
-
- nohttp {
- // Must cast, since `source` is only exposed as a FileTree
- (source as ConfigurableFileTree).exclude("buildSrc/build/generated-sources/**")
- }
-
- tasks {
- dependencyUpdates {
- checkConstraints = true
- resolutionStrategy {
- componentSelection {
- all {
- val rejected = listOf("alpha", "beta", "rc", "cr", "m", "preview", "b", "ea")
- .map { qualifier -> Regex("(?i).*[.-]$qualifier[.\\d-+]*") }
- .any { it.matches(candidate.version) }
- if (rejected) {
- reject("Release candidate")
- }
- }
- }
- }
- }
- }
-
- if (enableJaCoCo) {
- tasks {
- val jacocoMerge by registering(JacocoMerge::class) {
- subprojects.filter { it in jacocoTestProjects }
- .forEach { subproj ->
- executionData(fileTree("dir" to "${subproj.buildDir}/jacoco", "include" to "*.exec"))
- dependsOn(subproj.tasks.withType())
- }
- }
- register("jacocoRootReport") {
- dependsOn(jacocoMerge)
- jacocoCoveredProjects.forEach {
- it.pluginManager.withPlugin("java") {
- sourceDirectories.from(it.the()["main"].allSource.srcDirs)
- }
- }
- classDirectories.from(files(jacocoClassesDir))
- executionData(jacocoMerge.get().destinationFile)
- reports {
- html.isEnabled = true
- xml.isEnabled = true
- csv.isEnabled = false
- }
- }
- }
- }
+tasks.checkstyleNohttp {
+ notCompatibleWithConfigurationCache("https://github.com/spring-io/nohttp/issues/61")
}
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
deleted file mode 100644
index fd74ea278a13..000000000000
--- a/buildSrc/build.gradle.kts
+++ /dev/null
@@ -1,16 +0,0 @@
-plugins {
- `kotlin-dsl`
-}
-
-repositories {
- mavenCentral()
- gradlePluginPortal()
-}
-
-dependencies {
- implementation(kotlin("gradle-plugin"))
- implementation("de.marcphilipp.gradle:nexus-publish-plugin:0.4.0")
- implementation("biz.aQute.bnd:biz.aQute.bnd.gradle:5.1.2")
- implementation("com.github.jengelman.gradle.plugins:shadow:6.0.0")
- implementation("org.gradle:test-retry-gradle-plugin:1.1.8")
-}
diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts
deleted file mode 100644
index 44425d9c15fc..000000000000
--- a/buildSrc/settings.gradle.kts
+++ /dev/null
@@ -1 +0,0 @@
-// intentionally left blank
diff --git a/buildSrc/src/main/kotlin/JavaLibraryExtension.kt b/buildSrc/src/main/kotlin/JavaLibraryExtension.kt
deleted file mode 100644
index 7c020478d6c3..000000000000
--- a/buildSrc/src/main/kotlin/JavaLibraryExtension.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-import org.gradle.api.JavaVersion
-
-@Suppress("UnstableApiUsage")
-open class JavaLibraryExtension {
- var mainJavaVersion: JavaVersion = Versions.jvmTarget
- var testJavaVersion: JavaVersion = JavaVersion.VERSION_11
-}
diff --git a/buildSrc/src/main/kotlin/ProjectExtensions.kt b/buildSrc/src/main/kotlin/ProjectExtensions.kt
deleted file mode 100644
index d6d2f4215049..000000000000
--- a/buildSrc/src/main/kotlin/ProjectExtensions.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-import org.gradle.api.Project
-import org.gradle.kotlin.dsl.extra
-import org.gradle.kotlin.dsl.provideDelegate
-
-val Project.javaModuleName: String
- get() = "org." + this.name.replace('-', '.')
-
-val Project.versions: Versions
- get() {
- var versions: Versions? by rootProject.extra
- return versions ?: Versions(rootProject).also { versions = it }
- }
diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
deleted file mode 100644
index c74c5265d80f..000000000000
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-import org.gradle.api.JavaVersion
-import org.gradle.api.Project
-import org.gradle.kotlin.dsl.extra
-import kotlin.reflect.KProperty
-
-class Versions(private val project: Project) {
-
- companion object {
- val jvmTarget = JavaVersion.VERSION_1_8
- }
-
- private val properties = object {
- operator fun getValue(receiver: Any?, property: KProperty<*>) = get(property.name)
- }
-
- val junit4 by properties
- val junit4Min by properties
- val opentest4j by properties
- val apiguardian by properties
- val assertj by properties
-
- operator fun get(name: String) = project.extra.get("$name.version") as String
-
-}
diff --git a/buildSrc/src/main/kotlin/custom-java-home.gradle.kts b/buildSrc/src/main/kotlin/custom-java-home.gradle.kts
deleted file mode 100644
index 8533c5f949fd..000000000000
--- a/buildSrc/src/main/kotlin/custom-java-home.gradle.kts
+++ /dev/null
@@ -1,48 +0,0 @@
-import org.gradle.internal.os.OperatingSystem
-import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile
-
-if (project.hasProperty("javaHome")) {
-
- val javaHome: String by project
- require(file(javaHome).isDirectory) {
- "Java home directory set via `javaHome` project property is invalid: $javaHome"
- }
-
- fun javaHomeExecutable(execName: String): String {
- val extension = if (OperatingSystem.current().isWindows) ".exe" else ""
- val executable = File(File(javaHome, "bin"), "$execName$extension")
- require(executable.exists()) {
- "File does not exist: $executable"
- }
- return executable.canonicalPath
- }
-
- tasks {
- withType().configureEach {
- options.isFork = true
- options.forkOptions.javaHome = file(javaHome)
- doFirst {
- // Avoid compiler warnings for non-existing path entries
- classpath = classpath.filter { it.exists() }
- }
- }
- withType().configureEach {
- options.isFork = true
- options.forkOptions.javaHome = file(javaHome)
- }
- withType().configureEach {
- kotlinOptions {
- jdkHome = javaHome
- }
- }
- withType().configureEach {
- executable = javaHomeExecutable("javadoc")
- }
- withType().configureEach {
- executable = javaHomeExecutable("java")
- }
- withType().configureEach {
- setExecutable(javaHomeExecutable("java"))
- }
- }
-}
diff --git a/buildSrc/src/main/kotlin/java-multi-release-sources.gradle.kts b/buildSrc/src/main/kotlin/java-multi-release-sources.gradle.kts
deleted file mode 100644
index 1843acb7c395..000000000000
--- a/buildSrc/src/main/kotlin/java-multi-release-sources.gradle.kts
+++ /dev/null
@@ -1,41 +0,0 @@
-plugins {
- id("java-library-conventions")
-}
-
-val mavenizedProjects: List by rootProject.extra
-
-val mainRelease9 by sourceSets.registering {
- compileClasspath += sourceSets.main.get().output
- runtimeClasspath += sourceSets.main.get().output
- java {
- setSrcDirs(setOf("src/main/java9"))
- }
-}
-
-configurations.named(mainRelease9.get().compileClasspathConfigurationName).configure {
- extendsFrom(configurations.compileClasspath.get())
-}
-
-tasks {
-
- named("allMainClasses").configure {
- dependsOn(mainRelease9.get().classesTaskName)
- }
-
- named(mainRelease9.get().compileJavaTaskName).configure {
- options.release.set(9)
- }
-
- named("checkstyle${mainRelease9.name.capitalize()}").configure {
- configFile = rootProject.file("src/checkstyle/checkstyleMain.xml")
- }
-
- if (project in mavenizedProjects) {
- javadoc {
- source(mainRelease9.get().allJava)
- }
- named("sourcesJar").configure {
- from(mainRelease9.get().allSource)
- }
- }
-}
diff --git a/buildSrc/src/main/kotlin/junit4-compatibility.gradle.kts b/buildSrc/src/main/kotlin/junit4-compatibility.gradle.kts
deleted file mode 100644
index deb9fdced557..000000000000
--- a/buildSrc/src/main/kotlin/junit4-compatibility.gradle.kts
+++ /dev/null
@@ -1,28 +0,0 @@
-plugins {
- `java-library`
-}
-
-val junit_4_12 by configurations.creating {
- extendsFrom(configurations.testRuntimeClasspath.get())
-}
-
-dependencies {
- junit_4_12("junit:junit") {
- version {
- strictly("4.12")
- }
- }
- pluginManager.withPlugin("osgi-conventions") {
- "osgiVerification"("org.apache.servicemix.bundles:org.apache.servicemix.bundles.junit:4.13_1")
- }
-}
-
-tasks {
- val test_4_12 by registering(Test::class) {
- classpath -= configurations.testRuntimeClasspath.get()
- classpath += junit_4_12
- }
- check {
- dependsOn(test_4_12)
- }
-}
diff --git a/buildSrc/src/main/kotlin/kotlin-library-conventions.gradle.kts b/buildSrc/src/main/kotlin/kotlin-library-conventions.gradle.kts
deleted file mode 100644
index 3342cd189501..000000000000
--- a/buildSrc/src/main/kotlin/kotlin-library-conventions.gradle.kts
+++ /dev/null
@@ -1,14 +0,0 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-plugins {
- id("java-library-conventions")
- kotlin("jvm")
-}
-
-tasks.withType().configureEach {
- kotlinOptions {
- jvmTarget = Versions.jvmTarget.toString()
- apiVersion = "1.3"
- languageVersion = "1.3"
- }
-}
diff --git a/buildSrc/src/main/kotlin/osgi-conventions.gradle.kts b/buildSrc/src/main/kotlin/osgi-conventions.gradle.kts
deleted file mode 100644
index 8b8f1705dc7f..000000000000
--- a/buildSrc/src/main/kotlin/osgi-conventions.gradle.kts
+++ /dev/null
@@ -1,112 +0,0 @@
-import aQute.bnd.gradle.BundleTaskConvention
-import aQute.bnd.gradle.FileSetRepositoryConvention
-import aQute.bnd.gradle.Resolve
-
-plugins {
- `java-library`
-}
-
-// This task enhances `jar` and `shadowJar` tasks with the bnd
-// `BundleTaskConvention` convention which allows for generating OSGi
-// metadata into the jar
-tasks.withType().matching {
- task: Jar -> task.name == "jar" || task.name == "shadowJar"
-}.configureEach {
- val btc = BundleTaskConvention(this)
-
- // These are bnd instructions necessary for generating OSGi metadata.
- // We've generalized these so that they are widely applicable limiting
- // module configurations to special cases.
- btc.setBnd("""
- # These are the general rules for package imports.
- Import-Package: \
- !org.apiguardian.api,\
- org.junit.platform.commons.logging;status=INTERNAL,\
- kotlin.*;resolution:="optional",\
- *
-
- # This tells bnd not to complain if a module doesn't actually import
- # the kotlin packages, but enough modules do to make it a default.
- -fixupmessages.kotlin.import: "Unused Import-Package instructions: \\[kotlin.*\\]";is:=ignore
-
- # This tells bnd to ignore classes it finds in `META-INF/versions/`
- # because bnd doesn't yet support multi-release jars.
- -fixupmessages.wrong.dir: "Classes found in the wrong directory: \\{META-INF/versions/...";is:=ignore
-
- # Don't scan for Class.forName package imports.
- # See https://bnd.bndtools.org/instructions/noclassforname.html
- -noclassforname: true
-
- # Don't add all the extra headers bnd normally adds.
- # See https://bnd.bndtools.org/instructions/noextraheaders.html
- -noextraheaders: true
-
- # Don't add the Private-Package header.
- # See https://bnd.bndtools.org/instructions/removeheaders.html
- -removeheaders: Private-Package
-
- # Instruct the APIGuardianAnnotations how to operate.
- # See https://bnd.bndtools.org/instructions/export-apiguardian.html
- -export-apiguardian: *;version=${project.version}
- """)
-
- // Add the convention to the jar task
- convention.plugins["bundle"] = btc
-
- doLast {
- // Do the actual work putting OSGi stuff in the jar.
- btc.buildBundle()
- }
-}
-
-val osgiPropertiesFile = file("$buildDir/verifyOSGiProperties.bndrun")
-
-// Bnd's Resolve task uses a properties file for its configuration. This
-// task writes out the properties necessary for it to verify the OSGi
-// metadata.
-val osgiProperties by tasks.registering(WriteProperties::class) {
- outputFile = osgiPropertiesFile
- property("-standalone", true)
- project.extensions.getByType(JavaLibraryExtension::class.java).let { javaLibrary ->
- property("-runee", "JavaSE-${javaLibrary.mainJavaVersion}")
- }
- property("-runrequires", "osgi.identity;filter:='(osgi.identity=${project.name})'")
- property("-runsystempackages", "jdk.internal.misc,sun.misc")
-}
-
-val osgiVerification by configurations.creating {
- extendsFrom(configurations.runtimeClasspath.get())
-}
-
-// Bnd's Resolve task is what verifies that a jar can be used in OSGi and
-// that its metadata is valid. If the metadata is invalid this task will
-// fail.
-val verifyOSGi by tasks.registering(Resolve::class) {
- dependsOn(osgiProperties)
- setBndrun(osgiPropertiesFile)
- isReportOptional = false
- withConvention(FileSetRepositoryConvention::class) {
-
- // By default bnd will use jars found in:
- // 1. project.sourceSets.main.runtimeClasspath
- // 2. project.configurations.archives.artifacts.files
- // to validate the metadata.
- // 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)
- }
-}
-
-tasks.check {
- dependsOn(verifyOSGi)
-}
-
-// The ${project.description}, for some odd reason, is only available
-// after evaluation.
-afterEvaluate {
- tasks.withType().configureEach {
- convention.findPlugin(BundleTaskConvention::class.java)
- ?.bnd("Bundle-Name: ${project.description}")
- }
-}
diff --git a/buildSrc/src/main/kotlin/publishing-conventions.gradle.kts b/buildSrc/src/main/kotlin/publishing-conventions.gradle.kts
deleted file mode 100644
index 6f58ce7453af..000000000000
--- a/buildSrc/src/main/kotlin/publishing-conventions.gradle.kts
+++ /dev/null
@@ -1,101 +0,0 @@
-import java.time.Duration
-
-plugins {
- `maven-publish`
- signing
- id("de.marcphilipp.nexus-publish")
-}
-
-val isSnapshot = project.version.toString().contains("SNAPSHOT")
-val isContinuousIntegrationEnvironment = System.getenv("CI")?.toBoolean() ?: false
-val isJitPackEnvironment = System.getenv("JITPACK")?.toBoolean() ?: false
-
-// ensure project is built successfully before publishing it
-tasks.withType().configureEach {
- dependsOn(tasks.build)
-}
-tasks.withType().configureEach {
- dependsOn(tasks.build)
-}
-
-signing {
- sign(publishing.publications)
- isRequired = !(isSnapshot || isContinuousIntegrationEnvironment || isJitPackEnvironment)
-}
-
-tasks.withType().configureEach {
- onlyIf {
- !isSnapshot // Gradle Module Metadata currently does not support signing snapshots
- }
-}
-
-nexusPublishing {
- connectTimeout.set(Duration.ofMinutes(2))
- clientTimeout.set(Duration.ofMinutes(2))
- packageGroup.set("org.junit")
- repositories {
- sonatype()
- }
-}
-
-publishing {
- publications {
- create("maven") {
- pom {
- name.set(provider {
- project.description ?: "${project.group}:${project.name}"
- })
- url.set("https://junit.org/junit5/")
- scm {
- connection.set("scm:git:git://github.com/junit-team/junit5.git")
- developerConnection.set("scm:git:git://github.com/junit-team/junit5.git")
- url.set("https://github.com/junit-team/junit5")
- }
- licenses {
- license {
- val license: License by rootProject.extra
- name.set(license.name)
- url.set(license.url.toString())
- }
- }
- developers {
- developer {
- id.set("bechte")
- name.set("Stefan Bechtold")
- email.set("stefan.bechtold@me.com")
- }
- developer {
- id.set("jlink")
- name.set("Johannes Link")
- email.set("business@johanneslink.net")
- }
- developer {
- id.set("marcphilipp")
- name.set("Marc Philipp")
- email.set("mail@marcphilipp.de")
- }
- developer {
- id.set("mmerdes")
- name.set("Matthias Merdes")
- email.set("matthias.merdes@heidelpay.com")
- }
- developer {
- id.set("sbrannen")
- name.set("Sam Brannen")
- email.set("sam@sambrannen.com")
- }
- developer {
- id.set("sormuras")
- name.set("Christian Stein")
- email.set("sormuras@gmail.com")
- }
- developer {
- id.set("juliette-derancourt")
- name.set("Juliette de Rancourt")
- email.set("derancourt.juliette@gmail.com")
- }
- }
- }
- }
- }
-}
diff --git a/buildSrc/src/main/kotlin/testing-conventions.gradle.kts b/buildSrc/src/main/kotlin/testing-conventions.gradle.kts
deleted file mode 100644
index 57a97e90247d..000000000000
--- a/buildSrc/src/main/kotlin/testing-conventions.gradle.kts
+++ /dev/null
@@ -1,43 +0,0 @@
-import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
-import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED
-
-plugins {
- id("org.gradle.test-retry")
-}
-
-tasks.withType().configureEach {
- useJUnitPlatform {
- includeEngines("junit-jupiter")
- }
- include("**/*Test.class", "**/*Tests.class")
- testLogging {
- events = setOf(FAILED)
- exceptionFormat = FULL
- }
- retry {
- maxRetries.set(2)
- }
- systemProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager")
- // Required until ASM officially supports the JDK 14
- systemProperty("net.bytebuddy.experimental", true)
-}
-
-dependencies {
- "testImplementation"("org.assertj:assertj-core")
- "testImplementation"("org.mockito:mockito-junit-jupiter") {
- exclude(module = "junit-jupiter-engine")
- }
-
- if (project.name != "junit-jupiter-engine") {
- "testImplementation"(project(":junit-jupiter-api"))
- "testImplementation"(project(":junit-jupiter-params"))
-
- "testRuntimeOnly"(project(":junit-jupiter-engine"))
- }
- "testImplementation"(testFixtures(project(":junit-jupiter-api")))
-
- "testRuntimeOnly"(project(":junit-platform-launcher"))
-
- "testRuntimeOnly"("org.apache.logging.log4j:log4j-core")
- "testRuntimeOnly"("org.apache.logging.log4j:log4j-jul")
-}
diff --git a/dependencies/dependencies.gradle.kts b/dependencies/dependencies.gradle.kts
deleted file mode 100644
index 090ab495abd5..000000000000
--- a/dependencies/dependencies.gradle.kts
+++ /dev/null
@@ -1,37 +0,0 @@
-plugins {
- `java-platform`
-}
-
-dependencies {
- constraints {
- // api means "the dependency is for both compilation and runtime"
- // runtime means "the dependency is only for runtime, not for compilation"
- // In other words, marking dependency as "runtime" would avoid accidental
- // dependency on it during compilation
- api("org.apiguardian:apiguardian-api:${versions.apiguardian}")
- api("org.opentest4j:opentest4j:${versions.opentest4j}")
- runtime("org.apache.logging.log4j:log4j-core:${versions["log4j"]}")
- runtime("org.apache.logging.log4j:log4j-jul:${versions["log4j"]}")
- api("io.github.classgraph:classgraph:${versions["classgraph"]}")
- api("org.codehaus.groovy:groovy-all:${versions["groovy"]}")
- api("junit:junit:[${versions.junit4Min},)") {
- version {
- prefer(versions.junit4)
- }
- }
- api("com.univocity:univocity-parsers:${versions["univocity-parsers"]}")
- api("info.picocli:picocli:${versions["picocli"]}")
- api("org.assertj:assertj-core:${versions.assertj}")
- api("org.openjdk.jmh:jmh-core:${versions["jmh"]}")
- api("org.openjdk.jmh:jmh-generator-annprocess:${versions["jmh"]}")
- api("de.sormuras:bartholdy:${versions["bartholdy"]}")
- api("commons-io:commons-io:${versions["commons-io"]}")
- api("com.tngtech.archunit:archunit-junit5-api:${versions["archunit"]}")
- api("com.tngtech.archunit:archunit-junit5-engine:${versions["archunit"]}")
- api("org.slf4j:slf4j-jdk14:${versions["slf4j"]}")
- api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions["kotlinx-coroutines-core"]}")
- api("org.mockito:mockito-junit-jupiter:${versions["mockito"]}")
- api("biz.aQute.bnd:biz.aQute.bndlib:${versions["bnd"]}")
- api("org.spockframework:spock-core:${versions["spock"]}")
- }
-}
diff --git a/documentation/documentation.gradle.kts b/documentation/documentation.gradle.kts
index 4cb26b1433ca..4cc640563170 100644
--- a/documentation/documentation.gradle.kts
+++ b/documentation/documentation.gradle.kts
@@ -1,15 +1,19 @@
+import junitbuild.exec.CaptureJavaExecOutput
+import junitbuild.exec.ClasspathSystemPropertyProvider
+import junitbuild.exec.GenerateStandaloneConsoleLauncherShadowedArtifactsFile
+import junitbuild.exec.RunConsoleLauncher
+import junitbuild.javadoc.ModuleSpecificJavadocFileOption
+import org.asciidoctor.gradle.base.AsciidoctorAttributeProvider
import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask
-import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
-import org.junit.gradle.exec.ClasspathSystemPropertyProvider
-import org.junit.gradle.javadoc.ModuleSpecificJavadocFileOption
-import java.io.ByteArrayOutputStream
-import java.nio.file.Files
+import org.gradle.api.tasks.PathSensitivity.RELATIVE
plugins {
- id("org.asciidoctor.jvm.convert")
- id("org.asciidoctor.jvm.pdf")
- id("org.ajoberstar.git-publish")
- `kotlin-library-conventions`
+ alias(libs.plugins.asciidoctorConvert)
+ alias(libs.plugins.asciidoctorPdf)
+ alias(libs.plugins.gitPublish)
+ id("junitbuild.build-parameters")
+ id("junitbuild.kotlin-library-conventions")
+ id("junitbuild.testing-conventions")
}
val modularProjects: List by rootProject
@@ -22,14 +26,11 @@ javaLibrary {
testJavaVersion = JavaVersion.VERSION_1_8
}
-val apiReport by configurations.creating {
- extendsFrom(configurations.internal.get())
-}
+val apiReport by configurations.creatingResolvable
+val standaloneConsoleLauncher by configurations.creatingResolvable
dependencies {
- internal(platform(project(":dependencies")))
-
- implementation(project(":junit-jupiter-api")) {
+ implementation(projects.junitJupiterApi) {
because("Jupiter API is used in src/main/java")
}
@@ -37,42 +38,52 @@ dependencies {
// in reports generated by the ApiReportGenerator.
modularProjects.forEach { apiReport(it) }
- testImplementation(project(":junit-jupiter"))
- testImplementation(project(":junit-jupiter-migrationsupport"))
- testImplementation(project(":junit-platform-console"))
- testImplementation(project(":junit-platform-runner"))
- testImplementation(project(":junit-platform-testkit"))
- testImplementation("org.jetbrains.kotlin:kotlin-stdlib")
+ testImplementation(projects.junitJupiterMigrationsupport)
+ testImplementation(projects.junitPlatformConsole)
+ testImplementation(projects.junitPlatformRunner)
+ testImplementation(projects.junitPlatformSuite)
+ testImplementation(projects.junitPlatformTestkit)
+ testImplementation(kotlin("stdlib"))
- testImplementation(project(":junit-vintage-engine"))
- testRuntimeOnly("org.apache.logging.log4j:log4j-core")
- testRuntimeOnly("org.apache.logging.log4j:log4j-jul")
+ testImplementation(projects.junitVintageEngine)
+ testRuntimeOnly(libs.apiguardian) {
+ because("it's required to generate API tables")
+ }
- testImplementation("io.github.classgraph:classgraph") {
+ testImplementation(libs.classgraph) {
because("ApiReportGenerator needs it")
}
+
+ testImplementation(libs.jimfs) {
+ because("Jimfs is used in src/test/java")
+ }
+
+ standaloneConsoleLauncher(projects.junitPlatformConsoleStandalone)
}
asciidoctorj {
modules {
diagram.use()
- pdf.version(versions["asciidoctor-pdf"])
+ pdf.version(libs.versions.asciidoctor.pdf)
}
+ requires(file("src/docs/asciidoc/resources/themes/rouge_junit.rb"))
}
val snapshot = rootProject.version.toString().contains("SNAPSHOT")
val docsVersion = if (snapshot) "snapshot" else rootProject.version
val releaseBranch = if (snapshot) "HEAD" else "r${rootProject.version}"
-val docsDir = file("$buildDir/ghpages-docs")
-val replaceCurrentDocs = project.hasProperty("replaceCurrentDocs")
+val docsDir = layout.buildDirectory.dir("ghpages-docs")
+val replaceCurrentDocs = buildParameters.documentation.replaceCurrentDocs
val uploadPdfs = !snapshot
val userGuidePdfFileName = "junit-user-guide-${rootProject.version}.pdf"
-val ota4jDocVersion = if (versions.opentest4j.contains("SNAPSHOT")) "snapshot" else versions.opentest4j
-val apiGuardianDocVersion = if (versions.apiguardian.contains("SNAPSHOT")) "snapshot" else versions.apiguardian
+val ota4jDocVersion = if (libs.versions.opentest4j.get().contains("SNAPSHOT")) "snapshot" else libs.versions.opentest4j.get()
+val apiGuardianDocVersion = if (libs.versions.apiguardian.get().contains("SNAPSHOT")) "snapshot" else libs.versions.apiguardian.get()
gitPublish {
- repoUri.set("https://github.com/junit-team/junit5.git")
- branch.set("gh-pages")
+ repoUri = "https://github.com/junit-team/junit5.git"
+ branch = "gh-pages"
+ sign = false
+ fetchDepth = 1
contents {
from(docsDir)
@@ -88,15 +99,20 @@ gitPublish {
}
}
-val generatedAsciiDocPath = file("$buildDir/generated/asciidoc")
-val consoleLauncherOptionsFile = File(generatedAsciiDocPath, "console-launcher-options.txt")
-val experimentalApisTableFile = File(generatedAsciiDocPath, "experimental-apis-table.txt")
-val deprecatedApisTableFile = File(generatedAsciiDocPath, "deprecated-apis-table.txt")
-
-val elementListsDir = file("$buildDir/elementLists")
+val generatedAsciiDocPath = layout.buildDirectory.dir("generated/asciidoc")
+val consoleLauncherOptionsFile = generatedAsciiDocPath.map { it.file("console-launcher-options.txt") }
+val consoleLauncherDiscoverOptionsFile = generatedAsciiDocPath.map { it.file("console-launcher-discover-options.txt") }
+val consoleLauncherExecuteOptionsFile = generatedAsciiDocPath.map { it.file("console-launcher-execute-options.txt") }
+val consoleLauncherEnginesOptionsFile = generatedAsciiDocPath.map { it.file("console-launcher-engines-options.txt") }
+val experimentalApisTableFile = generatedAsciiDocPath.map { it.file("experimental-apis-table.adoc") }
+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 elementListsDir = layout.buildDirectory.dir("elementLists")
val externalModulesWithoutModularJavadoc = mapOf(
"org.apiguardian.api" to "https://apiguardian-team.github.io/apiguardian/docs/$apiGuardianDocVersion/api/",
- "org.assertj.core" to "https://javadoc.io/doc/org.assertj/assertj-core/${versions.assertj}/",
+ "org.assertj.core" to "https://javadoc.io/doc/org.assertj/assertj-core/${libs.versions.assertj.get()}/",
"org.opentest4j" to "https://ota4j-team.github.io/opentest4j/docs/$ota4jDocVersion/api/"
)
require(externalModulesWithoutModularJavadoc.values.all { it.endsWith("/") }) {
@@ -105,52 +121,102 @@ require(externalModulesWithoutModularJavadoc.values.all { it.endsWith("/") }) {
tasks {
- val consoleLauncherTest by registering(JavaExec::class) {
- dependsOn(testClasses)
- val reportsDir = file("$buildDir/test-results")
+ val consoleLauncherTest by registering(RunConsoleLauncher::class) {
+ args.addAll("execute")
+ args.addAll("--scan-classpath")
+ args.addAll("--config=junit.platform.reporting.open.xml.enabled=true")
+ val reportsDir = project.layout.buildDirectory.dir("console-launcher-test-results")
outputs.dir(reportsDir)
- outputs.cacheIf { true }
- classpath = sourceSets["test"].runtimeClasspath
- main = "org.junit.platform.console.ConsoleLauncher"
- args("--scan-classpath")
- args("--include-classname", ".*Tests")
- args("--include-classname", ".*Demo")
- args("--exclude-tag", "exclude")
- args("--reports-dir", reportsDir)
- systemProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager")
+ argumentProviders.add(CommandLineArgumentProvider {
+ listOf(
+ "--reports-dir=${reportsDir.get()}",
+ "--config=junit.platform.reporting.output.dir=${reportsDir.get()}"
+
+ )
+ })
+ args.addAll("--config", "enableHttpServer=true")
+ args.addAll("--include-classname", ".*Tests")
+ args.addAll("--include-classname", ".*Demo")
+ args.addAll("--exclude-tag", "exclude")
+ args.addAll("--exclude-tag", "timeout")
+ }
+
+ register("consoleLauncher") {
+ hideOutput = false
+ outputs.upToDateWhen { false }
}
test {
+ include("**/*Demo.class")
+ (options as JUnitPlatformOptions).apply {
+ includeEngines("junit-vintage")
+ includeTags("timeout")
+ }
+ }
+
+ check {
dependsOn(consoleLauncherTest)
- exclude("**/*")
}
- val generateConsoleLauncherOptions by registering(JavaExec::class) {
- classpath = sourceSets["test"].runtimeClasspath
- main = "org.junit.platform.console.ConsoleLauncher"
- args("--help", "--disable-banner")
- redirectOutput(consoleLauncherOptionsFile)
+ val generateConsoleLauncherOptions by registering(CaptureJavaExecOutput::class) {
+ classpath.from(sourceSets["test"].runtimeClasspath)
+ mainClass = "org.junit.platform.console.ConsoleLauncher"
+ args.addAll("--help", "--disable-banner")
+ outputFile = consoleLauncherOptionsFile
+ }
+
+ val generateConsoleLauncherDiscoverOptions by registering(CaptureJavaExecOutput::class) {
+ classpath.from(sourceSets["test"].runtimeClasspath)
+ 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)
+ 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)
+ mainClass = "org.junit.platform.console.ConsoleLauncher"
+ args.addAll("engines", "--help", "--disable-banner")
+ outputFile = consoleLauncherEnginesOptionsFile
}
- val generateExperimentalApisTable by registering(JavaExec::class) {
- classpath = sourceSets["test"].runtimeClasspath
- main = "org.junit.api.tools.ApiReportGenerator"
+ val generateExperimentalApisTable by registering(CaptureJavaExecOutput::class) {
+ classpath.from(sourceSets["test"].runtimeClasspath)
+ mainClass = "org.junit.api.tools.ApiReportGenerator"
jvmArgumentProviders += ClasspathSystemPropertyProvider("api.classpath", apiReport)
- args("EXPERIMENTAL")
- redirectOutput(experimentalApisTableFile)
+ args.add("EXPERIMENTAL")
+ outputFile = experimentalApisTableFile
}
- val generateDeprecatedApisTable by registering(JavaExec::class) {
- classpath = sourceSets["test"].runtimeClasspath
- main = "org.junit.api.tools.ApiReportGenerator"
+ val generateDeprecatedApisTable by registering(CaptureJavaExecOutput::class) {
+ classpath.from(sourceSets["test"].runtimeClasspath)
+ mainClass = "org.junit.api.tools.ApiReportGenerator"
jvmArgumentProviders += ClasspathSystemPropertyProvider("api.classpath", apiReport)
- args("DEPRECATED")
- redirectOutput(deprecatedApisTableFile)
+ args.add("DEPRECATED")
+ outputFile = deprecatedApisTableFile
+ }
+
+ val generateStandaloneConsoleLauncherShadowedArtifactsFile by registering(GenerateStandaloneConsoleLauncherShadowedArtifactsFile::class) {
+ inputJar.fileProvider(standaloneConsoleLauncher.elements.map { it.single().asFile })
+ outputFile = standaloneConsoleLauncherShadowedArtifactsFile
}
withType().configureEach {
- dependsOn(generateConsoleLauncherOptions, generateExperimentalApisTable, generateDeprecatedApisTable)
- inputs.files(consoleLauncherOptionsFile, experimentalApisTableFile, deprecatedApisTableFile)
+ inputs.files(
+ generateConsoleLauncherOptions,
+ generateConsoleLauncherDiscoverOptions,
+ generateConsoleLauncherExecuteOptions,
+ generateConsoleLauncherEnginesOptions,
+ generateExperimentalApisTable,
+ generateDeprecatedApisTable,
+ generateStandaloneConsoleLauncherShadowedArtifactsFile
+ )
resources {
from(sourceDir) {
@@ -159,30 +225,41 @@ tasks {
}
}
- attributes(mapOf(
+ // Temporary workaround for https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/599
+ inputs.dir(sourceDir).withPropertyName("sourceDir").withPathSensitivity(RELATIVE)
+
+ attributeProviders += AsciidoctorAttributeProvider {
+ mapOf(
"jupiter-version" to version,
"platform-version" to project.property("platformVersion"),
"vintage-version" to project.property("vintageVersion"),
"bom-version" to version,
- "junit4-version" to versions.junit4,
- "apiguardian-version" to versions.apiguardian,
- "ota4j-version" to versions.opentest4j,
- "surefire-version" to versions["surefire"],
+ "junit4-version" to libs.versions.junit4.get(),
+ "apiguardian-version" to libs.versions.apiguardian.get(),
+ "ota4j-version" to libs.versions.opentest4j.get(),
+ "surefire-version" to libs.versions.surefire.get(),
"release-branch" to releaseBranch,
"docs-version" to docsVersion,
"revnumber" to version,
- "consoleLauncherOptionsFile" to consoleLauncherOptionsFile,
- "experimentalApisTableFile" to experimentalApisTableFile,
- "deprecatedApisTableFile" to deprecatedApisTableFile,
+ "consoleLauncherOptionsFile" to consoleLauncherOptionsFile.get(),
+ "consoleLauncherDiscoverOptionsFile" to consoleLauncherDiscoverOptionsFile.get(),
+ "consoleLauncherExecuteOptionsFile" to consoleLauncherExecuteOptionsFile.get(),
+ "consoleLauncherEnginesOptionsFile" to consoleLauncherEnginesOptionsFile.get(),
+ "experimentalApisTableFile" to experimentalApisTableFile.get(),
+ "deprecatedApisTableFile" to deprecatedApisTableFile.get(),
+ "standaloneConsoleLauncherShadowedArtifactsFile" to standaloneConsoleLauncherShadowedArtifactsFile.get(),
"outdir" to outputDir.absolutePath,
"source-highlighter" to "rouge",
+ "rouge-style" to "junit",
"tabsize" to "4",
"toc" to "left",
"icons" to "font",
"sectanchors" to true,
"idprefix" to "",
- "idseparator" to "-"
- ))
+ "idseparator" to "-",
+ "jdk-javadoc-base-url" to jdkJavadocBaseUrl
+ )
+ }
sourceSets["test"].apply {
attributes(mapOf(
@@ -191,11 +268,19 @@ tasks {
))
inputs.dir(java.srcDirs.first())
inputs.dir(resources.srcDirs.first())
- withConvention(KotlinSourceSet::class) {
- attributes(mapOf("kotlinTestDir" to kotlin.srcDirs.first()))
- inputs.dir(kotlin.srcDirs.first())
- }
+ attributes(mapOf("kotlinTestDir" to kotlin.srcDirs.first()))
+ inputs.dir(kotlin.srcDirs.first())
+ }
+
+ forkOptions {
+ // To avoid warning, see https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/597
+ jvmArgs(
+ "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED",
+ "--add-opens", "java.base/java.io=ALL-UNNAMED"
+ )
}
+
+ notCompatibleWithConfigurationCache("https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/564")
}
asciidoctor {
@@ -229,7 +314,7 @@ tasks {
doFirst {
externalModulesWithoutModularJavadoc.forEach { (moduleName, baseUrl) ->
val resource = resources.text.fromUri("${baseUrl}element-list")
- elementListsDir.resolve(moduleName).apply {
+ elementListsDir.get().asFile.resolve(moduleName).apply {
mkdir()
resolve("element-list").writeText("module:$moduleName\n${resource.asString()}")
}
@@ -261,17 +346,17 @@ tasks {
this as StandardJavadocDocletOptions
splitIndex(true)
- addBooleanOption("Xdoclint:none", true)
+ addBooleanOption("Xdoclint:all,-missing", true)
addBooleanOption("html5", true)
addMultilineStringsOption("tag").value = listOf(
"apiNote:a:API Note:",
"implNote:a:Implementation Note:"
)
- links("https://docs.oracle.com/en/java/javase/11/docs/api/")
- links("https://junit.org/junit4/javadoc/${versions.junit4}/")
+ links(jdkJavadocBaseUrl)
+ links("https://junit.org/junit4/javadoc/${libs.versions.junit4.get()}/")
externalModulesWithoutModularJavadoc.forEach { (moduleName, baseUrl) ->
- linksOffline(baseUrl, "$elementListsDir/$moduleName")
+ linksOffline(baseUrl, elementListsDir.get().asFile.resolve(moduleName).absolutePath)
}
groups = mapOf(
@@ -293,6 +378,7 @@ tasks {
addStringOption("-add-modules", "info.picocli")
addOption(ModuleSpecificJavadocFileOption("-add-reads", mapOf(
"org.junit.platform.console" to "info.picocli",
+ "org.junit.platform.reporting" to "org.opentest4j.reporting.events",
"org.junit.jupiter.params" to "univocity.parsers"
)))
}
@@ -301,7 +387,7 @@ tasks {
classpath = files(modularProjects.map { it.sourceSets.main.get().compileClasspath })
maxMemory = "1024m"
- destinationDir = file("$buildDir/docs/javadoc")
+ destinationDir = layout.buildDirectory.dir("docs/javadoc").get().asFile
doFirst {
(options as CoreJavadocOptions).modulePath = classpath.files.toList()
@@ -331,14 +417,14 @@ tasks {
}
}
}
- into("$buildDir/docs/fixedJavadoc")
+ into(layout.buildDirectory.dir("docs/fixedJavadoc"))
}
val prepareDocsForUploadToGhPages by registering(Copy::class) {
dependsOn(fixJavadoc, asciidoctor, asciidoctorPdf)
outputs.dir(docsDir)
- from("$buildDir/checksum") {
+ from(layout.buildDirectory.dir("checksum")) {
include("published-checksum.txt")
}
from(asciidoctor.map { it.outputDir }) {
@@ -355,7 +441,7 @@ tasks {
from(fixJavadoc.map { it.destinationDir }) {
into("api")
}
- into("$docsDir/$docsVersion")
+ into(docsDir.map { it.dir(docsVersion.toString()) })
includeEmptyDirs = false
}
@@ -368,31 +454,36 @@ tasks {
into("$docsDir/current")
}
- gitPublishCommit {
+ val configureGitAuthor by registering {
+ dependsOn(gitPublishReset)
+ doFirst {
+ File(gitPublish.repoDir.get().asFile, ".git/config").appendText("""
+ [user]
+ name = JUnit Team
+ email = team@junit.org
+ """.trimIndent())
+ }
+ }
+
+ gitPublishCopy {
dependsOn(prepareDocsForUploadToGhPages, createCurrentDocsFolder)
}
-}
-fun JavaExec.redirectOutput(outputFile: File) {
- outputs.file(outputFile)
- val byteStream = ByteArrayOutputStream()
- standardOutput = byteStream
- doLast {
- Files.createDirectories(outputFile.parentFile.toPath())
- Files.write(outputFile.toPath(), byteStream.toByteArray())
+ gitPublishCommit {
+ dependsOn(configureGitAuthor)
}
}
eclipse {
classpath {
- plusConfigurations.add(project(":junit-platform-console").configurations["shadowed"])
- plusConfigurations.add(project(":junit-jupiter-params").configurations["shadowed"])
+ plusConfigurations.add(projects.junitPlatformConsole.dependencyProject.configurations["shadowed"])
+ plusConfigurations.add(projects.junitJupiterParams.dependencyProject.configurations["shadowed"])
}
}
idea {
module {
- scopes["PROVIDED"]!!["plus"]!!.add(project(":junit-platform-console").configurations["shadowed"])
- scopes["PROVIDED"]!!["plus"]!!.add(project(":junit-jupiter-params").configurations["shadowed"])
+ scopes["PROVIDED"]!!["plus"]!!.add(projects.junitPlatformConsole.dependencyProject.configurations["shadowed"])
+ scopes["PROVIDED"]!!["plus"]!!.add(projects.junitJupiterParams.dependencyProject.configurations["shadowed"])
}
}
diff --git a/documentation/src/docs/asciidoc/link-attributes.adoc b/documentation/src/docs/asciidoc/link-attributes.adoc
index a994609b335a..6aa27c3d46b8 100644
--- a/documentation/src/docs/asciidoc/link-attributes.adoc
+++ b/documentation/src/docs/asciidoc/link-attributes.adoc
@@ -14,6 +14,7 @@ endif::[]
:ClassSupport: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ClassSupport.html[ClassSupport]
: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]
// Platform Console Launcher
:junit-platform-console: {javadoc-root}/org.junit.platform.console/org/junit/platform/console/package-summary.html[junit-platform-console]
:ConsoleLauncher: {javadoc-root}/org.junit.platform.console/org/junit/platform/console/ConsoleLauncher.html[ConsoleLauncher]
@@ -27,20 +28,28 @@ endif::[]
// Platform Launcher API
:junit-platform-launcher: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/package-summary.html[junit-platform-launcher]
:Launcher: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/Launcher.html[Launcher]
+:LauncherConfig: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherConfig.html[LauncherConfig]
:LauncherDiscoveryListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherDiscoveryListener.html[LauncherDiscoveryListener]
:LauncherDiscoveryRequest: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherDiscoveryRequest.html[LauncherDiscoveryRequest]
-:PostDiscoveryFilter: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/PostDiscoveryFilter.html[PostDiscoveryFilter]
:LauncherDiscoveryRequestBuilder: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.html[LauncherDiscoveryRequestBuilder]
+:LauncherFactory: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherFactory.html[LauncherFactory]
+:LauncherInterceptor: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherInterceptor.html[LauncherInterceptor]
+:LauncherSession: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherSession.html[LauncherSession]
+:LauncherSessionListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherSessionListener.html[LauncherSessionListener]
:LoggingListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/LoggingListener.html[LoggingListener]
+:PostDiscoveryFilter: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/PostDiscoveryFilter.html[PostDiscoveryFilter]
:SummaryGeneratingListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/SummaryGeneratingListener.html[SummaryGeneratingListener]
:TestExecutionListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/TestExecutionListener.html[TestExecutionListener]
:TestPlan: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/TestPlan.html[TestPlan]
+:UniqueIdTrackingListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.html[UniqueIdTrackingListener]
// Platform Reporting
:LegacyXmlReportGeneratingListener: {javadoc-root}/org.junit.platform.reporting/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListener.html[LegacyXmlReportGeneratingListener]
+:OpenTestReportGeneratingListener: {javadoc-root}/org.junit.platform.reporting/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListener.html[OpenTestReportGeneratingListener]
// Platform Runner
:JUnitPlatform-Runner: {javadoc-root}/org.junit.platform.runnner/org/junit/platform/runner/JUnitPlatform.html[JUnitPlatform]
-// Platform Suite API
+// 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]
// 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]
@@ -56,18 +65,25 @@ endif::[]
:TestExecutionResultConditions: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/TestExecutionResultConditions.html[TestExecutionResultConditions]
// Jupiter Core API
:api-package: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/package-summary.html[org.junit.jupiter.api]
-:Alphanumeric: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.Alphanumeric.html[Alphanumeric]
-:MethodName: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.MethodName.html[MethodName]
: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]
+: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]
+:ClassOrderer_Random: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.Random.html[ClassOrderer.Random]
+:ClassOrderer: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.html[ClassOrderer]
:Disabled: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Disabled.html[@Disabled]
-:DisplayName: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.DisplayName.html[DisplayName]
+:MethodOrderer_Alphanumeric: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.Alphanumeric.html[MethodOrderer.Alphanumeric]
+:MethodOrderer_DisplayName: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.DisplayName.html[MethodOrderer.DisplayName]
+:MethodOrderer_MethodName: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.MethodName.html[MethodOrderer.MethodName]
+:MethodOrderer_OrderAnnotation: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.OrderAnnotation.html[MethodOrderer.OrderAnnotation]
+:MethodOrderer_Random: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.Random.html[MethodOrderer.Random]
:MethodOrderer: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.html[MethodOrderer]
+:Named: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Named.html[Named]
:Order: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Order.html[@Order]
-:OrderAnnotation: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.OrderAnnotation.html[OrderAnnotation]
-:Random: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.Random.html[Random]
:RepetitionInfo: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/RepetitionInfo.html[RepetitionInfo]
:TestInfo: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestInfo.html[TestInfo]
+:TestClassOrder: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestClassOrder.html[@TestClassOrder]
:TestMethodOrder: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestMethodOrder.html[@TestMethodOrder]
:TestReporter: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestReporter.html[TestReporter]
:TestTemplate: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestTemplate.html[@TestTemplate]
@@ -85,6 +101,7 @@ endif::[]
:BeforeAllCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeAllCallback.html[BeforeAllCallback]
:BeforeEachCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeEachCallback.html[BeforeEachCallback]
:BeforeTestExecutionCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.html[BeforeTestExecutionCallback]
+:ExecutableInvoker: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExecutableInvoker.html[ExecutableInvoker]
:ExecutionCondition: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExecutionCondition.html[ExecutionCondition]
:ExtendWith: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtendWith.html[@ExtendWith]
:ExtensionContext: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.html[ExtensionContext]
@@ -96,40 +113,47 @@ endif::[]
:TestExecutionExceptionHandler: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.html[TestExecutionExceptionHandler]
:TestInstanceFactory: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstanceFactory.html[TestInstanceFactory]
:TestInstancePostProcessor: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePostProcessor.html[TestInstancePostProcessor]
+:TestInstancePreConstructCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePreConstructCallback.html[TestInstancePreConstructCallback]
:TestInstancePreDestroyCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePreDestroyCallback.html[TestInstancePreDestroyCallback]
:TestTemplateInvocationContext: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestTemplateInvocationContext.html[TestTemplateInvocationContext]
:TestTemplateInvocationContextProvider: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestTemplateInvocationContextProvider.html[TestTemplateInvocationContextProvider]
:TestWatcher: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestWatcher.html[TestWatcher]
// Jupiter Conditions
:DisabledForJreRange: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledForJreRange.html[@DisabledForJreRange]
+:DisabledIf: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIf.html[@DisabledIf]
:DisabledIfEnvironmentVariable: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariable.html[@DisabledIfEnvironmentVariable]
:DisabledIfSystemProperty: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIfSystemProperty.html[@DisabledIfSystemProperty]
+:DisabledInNativeImage: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledInNativeImage.html[@DisabledInNativeImage]
:DisabledOnJre: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledOnJre.html[@DisabledOnJre]
:DisabledOnOs: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledOnOs.html[@DisabledOnOs]
-:DisabledIf: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIf.html[@DisabledIf]
:EnabledForJreRange: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledForJreRange.html[@EnabledForJreRange]
+:EnabledIf: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIf.html[@EnabledIf]
:EnabledIfEnvironmentVariable: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariable.html[@EnabledIfEnvironmentVariable]
:EnabledIfSystemProperty: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIfSystemProperty.html[@EnabledIfSystemProperty]
+:EnabledInNativeImage: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledInNativeImage.html[@EnabledInNativeImage]
:EnabledOnJre: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledOnJre.html[@EnabledOnJre]
:EnabledOnOs: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledOnOs.html[@EnabledOnOs]
-:EnabledIf: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIf.html[@EnabledIf]
:JRE: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/JRE.html[JRE]
// Jupiter I/O
: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]
+: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]
:EmptySource: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/EmptySource.html[@EmptySource]
: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]
// Jupiter Engine
:junit-jupiter-engine: {javadoc-root}/org.junit.jupiter.engine/org/junit/jupiter/engine/package-summary.html[junit-jupiter-engine]
// Jupiter Extension Implementations
:DisabledCondition: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DisabledCondition.java[DisabledCondition]
-:RepetitionInfoParameterResolver: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionInfoParameterResolver.java[RepetitionInfoParameterResolver]
+: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]
:TestInfoParameterResolver: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java[TestInfoParameterResolver]
:TestReporterParameterResolver: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java[TestReporterParameterResolver]
@@ -154,15 +178,19 @@ endif::[]
// Third-party Links
:API: https://apiguardian-team.github.io/apiguardian/docs/current/api/[@API]
:API_Guardian: https://github.com/apiguardian-team/apiguardian[@API Guardian]
-:AssertJ: https://joel-costigliola.github.io/assertj/[AssertJ]
+:AssertJ: https://assertj.github.io/doc/[AssertJ]
:Gitter: https://gitter.im/junit-team/junit5[Gitter]
:Hamcrest: https://hamcrest.org/JavaHamcrest/[Hamcrest]
+:Jimfs: https://google.github.io/jimfs/[Jimfs]
:Log4j: https://logging.apache.org/log4j/2.x/[Log4j]
: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]
: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]
:StackOverflow: https://stackoverflow.com/questions/tagged/junit5[Stack Overflow]
:Truth: https://truth.dev/[Truth]
+:OpenTestReporting: https://github.com/ota4j-team/open-test-reporting[Open Test Reporting]
+:OpenTestReportingCliTool: https://github.com/ota4j-team/open-test-reporting#cli-tool-for-validation-and-format-conversion[Open Test Reporting CLI Tool]
diff --git a/documentation/src/docs/asciidoc/release-notes/index.adoc b/documentation/src/docs/asciidoc/release-notes/index.adoc
index 82817f7d5100..ec787091b41e 100644
--- a/documentation/src/docs/asciidoc/release-notes/index.adoc
+++ b/documentation/src/docs/asciidoc/release-notes/index.adoc
@@ -8,7 +8,7 @@ Stefan Bechtold; Sam Brannen; Johannes Link; Matthias Merdes; Marc Philipp; Juli
:numbered!:
//
-This document contains the _change log_ for all JUnit 5 releases since 5.6 GA.
+This document contains the _change log_ for all JUnit 5 releases since 5.9 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,10 +16,12 @@ authors as well as build tool and IDE vendors.
include::{includedir}/link-attributes.adoc[]
-include::{basedir}/release-notes-5.7.0.adoc[]
+include::{basedir}/release-notes-5.10.0.adoc[]
-include::{basedir}/release-notes-5.6.2.adoc[]
+include::{basedir}/release-notes-5.9.3.adoc[]
-include::{basedir}/release-notes-5.6.1.adoc[]
+include::{basedir}/release-notes-5.9.2.adoc[]
-include::{basedir}/release-notes-5.6.0.adoc[]
+include::{basedir}/release-notes-5.9.1.adoc[]
+
+include::{basedir}/release-notes-5.9.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
new file mode 100644
index 000000000000..3141c2dbae97
--- /dev/null
+++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0.adoc
@@ -0,0 +1,173 @@
+[[release-notes-5.10.0]]
+== 5.10.0
+
+*Date of Release:* July 23, 2023
+
+*Scope:*
+
+* Promotion of various experimental APIs to stable
+* New `LauncherInterceptor` SPI
+* New `testfeed` details mode for `ConsoleLauncher`
+* New `ConsoleLauncher` subcommand for test discovery without execution
+* Dry-run mode for test execution
+* New `NamespacedHierarchicalStore` for use in third-party test engines
+* Stacktrace pruning to hide internal JUnit calls
+* New `@SelectMethod` support in test `@Suite` classes.
+* New `TempDirFactory` SPI for customizing how temporary directories are created
+* Failure threshold for `@RepeatedTest`
+* New convenience base classes for implementing `ArgumentsProvider` and `ArgumentConverter`
+* Custom class loader support for class/method selectors, `@MethodSource`, `@EnabledIf`,
+ and `@DisabledIf`
+* 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.
diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.6.0.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.6.0.adoc
deleted file mode 100644
index 472f6baa708d..000000000000
--- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.6.0.adoc
+++ /dev/null
@@ -1,22 +0,0 @@
-[[release-notes-5.6.0]]
-== 5.6.0
-
-*Date of Release:* January 20, 2020
-
-*Scope:*
-
-* New `@EnabledForJreRange` and `@DisabledForJreRange` execution conditions
-* `@Order` allows to specify relative order
-* Parameter names are included in default display names of parameterized test invocations
-* Improvements to `@CsvSource` and `@CsvFileSource`
-* New `TestInstancePreDestroyCallback` extension API
-* Performance improvements and bug fixes for the Vintage engine
-* Improved error reporting for failures during test discovery and execution
-* Support for using `any()` and `none()` in tag expressions
-* `org.junit.platform.console` now provides a `java.util.spi.ToolProvider`
-* `DiscoverySelectors` for tests in inherited nested classes
-* OSGi metadata
-* Minor bug fixes and improvements
-
-For complete details consult the
-https://junit.org/junit5/docs/5.6.0/release-notes/index.html[5.6.0 Release Notes] online.
diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.6.1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.6.1.adoc
deleted file mode 100644
index ba7fd3266eb3..000000000000
--- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.6.1.adoc
+++ /dev/null
@@ -1,36 +0,0 @@
-[[release-notes-5.6.1]]
-== 5.6.1
-
-*Date of Release:* March 22, 2020
-
-*Scope:* Bug fixes since 5.6.0
-
-For a complete list of all _closed_ issues and pull requests for this release, consult the
-link:{junit5-repo}+/milestone/47?closed=1+[5.6.1] milestone page in the JUnit repository
-on GitHub.
-
-
-[[release-notes-5.6.1-junit-platform]]
-=== JUnit Platform
-
-==== Bug Fixes
-
-* In order to avoid file locking issues on Microsoft Windows, URLs are no longer cached
- when loading `junit-platform.properties` files.
-* The presence of multiple `junit-platform.properties` files on the classpath now only
- results in a warning if the files have different URLs.
-
-
-[[release-notes-5.6.1-junit-jupiter]]
-=== JUnit Jupiter
-
-==== Bug Fixes
-
-* `TestInstancePreDestroyCallback` extensions are now invoked in reverse registration
- order when `PER_CLASS` test instance lifecycle semantics have been configured.
-
-
-[[release-notes-5.6.1-junit-vintage]]
-=== JUnit Vintage
-
-No changes.
diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.6.2.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.6.2.adoc
deleted file mode 100644
index 0da105d0ef82..000000000000
--- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.6.2.adoc
+++ /dev/null
@@ -1,39 +0,0 @@
-[[release-notes-5.6.2]]
-== 5.6.2
-
-*Date of Release:* April 10, 2020
-
-*Scope:* Bug fixes since 5.6.1
-
-For a complete list of all _closed_ issues and pull requests for this release, consult the
-link:{junit5-repo}+/milestone/48?closed=1+[5.6.2] milestone page in the JUnit repository
-on GitHub.
-
-
-[[release-notes-5.6.2-junit-platform]]
-=== JUnit Platform
-
-==== Bug Fixes
-
-* `ReflectionSupport.findNestedClasses()` no longer detects inner class cycles for classes
- that do not match the supplied `Predicate`. For example, JUnit Jupiter no longer throws
- an exception if an inner class cycle is detected in a nested class hierarchy whose inner
- classes are not annotated with `@Nested`.
-
-
-[[release-notes-5.6.2-junit-jupiter]]
-=== JUnit Jupiter
-
-==== Bug Fixes
-
-* Test discovery no longer halts with an exception for inner class hierarchies that form a
- cycle if such inner classes are not annotated with `@Nested`.
-
-
-[[release-notes-5.6.2-junit-vintage]]
-=== JUnit Vintage
-
-==== Bug Fixes
-
-* Generating display names from JUnit 4 `Descriptions` now falls back to `getDisplayName`
- if `getMethodName` returns a blank `String` instead of throwing an exception.
diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.7.0.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.7.0.adoc
deleted file mode 100644
index 972739531da1..000000000000
--- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.7.0.adoc
+++ /dev/null
@@ -1,210 +0,0 @@
-[[release-notes-5.7.0]]
-== 5.7.0
-
-*Date of Release:* September 13, 2020
-
-*Scope:*
-
-* Promotion of experimental features in JUnit Platform and Jupiter
-* Java Flight Recorder support
-* TestKit improvements
-* `@Isolated` tests
-* Configurable default method orderer
-* Custom disabled reasons for all `@Enabled*`/`@Disabled*` annotations
-* Improvements to `assertTimeoutPreemptively()`
-* Improvements to `@CsvFileSource` and `@CsvSource`
-
-
-For a complete list of all _closed_ issues and pull requests for this release, consult the
-link:{junit5-repo}+/milestone/44?closed=1+[5.7 M1],
-link:{junit5-repo}+/milestone/49?closed=1+[5.7 RC1], and
-link:{junit5-repo}+/milestone/50?closed=1+[5.7 GA] milestone pages in the JUnit repository
-on GitHub.
-
-
-[[release-notes-5.7.0-RC1-overall-improvements]]
-=== Overall Improvements
-
-* Javadoc JARs now contain `package-list` in addition to `element-list` for compatibility
- with tools like NetBeans 11.
-
-
-[[release-notes-5.7.0-junit-platform]]
-=== JUnit Platform
-
-==== Promoted Features
-
-The following APIs have been promoted from "experimental" status:
-
-* `LauncherConstants` is now _stable_
-* `LauncherConfig` (except for methods introduced in 5.7) is now _stable_
-* `LegacyXmlReportGeneratingListener` is now _stable_
-* `org.junit.platform.testkit.engine` is now _maintained_
-
-==== Bug Fixes
-
-* In XML reports generated by the `ConsoleLauncher` or
- `LegacyXmlReportGeneratingListener`, characters in exception messages and other
- user-supplied values that are not allowed in XML are now replaced with their character
- reference – for example, `\0` becomes ``.
-* The `Launcher` now throws an exception when a previously executed `TestPlan` is
- attempted to be executed again.
-
-==== Deprecations and Breaking Changes
-
-* In the `EngineTestKit` API, the `all()`, `containers()`, and `tests()` methods in
- `EngineExecutionResults` that were deprecated in JUnit Platform 1.6.0 have been removed
- in favor of `allEvents()`, `containerEvents()`, and `testEvents()`, respectively.
-* The following methods in `EngineTestKit` are now deprecated with replacements:
- - `execute(String, EngineDiscoveryRequest)` → `execute(String, LauncherDiscoveryRequest)`
- - `execute(TestEngine, EngineDiscoveryRequest)` → `execute(TestEngine, LauncherDiscoveryRequest)`
- - `Builder.filters(DiscoveryFilter...)` → `Builder.filters(Filter...)`
-* `EngineTestKit` no longer takes into account implicit configuration parameters (i.e.
- system properties and the `junit-platform.properties` classpath resource) by default.
- This change makes tests executed via `EngineTestKit` independent of the environment they
- are executed in.
-
-==== New Features and Improvements
-
-* The number of containers and tests excluded by post discovery filters based on their tags
- is now logged, along with the exclusion reasons.
-* New `junit.platform.execution.listeners.deactivate` configuration parameter that allows
- one to specify a comma-separated list of patterns for deactivating
- `TestExecutionListener` implementations registered via the `ServiceLoader` mechanism.
-* The `@Testable` annotation may now be applied _directly_ to fields.
-* New `Node.DynamicTestExecutor#execute(TestDescriptor, EngineExecutionListener)` method
- for engines that wish to provide a custom `EngineExecutionListener` and cancel or wait
- for the execution of a submitted test via the returned `Future`.
-* New `EngineExecutionListener.NOOP` `EngineExecutionListener` implementation.
-* All declared methods in the `EngineExecutionListener` API now have empty `default`
- implementations.
-* The `EngineTestKit` now reuses the same test discovery and execution logic as the
- `Launcher`. Thus, it's now possible to test an engine's behavior in the presence of
- post-discovery filters (e.g. tag filters) and with regard to pruning.
-* The `EngineTestKit` now supports matching conditions with events loosely, i.e. an
- incomplete match with or without a fixed order.
-* The `@Testable` annotation may now target any element type, including fields, methods,
- classes, packages, and modules.
-* When using the `ConsoleLauncher`, classes selected explicitly via `--select-class` and
- `--select-method` are now always executed regardless of class name patterns provided
- via `--include-classname` or the default class name pattern.
-* The `ConsoleLauncher` now honors the `--disable-ansi-colors` option when printing usage
- help.
-* New `FilePosition` support in `FileSelector` and `ClasspathResourceSelector`.
-* New `getJavaClass()` and `getJavaMethod()` methods in
- `org.junit.platform.engine.support.descriptor.MethodSource`.
-* Custom `PostDiscoveryFilter` implementations can now be registered via Java’s
- `ServiceLoader` mechanism.
-* New `org.junit.platform.jfr` module. When running on Java 11 or later, it provides and
- registers a `TestExecutionListener` that generates Java Flight Recorder events.
-* The `ExecutionRecorder` in `junit-platform-testkit` is now public for fine-grained
- testing of classes that use `EngineExecutionListener`.
-* `ForkJoinPoolHierarchicalTestExecutorService` can now be constructed by supplying a
- `ParallelExecutionConfiguration`.
-* `HierarchicalTestEngine` now supports a global resource lock.
-
-
-[[release-notes-5.7.0-junit-jupiter]]
-=== JUnit Jupiter
-
-==== Promoted Features
-
-The following APIs have been promoted from _experimental_ to _stable_:
-
-* `junit-jupiter-migrationsupport`
-* `junit-jupiter-params` (i.e. `@ParameterizedTest` etc.)
-* `@TestMethodOrder`, `MethodOrderer`, and its pre-5.7 implementations
-* `@DisplayNameGeneration`, `DisplayNameGenerator`, and its pre-5.7 implementations
-* `@Timeout`
-* `TestInstanceFactory`
-* `TestInstancePreDestroyCallback`
-* `TestInstances` and corresponding `ExtensionContext` methods
-* `TestWatcher`
-* Kotlin-specific assertions that were introduced in 5.1
-
-==== Bug Fixes
-
-* `@TempDir` is now able to clean up files in read-only directories.
-* The Jupiter engine now ignores `MethodSelectors` for methods in non-Jupiter test
- classes instead of failing for missing methods in such cases.
-* `CloseableResource` instances stored in `ExtensionContext.Store` are now closed in the
- reverse order they were added in. Previously, the order was undefined and unstable.
-* Inherited `@BeforeEach` methods are now executed on correct instances for `@Nested`
- classes.
-* Registered `TestInstancePreDestroyCallback` extensions are now always called if an
- instance of a test class was created, regardless whether any registered
- `TestInstancePostProcessor` extension threw an exception.
-* Disabled `@TestTemplate` methods (e.g. `@ParameterizedTest` and `@RepeatedTest` methods)
- are now reported to registered `TestWatcher` extensions.
-
-==== Deprecations and Breaking Changes
-
-* `MethodOrderer.Alphanumeric` has been deprecated in favor of `MethodOrderer.MethodName`
- which provides the exact same functionality but has a more descriptive name.
-
-==== New Features and Improvements
-
-* New `@EnabledIf` and `@DisabledIf` annotations can be used to enable or disable a test
- or container based on condition methods.
-* New `MethodOrderer` named `DisplayName` that sorts test methods alphanumerically based
- on their display names.
-* New `DisplayNameGenerator` named `Simple` (based on `Standard`) that removes trailing
- parentheses for methods with no parameters.
-* `assertThrows()` for Kotlin can now be used with suspending functions and other lambda
- contexts that require inlining.
-* The `JRE` enum now provides a static `currentVersion()` method that returns the enum
- constant for the currently executing JRE, e.g. for use in custom execution conditions
- and other extensions.
-* The `name` attribute of `@ParameterizedTest` is now clearly documented to be a
- `MessageFormat` pattern.
-* Synthetic constructors are now ignored when instantiating a test class.
-* The Javadoc for the `provideTestTemplateInvocationContexts()` method in
- `TestTemplateInvocationContextProvider` has been aligned with the actual implementation.
- Providers are now officially allowed to return an empty stream, and the error message
- when all provided streams are empty is now more helpful.
-* New `getDisplayName()` method in `MethodDescriptor` for use in `MethodOrderer`
- implementations.
-* New `assertLinesMatch()` method overloads in `Assertions` that accept two
- `Stream` instances for comparison.
-* `assertTimeoutPreemptively()` in `Assertions` now reports the stack trace of the timed
- out thread in the cause of the `AssertionFailedError`.
-* `assertTimeoutPreemptively()` now uses threads with a specific name, conveying their use
- by the framework, to facilitate debugging and stack trace analysis.
-* All `@Enabled*`/`@Disabled*` annotations now have an optional `disabledReason` attribute
- that can be used to provide an additional explanation as to why a test or container
- might be disabled.
-* `JAVA_16` has been added to the `JRE` enum for use with JRE-based execution conditions.
-* New `MethodOrderer.MethodName` to replace `MethodOrderer.Alphanumeric` with the exact
- same functionality but a more descriptive name.
-* New `junit.jupiter.testmethod.order.default` configuration parameter to set the default
- `MethodOrderer` that will be used unless `@TestMethodOrder` is present.
-* New `DynamicTest.stream()` factory method that accepts a `Stream` instead of an
- `Iterator` for the input source.
-* `@CsvFileSource` now allows one to specify file paths as an alternative to classpath
- resources.
-* `@CsvFileSource` and `@CsvSource` now provide a `maxCharsPerColumn` attribute for
- configuring the maximum number of characters per column.
-* Arguments in display names of parameterized test invocations are now truncated if they
- exceed a configurable maximum length (defaults to 512 characters).
-* New `@Isolated` annotation allows to run test classes in isolation of other test classes
- when using parallel test execution.
-* New `TypedArgumentConverter` for converting one specific type to another, therefore
- reducing boilerplate type checks compared to implementing `ArgumentConverter` directly.
-* New `ExtensionContext.getConfigurationParameter(String, Function)`
- convenience method for reading transformed configuration parameters from extensions.
-
-
-[[release-notes-5.7.0-junit-vintage]]
-=== JUnit Vintage
-
-==== Bug Fixes
-
-* The Vintage engine no longer fails when resolving a `MethodSelector` for methods of test
- classes that cannot be found via reflection. This allows selecting Spock feature methods
- by their source code name even though they have a generated method name in the bytecode.
-
-==== New Features and Improvements
-
-* The internal `JUnit4VersionCheck` class -- which verifies that a supported version of
- JUnit 4 is on the classpath -- now implements a lenient version ID parsing algorithm in
- order to support custom version ID formats such as `4.12.0`, `4.12-patch_1`, etc.
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
new file mode 100644
index 000000000000..e5ee0004d778
--- /dev/null
+++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.0.adoc
@@ -0,0 +1,21 @@
+[[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
new file mode 100644
index 000000000000..3c82815368e1
--- /dev/null
+++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.1.adoc
@@ -0,0 +1,53 @@
+[[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
new file mode 100644
index 000000000000..249e805dd0f0
--- /dev/null
+++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.2.adoc
@@ -0,0 +1,61 @@
+[[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
new file mode 100644
index 000000000000..6d1020227e39
--- /dev/null
+++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.9.3.adoc
@@ -0,0 +1,59 @@
+[[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 2952e59175fe..ef56e8c27ff9 100644
--- a/documentation/src/docs/asciidoc/release-notes/release-notes-TEMPLATE.adoc
+++ b/documentation/src/docs/asciidoc/release-notes/release-notes-TEMPLATE.adoc
@@ -1,18 +1,31 @@
-// TODO: replace all occurrences of ⚠️ with appropriate values, delete this comment, and
-// 'include:' this new file in index.adoc.
-[[release-notes-⚠️]]
-== ⚠️
+// TODO:
+//
+// 1) Make a copy of this template file, replacing TEMPLATE in the file name with the
+// current version (for example, 5.10.0-M1, 5.10.0-RC1, 5.10.0).
+// 2) Open the new file for editing.
+// 3) Replace all occurrences of VERSION with the current version (for example, 5.10.0-M1,
+// 5.10.0-RC1, 5.10.0). The same version must be used in the file name and within the
+// file.
+// 4) Replace MILESTONE_NUMBER with the appropriate milestone number. This is an integer
+// which you can determine via https://github.com/junit-team/junit5/milestones/. If a
+// GitHub milestone does not yet exist for the given VERSION, you will need to create
+// a new GitHub milestone or ask a member of the JUnit team to create it for you.
+// 5) 'include:' this new file in index.adoc.
+// 6) Delete this entire comment block.
+//
+[[release-notes-VERSION]]
+== VERSION
*Date of Release:* ❓
*Scope:* ❓
-For a complete list of all _closed_ issues and pull requests for this release, consult
-the link:{junit5-repo}+/milestone/⚠️?closed=1+[⚠️] milestone page in the JUnit repository
-on GitHub.
+For a complete list of all _closed_ issues and pull requests for this release, consult the
+link:{junit5-repo}+/milestone/MILESTONE_NUMBER?closed=1+[VERSION] milestone page in the
+JUnit repository on GitHub.
-[[release-notes-⚠️-junit-platform]]
+[[release-notes-VERSION-junit-platform]]
=== JUnit Platform
==== Bug Fixes
@@ -28,7 +41,7 @@ on GitHub.
* ❓
-[[release-notes-⚠️-junit-jupiter]]
+[[release-notes-VERSION-junit-jupiter]]
=== JUnit Jupiter
==== Bug Fixes
@@ -44,7 +57,7 @@ on GitHub.
* ❓
-[[release-notes-⚠️-junit-vintage]]
+= [[release-notes-VERSION-junit-vintage]]
=== JUnit Vintage
==== Bug Fixes
diff --git a/documentation/src/docs/asciidoc/resources/themes/rouge_junit.rb b/documentation/src/docs/asciidoc/resources/themes/rouge_junit.rb
new file mode 100644
index 000000000000..003a88b51955
--- /dev/null
+++ b/documentation/src/docs/asciidoc/resources/themes/rouge_junit.rb
@@ -0,0 +1,148 @@
+require 'rouge' unless defined? ::Rouge.version
+
+module Rouge; module Themes
+
+ class JUnit < CSSTheme
+ name 'junit'
+
+ # This is an extension of the official "github" theme to remove accessibility issues in code blocks
+ # Primer primitives (https://github.com/primer/primitives/tree/main/src/tokens)
+ P_RED_0 = {:light => '#ffebe9', :dark => '#ffdcd7'}
+ P_RED_3 = {:dark => '#ff7b72'}
+ P_RED_5 = {:light => '#cf222e'}
+ P_RED_7 = {:light => '#82071e', :dark => '#8e1519'}
+ P_RED_8 = {:dark => '#67060c'}
+ P_ORANGE_2 = {:dark => '#ffa657'}
+ P_ORANGE_6 = {:light => '#953800'}
+ P_GREEN_0 = {:light => '#dafbe1', :dark => '#aff5b4'}
+ P_GREEN_1 = {:dark => '#7ee787'}
+ P_GREEN_6 = {:light => '#116329'}
+ P_GREEN_8 = {:dark => '#033a16'}
+ P_BLUE_1 = {:dark => '#a5d6ff'}
+ P_BLUE_2 = {:dark => '#79c0ff'}
+ P_BLUE_5 = {:dark => '#1f6feb'}
+ P_BLUE_6 = {:light => '#0550ae'}
+ P_BLUE_7 = {:light => '#055099'}
+ P_BLUE_8 = {:light => '#0a3069'}
+ P_PURPLE_2 = {:dark => '#d2a8ff'}
+ P_PURPLE_5 = {:light => '#8250df'}
+ P_GRAY_0 = {:light => '#f6f8fa', :dark => '#f0f6fc'}
+ P_GRAY_1 = {:dark => '#c9d1d9'}
+ P_GRAY_3 = {:dark => '#8b949e'}
+ P_GRAY_5 = {:light => '#34383d'} # '#6e7781'
+ P_GRAY_8 = {:dark => '#161b22'}
+ P_GRAY_9 = {:light => '#24292f'}
+
+ extend HasModes
+
+ def self.light!
+ mode :dark # indicate that there is a dark variant
+ mode! :light
+ end
+
+ def self.dark!
+ mode :light # indicate that there is a light variant
+ mode! :dark
+ end
+
+ def self.make_dark!
+ palette :comment => P_GRAY_3[@mode]
+ palette :constant => P_BLUE_2[@mode]
+ palette :entity => P_PURPLE_2[@mode]
+ palette :heading => P_BLUE_5[@mode]
+ palette :keyword => P_RED_3[@mode]
+ palette :string => P_BLUE_1[@mode]
+ palette :tag => P_GREEN_1[@mode]
+ palette :variable => P_ORANGE_2[@mode]
+
+ palette :fgDefault => P_GRAY_1[@mode]
+ palette :bgDefault => P_GRAY_8[@mode]
+
+ palette :fgInserted => P_GREEN_0[@mode]
+ palette :bgInserted => P_GREEN_8[@mode]
+
+ palette :fgDeleted => P_RED_0[@mode]
+ palette :bgDeleted => P_RED_8[@mode]
+
+ palette :fgError => P_GRAY_0[@mode]
+ palette :bgError => P_RED_7[@mode]
+ end
+
+ def self.make_light!
+ palette :comment => P_GREEN_6[@mode]
+ palette :constant => P_BLUE_6[@mode]
+ palette :entity => P_PURPLE_5[@mode]
+ palette :heading => P_BLUE_6[@mode]
+ palette :keyword => P_RED_5[@mode]
+ palette :string => P_BLUE_8[@mode]
+ palette :tag => P_BLUE_7[@mode]
+ palette :variable => P_ORANGE_6[@mode]
+
+ palette :fgDefault => P_GRAY_9[@mode]
+ palette :bgDefault => P_GRAY_0[@mode]
+
+ palette :fgInserted => P_GREEN_6[@mode]
+ palette :bgInserted => P_GREEN_0[@mode]
+
+ palette :fgDeleted => P_RED_7[@mode]
+ palette :bgDeleted => P_RED_0[@mode]
+
+ palette :fgError => P_GRAY_0[@mode]
+ palette :bgError => P_RED_7[@mode]
+ end
+
+ light!
+
+ style Text, :fg => :fgDefault, :bg => :bgDefault
+
+ style Keyword, :fg => :keyword
+
+ style Generic::Error, :fg => :fgError
+
+ style Generic::Deleted, :fg => :fgDeleted, :bg => :bgDeleted
+
+ style Name::Builtin,
+ Name::Class,
+ Name::Constant,
+ Name::Namespace, :fg => :variable
+
+ style Literal::String::Regex,
+ Name::Attribute,
+ Name::Tag, :fg => :tag
+
+ style Generic::Inserted, :fg => :fgInserted, :bg => :bgInserted
+
+ style Keyword::Constant,
+ Literal,
+ Literal::String::Backtick,
+ Name::Builtin::Pseudo,
+ Name::Exception,
+ Name::Label,
+ Name::Property,
+ Name::Variable,
+ Operator, :fg => :constant
+
+ style Generic::Heading,
+ Generic::Subheading, :fg => :heading, :bold => true
+
+ style Literal::String, :fg => :string
+
+ style Name::Decorator,
+ Name::Function, :fg => :entity
+
+ style Error, :fg => :fgError, :bg => :bgError
+
+ style Comment,
+ Generic::Lineno,
+ Generic::Traceback, :fg => :comment
+
+ style Name::Entity,
+ Literal::String::Interpol, :fg => :fgDefault
+
+ style Generic::Emph, :fg => :fgDefault, :italic => true
+
+ style Generic::Strong, :fg => :fgDefault, :bold => true
+
+ end
+end; end
+
diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics.adoc b/documentation/src/docs/asciidoc/user-guide/advanced-topics.adoc
index 53164790e5b2..ab7f4d11463a 100644
--- a/documentation/src/docs/asciidoc/user-guide/advanced-topics.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics.adoc
@@ -1,10 +1,12 @@
[[advanced-topics]]
== Advanced Topics
-include::launcher-api.adoc[]
+include::advanced-topics/junit-platform-reporting.adoc[]
-include::testkit.adoc[]
+include::advanced-topics/junit-platform-suite-engine.adoc[]
-////
-include::engines.adoc[]
-////
+include::advanced-topics/testkit.adoc[]
+
+include::advanced-topics/launcher-api.adoc[]
+
+include::advanced-topics/engines.adoc[]
diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics/engines.adoc b/documentation/src/docs/asciidoc/user-guide/advanced-topics/engines.adoc
new file mode 100644
index 000000000000..460a6fd4ba3c
--- /dev/null
+++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics/engines.adoc
@@ -0,0 +1,122 @@
+[[test-engines]]
+=== Test Engines
+
+A `TestEngine` facilitates _discovery_ and _execution_ of tests for a particular
+programming model.
+
+For example, JUnit provides a `TestEngine` that discovers and executes tests written using
+the JUnit Jupiter programming model (see <> and <>).
+
+[[test-engines-junit]]
+==== JUnit Test Engines
+
+JUnit provides three `TestEngine` implementations.
+
+* `{junit-jupiter-engine}`: The core of JUnit Jupiter.
+* `{junit-vintage-engine}`: A thin layer on top of JUnit 4 to allow running _vintage_
+ tests (based on JUnit 3.8 and JUnit 4) with the JUnit Platform launcher infrastructure.
+* `{junit-platform-suite-engine}`: Executes declarative suites of tests with the JUnit
+ Platform launcher infrastructure.
+
+[[test-engines-custom]]
+==== Custom Test Engines
+
+You can contribute your own custom `{TestEngine}` by implementing the interfaces in the
+{junit-platform-engine} module and _registering_ your engine.
+
+Every `TestEngine` must provide its own _unique ID_, _discover_ tests from an
+`EngineDiscoveryRequest`, and _execute_ those tests according to an `ExecutionRequest`.
+
+[WARNING]
+.The `junit-` unique ID prefix is reserved for TestEngines from the JUnit Team
+====
+The JUnit Platform `Launcher` enforces that only `TestEngine` implementations published
+by the JUnit Team may use the `junit-` prefix for their `TestEngine` IDs.
+
+* If any third-party `TestEngine` claims to be `junit-jupiter` or `junit-vintage`, an
+ exception will be thrown, immediately halting execution of the JUnit Platform.
+* If any third-party `TestEngine` uses the `junit-` prefix for its ID, a warning message
+ will be logged. Later releases of the JUnit Platform will throw an exception for such
+ violations.
+====
+
+In order to facilitate test discovery within IDEs and tools prior to launching the JUnit
+Platform, `TestEngine` implementations are encouraged to make use of the `@Testable`
+annotation. For example, the `@Test` and `@TestFactory` annotations in JUnit Jupiter are
+meta-annotated with `@Testable`. Consult the Javadoc for `{Testable}` for further details.
+
+If your custom `TestEngine` needs to be configured, consider allowing users to supply
+configuration via <>. Please note,
+however, that you are strongly encouraged to use a unique prefix for all configuration
+parameters supported by your test engine. Doing so will ensure that there are no conflicts
+between the names of your configuration parameters and those from other test engines. In
+addition, since configuration parameters may be supplied as JVM system properties, it is
+wise to avoid conflicts with the names of other system properties. For example, JUnit
+Jupiter uses `junit.jupiter.` as a prefix of all of its supported configuration
+parameters. Furthermore, as with the warning above regarding the `junit-` prefix for
+`TestEngine` IDs, you should not use `junit.` as a prefix for the names of your own
+configuration parameters.
+
+Although there is currently no official guide on how to implement a custom `TestEngine`,
+you can consult the implementation of <> or the implementation of
+third-party test engines listed in the
+https://github.com/junit-team/junit5/wiki/Third-party-Extensions#junit-platform-test-engines[JUnit 5 wiki].
+You will also find various tutorials and blogs on the Internet that demonstrate how to
+write a custom `TestEngine`.
+
+NOTE: `{HierarchicalTestEngine}` is a convenient abstract base implementation of the
+`TestEngine` SPI (used by the `{junit-jupiter-engine}`) that only requires implementors to
+provide the logic for test discovery. It implements execution of `TestDescriptors` that
+implement the `Node` interface, including support for parallel execution.
+
+[[test-engines-registration]]
+==== Registering a TestEngine
+
+`TestEngine` registration is supported via Java's `{ServiceLoader}` mechanism.
+
+For example, the `junit-jupiter-engine` module registers its
+`org.junit.jupiter.engine.JupiterTestEngine` in a file named
+`org.junit.platform.engine.TestEngine` within the `/META-INF/services` folder in the
+`junit-jupiter-engine` JAR.
+
+[[test-engines-requirements]]
+==== Requirements
+
+NOTE: The words "must", "must not", "required", "shall", "shall not", "should", "should
+not", "recommended", "may", and "optional" in this section are to be interpreted as
+described in https://www.ietf.org/rfc/rfc2119.txt[RFC 2119.]
+
+[[test-engines-requirements-mandatory]]
+===== Mandatory requirements
+
+For interoperability with build tools and IDEs, `TestEngine` implementations must adhere
+to the following requirements:
+
+* The `TestDescriptor` returned from `TestEngine.discover()` _must_ be the root of a tree
+ of `TestDescriptor` instances. This implies that there _must not_ be any cycles between
+ a node and its descendants.
+* A `TestEngine` _must_ be able to discover `UniqueIdSelectors` for any unique ID that it
+ previously generated and returned from `TestEngine.discover()`. This enables selecting a
+ subset of tests to execute or rerun.
+* The `executionSkipped`, `executionStarted`, and `executionFinished` methods of the
+ `EngineExecutionListener` passed to `TestEngine.execute()` _must_ be called for every
+ `TestDescriptor` node in the tree returned from `TestEngine.discover()` at most
+ once. Parent nodes _must_ be reported as started before their children and as finished
+ after their children. If a node is reported as skipped, there _must not_ be any events
+ reported for its descendants.
+
+[[test-engines-requirements-enhanced-compatibility]]
+===== Enhanced compatibility
+
+Adhering to the following requirements is optional but recommended for enhanced
+compatibility with build tools and IDEs:
+
+* Unless to indicate an empty discovery result, the `TestDescriptor` returned from
+ `TestEngine.discover()` _should_ have children rather than being completely dynamic.
+ This allows tools to display the structure of the tests and to select a subset of tests
+ to execute.
+* When resolving `UniqueIdSelectors`, a `TestEngine` _should_ only return `TestDescriptor`
+ instances with matching unique IDs including their ancestors but _may_ return additional
+ siblings or other nodes that are required for the execution of the selected tests.
+* `TestEngines` _should_ support <> tests and containers so
+ that tag filters can be applied when discovering tests.
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
new file mode 100644
index 000000000000..655d6bc7903b
--- /dev/null
+++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-reporting.adoc
@@ -0,0 +1,138 @@
+[[junit-platform-reporting]]
+=== JUnit Platform Reporting
+
+The `junit-platform-reporting` artifact contains `{TestExecutionListener}` implementations
+that generate XML test reports in two flavors:
+<> and
+<>.
+
+NOTE: The module also contains other `TestExecutionListener` implementations that can be
+used to build custom reporting. See <> for details.
+
+[[junit-platform-reporting-legacy-xml]]
+==== Legacy XML format
+
+`{LegacyXmlReportGeneratingListener}` generates a separate XML report for each root in the
+`{TestPlan}`. Note that the generated XML format is compatible with the de facto standard
+for JUnit 4 based test reports that was made popular by the Ant build system.
+
+The `LegacyXmlReportGeneratingListener` is used by the <>
+as well.
+
+[[junit-platform-reporting-open-test-reporting]]
+==== Open Test Reporting XML format
+
+`{OpenTestReportGeneratingListener}` writes an XML report for the entire execution in the
+event-based format specified by {OpenTestReporting} which supports all features of the
+JUnit Platform such as hierarchical test structures, display names, tags, etc.
+
+The listener is auto-registered and can be configured via the following
+<>:
+
+`junit.platform.reporting.open.xml.enabled=true|false`::
+ Enable/disable writing the report.
+`junit.platform.reporting.output.dir=`::
+ Configure the output directory for the reports. By default, `build` is used if a Gradle
+ build script is found, and `target` if a Maven POM is found; otherwise, the current
+ working directory is used.
+
+If enabled, the listener creates an XML report file named
+`junit-platform-events-.xml` per test run in the configured output directory.
+
+TIP: The {OpenTestReportingCliTool} can be used to convert from the event-based format to
+the hierarchical format which is more human-readable.
+
+===== Gradle
+
+For Gradle, writing Open Test Reporting compatible XML reports can be enabled and
+configured via system properties. The following samples configure its output directory to
+be the same directory Gradle uses for its own XML reports. A `CommandLineArgumentProvider`
+is used to keep the tasks relocatable across different machines which is important when
+using Gradle's Build Cache.
+
+[source,groovy,indent=0]
+[subs=attributes+]
+.Groovy DSL
+----
+dependencies {
+ testRuntimeOnly("org.junit.platform:junit-platform-reporting:{platform-version}")
+}
+tasks.withType(Test).configureEach {
+ def outputDir = reports.junitXml.outputLocation
+ jvmArgumentProviders << ({
+ [
+ "-Djunit.platform.reporting.open.xml.enabled=true",
+ "-Djunit.platform.reporting.output.dir=${outputDir.get().asFile.absolutePath}"
+ ]
+ } as CommandLineArgumentProvider)
+}
+----
+
+[source,kotlin,indent=0]
+[subs=attributes+]
+.Kotlin DSL
+----
+dependencies {
+ testRuntimeOnly("org.junit.platform:junit-platform-reporting:{platform-version}")
+}
+tasks.withType().configureEach {
+ val outputDir = reports.junitXml.outputLocation
+ jvmArgumentProviders += CommandLineArgumentProvider {
+ listOf(
+ "-Djunit.platform.reporting.open.xml.enabled=true",
+ "-Djunit.platform.reporting.output.dir=${outputDir.get().asFile.absolutePath}"
+ )
+ }
+}
+----
+
+===== Maven
+
+For Maven Surefire/Failsafe, you can enable Open Test Reporting output and configure the
+resulting XML files to be written to the same directory Surefire/Failsafe uses for its own
+XML reports as follows:
+
+[source,xml,indent=0]
+[subs=attributes+]
+----
+
+
+
+
+ org.junit.platform
+ junit-platform-reporting
+ {platform-version}
+ test
+
+
+
+
+
+ maven-surefire-plugin
+ {surefire-version}
+
+
+
+ junit.platform.reporting.open.xml.enabled = true
+ junit.platform.reporting.output.dir = target/surefire-reports
+
+
+
+
+
+
+
+
+----
+
+===== Console Launcher
+
+When using the <>, you can enable Open Test Reporting
+output by setting the configuration parameters via `--config`:
+
+[source,console,subs=attributes+]
+----
+$ java -jar junit-platform-console-standalone-{platform-version}.jar \
+ --config=junit.platform.reporting.open.xml.enabled=true \
+ --config=junit.platform.reporting.output.dir=reports
+----
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
new file mode 100644
index 000000000000..d38a312d799f
--- /dev/null
+++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-suite-engine.adoc
@@ -0,0 +1,50 @@
+[[junit-platform-suite-engine]]
+=== JUnit Platform Suite Engine
+
+The JUnit Platform supports the declarative definition and execution of suites of tests
+from _any_ test engine using the JUnit Platform.
+
+[[junit-platform-suite-engine-setup]]
+==== Setup
+
+In addition to the `junit-platform-suite-api` and `junit-platform-suite-engine` artifacts,
+you need _at least one_ other test engine and its dependencies on the classpath. See
+<> for details regarding group IDs, artifact IDs, and versions.
+
+[[junit-platform-suite-engine-setup-required-dependencies]]
+===== Required Dependencies
+
+* `junit-platform-suite-api` in _test_ scope: artifact containing annotations needed to
+ configure a test suite
+* `junit-platform-suite-engine` in _test runtime_ scope: implementation of the
+ `TestEngine` API for declarative test suites
+
+NOTE: Both of the required dependencies are aggregated in the `junit-platform-suite`
+artifact which can be declared in _test_ scope instead of declaring explicit dependencies
+on `junit-platform-suite-api` and `junit-platform-suite-engine`.
+
+[[junit-platform-suite-engine-setup-transitive-dependencies]]
+===== Transitive Dependencies
+
+* `junit-platform-suite-commons` in _test_ scope
+* `junit-platform-launcher` in _test_ scope
+* `junit-platform-engine` in _test_ scope
+* `junit-platform-commons` in _test_ scope
+* `opentest4j` in _test_ scope
+
+[[junit-platform-suite-engine-example]]
+==== @Suite Example
+
+By annotating a class with `@Suite` it is marked as a test suite on the JUnit Platform.
+As seen in the following example, selector and filter annotations can then be used to
+control the contents of the suite.
+
+[source,java,indent=0]
+----
+include::{testDir}/example/SuiteDemo.java[tags=user_guide]
+----
+
+.Additional Configuration Options
+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.
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
new file mode 100644
index 000000000000..18c0b261e299
--- /dev/null
+++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics/launcher-api.adoc
@@ -0,0 +1,277 @@
+[[launcher-api]]
+=== JUnit Platform Launcher API
+
+One of the prominent goals of JUnit 5 is to make the interface between JUnit and its
+programmatic clients – build tools and IDEs – more powerful and stable. The purpose is to
+decouple the internals of discovering and executing tests from all the filtering and
+configuration that's necessary from the outside.
+
+JUnit 5 introduces the concept of a `Launcher` that can be used to discover, filter, and
+execute tests. Moreover, third party test libraries – like Spock, Cucumber, and FitNesse
+– can plug into the JUnit Platform's launching infrastructure by providing a custom
+<>.
+
+The launcher API is in the `{junit-platform-launcher}` module.
+
+An example consumer of the launcher API is the `{ConsoleLauncher}` in the
+`{junit-platform-console}` project.
+
+[[launcher-api-discovery]]
+==== Discovering Tests
+
+Having _test discovery_ as a dedicated feature of the platform itself frees IDEs and build
+tools from most of the difficulties they had to go through to identify test classes and
+test methods in previous versions of JUnit.
+
+Usage Example:
+
+[source,java,indent=0]
+----
+include::{testDir}/example/UsingTheLauncherDemo.java[tags=imports]
+----
+
+[source,java,indent=0]
+----
+include::{testDir}/example/UsingTheLauncherDemo.java[tags=discovery]
+----
+
+You can select classes, methods, and all classes in a package or even search for all tests
+in the class-path or module-path. Discovery takes place across all participating test
+engines.
+
+The resulting `TestPlan` is a hierarchical (and read-only) description of all engines,
+classes, and test methods that fit the `LauncherDiscoveryRequest`. The client can
+traverse the tree, retrieve details about a node, and get a link to the original source
+(like class, method, or file position). Every node in the test plan has a _unique ID_
+that can be used to invoke a particular test or group of tests.
+
+Clients can register one or more `{LauncherDiscoveryListener}` implementations via the
+`{LauncherDiscoveryRequestBuilder}` to gain insight into events that occur during test
+discovery. By default, the builder registers an "abort on failure" listener that aborts
+test discovery after the first discovery failure is encountered. The default
+`LauncherDiscoveryListener` can be changed via the
+`junit.platform.discovery.listener.default` <>.
+
+[[launcher-api-execution]]
+==== Executing Tests
+
+To execute tests, clients can use the same `LauncherDiscoveryRequest` as in the discovery
+phase or create a new request. Test progress and reporting can be achieved by registering
+one or more `{TestExecutionListener}` implementations with the `Launcher` as in the
+following example.
+
+[source,java,indent=0]
+----
+include::{testDir}/example/UsingTheLauncherDemo.java[tags=execution]
+----
+
+There is no return value for the `execute()` method, but you can use a
+`TestExecutionListener` to aggregate the results. For examples see the
+`{SummaryGeneratingListener}`, `{LegacyXmlReportGeneratingListener}`, and
+`{UniqueIdTrackingListener}`.
+
+NOTE: All `TestExecutionListener` methods are called sequentially. Methods for start
+events are called in registration order while methods for finish events are called in
+reverse order.
+Test case execution won't start before all `executionStarted` calls have returned.
+
+[[launcher-api-engines-custom]]
+==== Registering a TestEngine
+
+See the dedicated section on <> for
+details.
+
+[[launcher-api-post-discovery-filters-custom]]
+==== Registering a PostDiscoveryFilter
+
+In addition to specifying post-discovery filters as part of a `{LauncherDiscoveryRequest}`
+passed to the `{Launcher}` API, `{PostDiscoveryFilter}` implementations will be discovered
+at runtime via Java's `{ServiceLoader}` mechanism and automatically applied by the
+`Launcher` in addition to those that are part of the request.
+
+For example, an `example.CustomTagFilter` class implementing `PostDiscoveryFilter` and
+declared within the `/META-INF/services/org.junit.platform.launcher.PostDiscoveryFilter`
+file is loaded and applied automatically.
+
+[[launcher-api-launcher-session-listeners-custom]]
+==== Registering a LauncherSessionListener
+
+Registered implementations of `{LauncherSessionListener}` are notified when a
+`{LauncherSession}` is opened (before a `{Launcher}` first discovers and executes tests)
+and closed (when no more tests will be discovered or executed). They can be registered
+programmatically via the `{LauncherConfig}` that is passed to the `{LauncherFactory}`, or
+they can be discovered at runtime via Java's `{ServiceLoader}` mechanism and automatically
+registered with `LauncherSession` (unless automatic registration is disabled.)
+
+[[launcher-api-launcher-session-listeners-tool-support]]
+===== Tool Support
+
+The following build tools and IDEs are known to provide full support for `LauncherSession`:
+
+* Gradle 4.6 and later
+* Maven Surefire/Failsafe 3.0.0-M6 and later
+* IntelliJ IDEA 2017.3 and later
+
+Other tools might also work but have not been tested explicitly.
+
+[[launcher-api-launcher-session-listeners-tool-example-usage]]
+===== Example Usage
+
+A `LauncherSessionListener` is well suited for implementing once-per-JVM setup/teardown
+behavior since it's called before the first and after the last test in a launcher session,
+respectively. The scope of a launcher session depends on the used IDE or build tool but
+usually corresponds to the lifecycle of the test JVM. A custom listener that starts an
+HTTP server before executing the first test and stops it after the last test has been
+executed, could look like this:
+
+[source,java]
+.src/test/java/example/session/GlobalSetupTeardownListener.java
+----
+package example.session;
+
+include::{testDir}/example/session/GlobalSetupTeardownListener.java[tags=user_guide]
+----
+<1> Start the HTTP server
+<2> Export its host address as a system property for consumption by tests
+<3> Export its port as a system property for consumption by tests
+<4> Stop the HTTP server
+
+This sample uses the HTTP server implementation from the jdk.httpserver module that comes
+with the JDK but would work similarly with any other server or resource. In order for the
+listener to be picked up by JUnit Platform, you need to register it as a service by adding
+a resource file with the following name and contents to your test runtime classpath (e.g.
+by adding the file to `src/test/resources`):
+
+[source]
+.src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener
+----
+include::{testResourcesDir}/META-INF/services/org.junit.platform.launcher.LauncherSessionListener[]
+----
+
+You can now use the resource from your test:
+
+[source,java]
+.src/test/java/example/session/HttpTests.java
+----
+package example.session;
+
+include::{testDir}/example/session/HttpTests.java[tags=user_guide]
+----
+<1> Read the host address of the server from the system property set by the listener
+<2> Read the port of the server from the system property set by the listener
+<3> Send a request to the server
+<4> Check the status code of the response
+
+[[launcher-api-launcher-interceptors-custom]]
+==== Registering a LauncherInterceptor
+
+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
+`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.
+
+[source,java]
+----
+include::{testDir}/example/CustomLauncherInterceptor.java[tags=user_guide]
+----
+
+[[launcher-api-launcher-discovery-listeners-custom]]
+==== Registering a LauncherDiscoveryListener
+
+In addition to specifying discovery listeners as part of a `{LauncherDiscoveryRequest}` or
+registering them programmatically via the `{Launcher}` API, custom
+`LauncherDiscoveryListener` implementations can be discovered at runtime via Java's
+`{ServiceLoader}` mechanism and automatically registered with the `Launcher` created via
+the `{LauncherFactory}`.
+
+For example, an `example.CustomLauncherDiscoveryListener` class implementing
+`LauncherDiscoveryListener` and declared within the
+`/META-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener` file is loaded
+and registered automatically.
+
+[[launcher-api-listeners-custom]]
+==== Registering a TestExecutionListener
+
+In addition to the public `{Launcher}` API method for registering test execution listeners
+programmatically, custom `{TestExecutionListener}` implementations will be discovered at
+runtime via Java's `{ServiceLoader}` mechanism and automatically registered with the
+`Launcher` created via the `{LauncherFactory}`.
+
+For example, an `example.CustomTestExecutionListener` class implementing
+`TestExecutionListener` and declared within the
+`/META-INF/services/org.junit.platform.launcher.TestExecutionListener` file is loaded and
+registered automatically.
+
+[[launcher-api-listeners-config]]
+==== Configuring a TestExecutionListener
+
+When a `{TestExecutionListener}` is registered programmatically via the `{Launcher}` API,
+the listener may provide programmatic ways for it to be configured -- for example, via its
+constructor, setter methods, etc. However, when a `TestExecutionListener` is registered
+automatically via Java's `ServiceLoader` mechanism (see
+<>), there is no way for the user to directly configure the
+listener. In such cases, the author of a `TestExecutionListener` may choose to make the
+listener configurable via <>. The
+listener can then access the configuration parameters via the `TestPlan` supplied to the
+`testPlanExecutionStarted(TestPlan)` and `testPlanExecutionFinished(TestPlan)` callback
+methods. See the `{UniqueIdTrackingListener}` for an example.
+
+[[launcher-api-listeners-custom-deactivation]]
+==== Deactivating a TestExecutionListener
+
+Sometimes it can be useful to run a test suite _without_ certain execution listeners being
+active. For example, you might have custom a `{TestExecutionListener}` that sends the test
+results to an external system for reporting purposes, and while debugging you might not
+want these _debug_ results to be reported. To do this, provide a pattern for the
+`junit.platform.execution.listeners.deactivate` _configuration parameter_ to specify which
+execution listeners should be deactivated (i.e. not registered) for the current test run.
+
+[NOTE]
+====
+Only listeners registered via the `{ServiceLoader}` mechanism within the
+`/META-INF/services/org.junit.platform.launcher.TestExecutionListener` file can be
+deactivated. In other words, any `TestExecutionListener` registered explicitly via the
+`{LauncherDiscoveryRequest}` cannot be deactivated via the
+`junit.platform.execution.listeners.deactivate` _configuration parameter_.
+
+In addition, since execution listeners are registered before the test run starts, the
+`junit.platform.execution.listeners.deactivate` _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}`.
+====
+
+[[launcher-api-listeners-custom-deactivation-pattern]]
+===== Pattern Matching Syntax
+
+Refer to <> for details.
+
+[[launcher-api-launcher-config]]
+==== Configuring the Launcher
+
+If you require fine-grained control over automatic detection and registration of test
+engines and listeners, you may create an instance of `{LauncherConfig}` and supply that to
+the `{LauncherFactory}`. Typically, an instance of `LauncherConfig` is created via the
+built-in fluent _builder_ API, as demonstrated in the following example.
+
+[source,java,indent=0]
+----
+include::{testDir}/example/UsingTheLauncherDemo.java[tags=launcherConfig]
+----
+
+[[launcher-api-dry-run-mode]]
+==== Dry-Run Mode
+
+When running tests via the `{Launcher}` API, you can enable _dry-run mode_ by setting the
+`junit.platform.execution.dryRun.enabled` <> to `true`. In this mode, the `{Launcher}` will not actually
+execute any tests but will notify registered `{TestExecutionListener}` instances as if all
+tests had been skipped and their containers had been successful. This can be useful to
+test changes in the configuration of a build or to verify a listener is called as expected
+without having to wait for all tests to be executed.
diff --git a/documentation/src/docs/asciidoc/user-guide/testkit.adoc b/documentation/src/docs/asciidoc/user-guide/advanced-topics/testkit.adoc
similarity index 97%
rename from documentation/src/docs/asciidoc/user-guide/testkit.adoc
rename to documentation/src/docs/asciidoc/user-guide/advanced-topics/testkit.adoc
index 32658381eaff..665c4daea788 100644
--- a/documentation/src/docs/asciidoc/user-guide/testkit.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics/testkit.adoc
@@ -5,10 +5,6 @@ The `junit-platform-testkit` artifact provides support for executing a test plan
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 <>).
-WARNING: Although the Test Kit is currently an <> feature, the JUnit Team invites you to try it out and provide feedback to
-help improve the Test Kit APIs and eventually <> this feature.
-
[[testkit-engine]]
==== Engine Test Kit
diff --git a/documentation/src/docs/asciidoc/user-guide/appendix.adoc b/documentation/src/docs/asciidoc/user-guide/appendix.adoc
index 2551c36ba692..d92194fd2b79 100644
--- a/documentation/src/docs/asciidoc/user-guide/appendix.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/appendix.adoc
@@ -4,7 +4,8 @@
[[reproducible-builds]]
=== Reproducible Builds
-Starting with version 5.7, JUnit 5 aims for its non-javadoc JARs to be https://reproducible-builds.org/[reproducible]
+Starting with version 5.7, JUnit 5 aims for its non-javadoc JARs to be
+https://reproducible-builds.org/[reproducible].
Under identical build conditions, such as Java version, repeated builds should provide the
same output byte-for-byte.
@@ -39,19 +40,32 @@ artifacts are deployed to Sonatype's {snapshot-repo}[snapshots repository] under
directory. See <> for details.
`junit-platform-engine`::
Public API for test engines. See <> for details.
+ `junit-platform-jfr`::
+ Provides a `LauncherDiscoveryListener` and `TestExecutionListener` for Java Flight
+ Recorder events on the JUnit Platform. See <>
+ for details.
`junit-platform-launcher`::
Public API for configuring and launching test plans -- typically used by IDEs and
build tools. See <> for details.
`junit-platform-reporting`::
`TestExecutionListener` implementations that generate test reports -- typically used
- by IDEs and build tools. See <> for details.
+ by IDEs and build tools. See <> for details.
`junit-platform-runner`::
Runner for executing tests and test suites on the JUnit Platform in a JUnit 4
environment. See <> for details.
+ `junit-platform-suite`::
+ JUnit Platform Suite artifact that transitively pulls in dependencies on
+ `junit-platform-suite-api` and `junit-platform-suite-engine` for simplified dependency
+ management in build tools such as Gradle and Maven.
`junit-platform-suite-api`::
Annotations for configuring test suites on the JUnit Platform. Supported by the
- <> and possibly by
- third-party `TestEngine` implementations.
+ <> and the
+ <>.
+ `junit-platform-suite-commons`::
+ Common support utilities for executing test suites on the JUnit Platform.
+ `junit-platform-suite-engine`::
+ Engine that executes test suites on the JUnit Platform; only required at runtime. See
+ <> for details.
`junit-platform-testkit`::
Provides support for executing a test plan for a given `TestEngine` and then
accessing the results via a fluent API to verify the expected results.
@@ -135,20 +149,27 @@ package org.junit.jupiter {
package org.junit.vintage {
[junit-vintage-engine] as vintage_engine
- [junit:junit] as junit4
}
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]
}
@@ -170,33 +191,42 @@ jupiter ..> jupiter_api
jupiter ..> jupiter_params
jupiter ..> jupiter_engine
-jupiter_api ..> opentest4j
-jupiter_api ..> commons
+jupiter_api ....> opentest4j
+jupiter_api ...> commons
-jupiter_engine ..> engine
+jupiter_engine ...> engine
jupiter_engine ..> jupiter_api
jupiter_params ..> jupiter_api
jupiter_migration_support ..> jupiter_api
-jupiter_migration_support ..> junit4
+jupiter_migration_support ...> junit4
console ..> launcher
console ..> reporting
launcher ..> engine
-engine ..> opentest4j
+jfr ..> launcher
+
+engine ....> opentest4j
engine ..> commons
reporting ..> launcher
-runner ..> launcher
-runner ..> suite_api
-runner ..> junit4
+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 ....> opentest4j
testkit ..> launcher
-vintage_engine ..> engine
+vintage_engine ...> engine
vintage_engine ..> junit4
----
diff --git a/documentation/src/docs/asciidoc/user-guide/extensions.adoc b/documentation/src/docs/asciidoc/user-guide/extensions.adoc
index 3ad1310f8a26..09ca2ec5e88b 100644
--- a/documentation/src/docs/asciidoc/user-guide/extensions.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/extensions.adoc
@@ -21,27 +21,32 @@ Java's <> mechanism.
Developers can register one or more extensions _declaratively_ by annotating a test
interface, test class, test method, or custom _<>_ with `@ExtendWith(...)` and supplying class references for the extensions
-to register.
+annotation>>_ with `@ExtendWith(...)` and supplying class references for the extensions to
+register. As of JUnit Jupiter 5.8, `@ExtendWith` may also be declared on fields or on
+parameters in test class constructors, in test methods, and in `@BeforeAll`, `@AfterAll`,
+`@BeforeEach`, and `@AfterEach` lifecycle methods.
-For example, to register a custom `RandomParametersExtension` for a particular test
-method, you would annotate the test method as follows.
+For example, to register a `WebServerExtension` for a particular test method, you would
+annotate the test method as follows. We assume the `WebServerExtension` starts a local web
+server and injects the server's URL into parameters annotated with `@WebServerUrl`.
[source,java,indent=0]
----
-@ExtendWith(RandomParametersExtension.class)
@Test
-void test(@Random int i) {
- // ...
+@ExtendWith(WebServerExtension.class)
+void getProductList(@WebServerUrl String serverUrl) {
+ WebClient webClient = new WebClient();
+ // Use WebClient to connect to web server using serverUrl and verify response
+ assertEquals(200, webClient.get(serverUrl + "/products").getResponseStatus());
}
----
-To register a custom `RandomParametersExtension` for all tests in a particular class and
-its subclasses, you would annotate the test class as follows.
+To register the `WebServerExtension` for all tests in a particular class and its
+subclasses, you would annotate the test class as follows.
[source,java,indent=0]
----
-@ExtendWith(RandomParametersExtension.class)
+@ExtendWith(WebServerExtension.class)
class MyTests {
// ...
}
@@ -71,15 +76,16 @@ class MySecondTests {
[TIP]
.Extension Registration Order
====
-Extensions registered declaratively via `@ExtendWith` will be executed in the order in
-which they are declared in the source code. For example, the execution of tests in both
-`MyFirstTests` and `MySecondTests` will be extended by the `DatabaseExtension` and
-`WebServerExtension`, **in exactly that order**.
+Extensions registered declaratively via `@ExtendWith` at the class level, method level, or
+parameter level will be executed in the order in which they are declared in the source
+code. For example, the execution of tests in both `MyFirstTests` and `MySecondTests` will
+be extended by the `DatabaseExtension` and `WebServerExtension`, **in exactly that order**.
====
If you wish to combine multiple extensions in a reusable way, you can define a custom
_<>_ and use `@ExtendWith` as a
-_meta-annotation_:
+_meta-annotation_ as in the following code listing. Then `@DatabaseAndWebServerExtension`
+can be used in place of `@ExtendWith({ DatabaseExtension.class, WebServerExtension.class })`.
[source,java,indent=0]
----
@@ -90,6 +96,59 @@ 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
+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
+transparently as in the following `RandomNumberDemo` example.
+
+[source,java,indent=0]
+----
+include::{testDir}/example/extensions/Random.java[tags=user_guide]
+----
+
+[source,java,indent=0]
+----
+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
+important to note which extension APIs are implemented and for what reasons.
+
+Specifically, `RandomNumberExtension` implements the following extension APIs:
+
+- `BeforeAllCallback`: to support static field injection
+- `TestInstancePostProcessor`: to support non-static field injection
+- `ParameterResolver`: to support constructor and method injection
+
+[source,java,indent=0]
+----
+include::{testDir}/example/extensions/RandomNumberExtension.java[tags=user_guide]
+----
+
+[TIP]
+.Extension Registration Order for `@ExtendWith` on Fields
+====
+Extensions registered declaratively via `@ExtendWith` on fields will be ordered relative
+to `@RegisterExtension` fields and other `@ExtendWith` fields using an algorithm that is
+deterministic but intentionally nonobvious. However, `@ExtendWith` fields can be ordered
+using the `@Order` annotation. See the <> tip for `@RegisterExtension` fields for details.
+====
+
+NOTE: `@ExtendWith` fields may be either `static` or non-static. The documentation on
+<> and
+<> for
+`@RegisterExtension` fields also applies to `@ExtendWith` fields.
+
[[extensions-registration-programmatic]]
==== Programmatic Extension Registration
@@ -106,27 +165,27 @@ extension's constructor, a static factory method, or a builder API.
[TIP]
.Extension Registration Order
====
-By default, extensions registered programmatically via `@RegisterExtension` will be
-ordered using an algorithm that is deterministic but intentionally nonobvious. This
-ensures that subsequent runs of a test suite execute extensions in the same order, thereby
-allowing for repeatable builds. However, there are times when extensions need to be
-registered in an explicit order. To achieve that, annotate `@RegisterExtension` fields
-with `{Order}`.
-
-Any `@RegisterExtension` field not annotated with `@Order` will be ordered using the
-_default_ order which has a value of `Integer.MAX_VALUE / 2`. This allows `@Order`
-annotated extension fields to be explicitly ordered before or after non-annotated
-extension fields. Extensions with an explicit order value less than the default order
-value will be registered before non-annotated extensions. Similarly, extensions with an
-explicit order value greater than the default order value will be registered after
-non-annotated extensions. For example, assigning an extension an explicit order value that
-is greater than the default order value allows _before_ callback extensions to be
-registered last and _after_ callback extensions to be registered first, relative to other
-programmatically registered extensions.
+By default, extensions registered programmatically via `@RegisterExtension` or
+declaratively via `@ExtendWith` on fields will be ordered using an algorithm that is
+deterministic but intentionally nonobvious. This ensures that subsequent runs of a test
+suite execute extensions in the same order, thereby allowing for repeatable builds.
+However, there are times when extensions need to be registered in an explicit order. To
+achieve that, annotate `@RegisterExtension` fields or `@ExtendWith` fields with `{Order}`.
+
+Any `@RegisterExtension` field or `@ExtendWith` field not annotated with `@Order` will be
+ordered using the _default_ order which has a value of `Integer.MAX_VALUE / 2`. This
+allows `@Order` annotated extension fields to be explicitly ordered before or after
+non-annotated extension fields. Extensions with an explicit order value less than the
+default order value will be registered before non-annotated extensions. Similarly,
+extensions with an explicit order value greater than the default order value will be
+registered after non-annotated extensions. For example, assigning an extension an explicit
+order value that is greater than the default order value allows _before_ callback
+extensions to be registered last and _after_ callback extensions to be registered first,
+relative to other programmatically registered extensions.
====
-NOTE: `@RegisterExtension` fields must not be `private` or `null` (at evaluation time) but
-may be either `static` or non-static.
+NOTE: `@RegisterExtension` fields must not be `null` (at evaluation time) but may be
+either `static` or non-static.
[[extensions-registration-programmatic-static-fields]]
===== Static Fields
@@ -149,19 +208,18 @@ lifecycle methods annotated with `@BeforeAll` or `@AfterAll` as well as `@Before
`server` field if necessary.
[source,java,indent=0]
-.An extension registered via a static field
+.Registering an extension via a static field in Java
----
include::{testDir}/example/registration/WebServerDemo.java[tags=user_guide]
----
[[extensions-registration-programmatic-static-fields-kotlin]]
-===== Static Fields in Kotlin
+====== Static Fields in Kotlin
The Kotlin programming language does not have the concept of a `static` field. However,
-the compiler can be instructed to generate static fields using annotations. Since, as
-stated earlier, `@RegisterExtension` fields must not be `private` nor `null`, one
-**cannot** use the `@JvmStatic` annotation in Kotlin as it generates `private` fields.
-Rather, the `@JvmField` annotation must be used.
+the compiler can be instructed to generate a `private static` field using the `@JvmStatic`
+annotation in Kotlin. If you want the Kotlin compiler to generate a `public static` field,
+you can use the `@JvmField` annotation instead.
The following example is a version of the `WebServerDemo` from the previous section that
has been ported to Kotlin.
@@ -206,8 +264,8 @@ include::{testDir}/example/registration/DocumentationDemo.java[tags=user_guide]
In addition to <>
and <> support
using annotations, JUnit Jupiter also supports _global extension registration_ via Java's
-`java.util.ServiceLoader` mechanism, allowing third-party extensions to be auto-detected
-and automatically registered based on what is available in the classpath.
+`{ServiceLoader}` mechanism, allowing third-party extensions to be auto-detected and
+automatically registered based on what is available in the classpath.
Specifically, a custom extension can be registered by supplying its fully qualified class
name in a file named `org.junit.jupiter.api.extension.Extension` within the
@@ -227,7 +285,7 @@ following system property.
`-Djunit.jupiter.extensions.autodetection.enabled=true`
-When auto-detection is enabled, extensions discovered via the `ServiceLoader` mechanism
+When auto-detection is enabled, extensions discovered via the `{ServiceLoader}` mechanism
will be added to the extension registry after JUnit Jupiter's global extensions (e.g.,
support for `TestInfo`, `TestReporter`, etc.).
@@ -282,6 +340,17 @@ following system property.
Refer to <> for details.
+[[extensions-test-instance-pre-construct-callback]]
+=== Test Instance Pre-construct Callback
+
+`{TestInstancePreConstructCallback}` defines the API for `Extensions` that wish to be invoked
+_prior_ to test instances being constructed (by a constructor call or via
+`{TestInstanceFactory}`).
+
+This extension provides a symmetric call to `{TestInstancePreDestroyCallback}` and is useful
+in combination with other extensions to prepare constructor parameters or keeping track of test
+instances and their lifecycle.
+
[[extensions-test-instance-factories]]
=== Test Instance Factories
@@ -336,11 +405,11 @@ test instance, invoking custom de-initialization methods on the test instance, e
runtime.
If a _test class_ constructor, _test method_, or _lifecycle method_ (see
-<>) declares a parameter, the parameter must be
-_resolved_ at runtime by a `ParameterResolver`. A `ParameterResolver` can either be
-built-in (see `{TestInfoParameterResolver}`) or <>. Generally speaking, parameters may be resolved by _name_, _type_,
-_annotation_, or any combination thereof.
+<>) declares a parameter, the parameter must be _resolved_ at
+runtime by a `ParameterResolver`. A `ParameterResolver` can either be built-in (see
+`{TestInfoParameterResolver}`) or <>.
+Generally speaking, parameters may be resolved by _name_, _type_, _annotation_, or any
+combination thereof.
If you wish to implement a custom `{ParameterResolver}` that resolves parameters based
solely on the type of the parameter, you may find it convenient to extend the
@@ -366,6 +435,13 @@ those provided in `java.lang.reflect.Parameter` in order to avoid this bug in th
* `List findRepeatableAnnotations(Class annotationType)`
====
+[NOTE]
+====
+Other extensions can also leverage registered `ParameterResolvers` for method and
+constructor invocations, using the `{ExecutableInvoker}` available via the
+`getExecutableInvoker()` method in the `ExtensionContext`.
+====
+
[[extensions-test-result-processing]]
=== Test Result Processing
@@ -379,20 +455,44 @@ information for the following events.
* `testFailed`: invoked after a _test method_ has failed
NOTE: In contrast to the definition of "test method" presented in
-<>, in this context _test method_ refers to any
-`@Test` method or `@TestTemplate` method (for example, a `@RepeatedTest` or
-`@ParameterizedTest`).
+<>, in this context _test method_ refers to any `@Test` method
+or `@TestTemplate` method (for example, a `@RepeatedTest` or `@ParameterizedTest`).
+
+Extensions implementing this interface can be registered at the class level, instance
+level, or method level. When registered at the class level, a `TestWatcher` will be
+invoked for any contained _test method_ including those in `@Nested` classes. When
+registered at the method level, a `TestWatcher` will only be invoked for the _test method_
+for which it was registered.
+
+[WARNING]
+====
+If a `TestWatcher` is registered via a non-static (instance) field – for example, using
+`@RegisterExtension` – and the test class is configured with
+`@TestInstance(Lifecycle.PER_METHOD)` semantics (which is the default lifecycle mode), the
+`TestWatcher` will **not** be invoked with events for `@TestTemplate` methods (for
+example, `@RepeatedTest` or `@ParameterizedTest`).
+
+To ensure that a `TestWatcher` is invoked for all _test methods_ in a given class, it is
+therefore recommended that the `TestWatcher` be registered at the class level with
+`@ExtendWith` or via a `static` field with `@RegisterExtension` or `@ExtendWith`.
+====
+
+If there is a failure at the class level — for example, an exception thrown by a
+`@BeforeAll` method — no test results will be reported. Similarly, if the test class is
+disabled via an `ExecutionCondition` — for example, `@Disabled` — no test results will be
+reported.
-Extensions implementing this interface can be registered at the method level or at the
-class level. In the latter case they will be invoked for any contained _test method_
-including those in `@Nested` classes.
+In contrast to other Extension APIs, a `TestWatcher` is not permitted to adversely
+influence the execution of tests. Consequently, any exception thrown by a method in the
+`TestWatcher` API will be logged at `WARNING` level and will not be allowed to propagate
+or fail test execution.
[WARNING]
====
Any instances of `ExtensionContext.Store.CloseableResource` stored in the `Store` of the
-provided `{ExtensionContext}` will be closed _before_ methods in this API are invoked (see
-<>). You can use the parent context's `Store` to work with such
-resources.
+provided `{ExtensionContext}` will be closed _before_ methods in the `TestWatcher` API are
+invoked (see <>). You can use the parent context's `Store` to
+work with such resources.
====
[[extensions-lifecycle-callbacks]]
@@ -740,29 +840,33 @@ callbacks implemented by `Extension2`. `Extension1` is therefore said to _wrap_
`Extension2`.
JUnit Jupiter also guarantees _wrapping_ behavior within class and interface hierarchies
-for user-supplied _lifecycle methods_ (see <>).
+for user-supplied _lifecycle methods_ (see <>).
-* `@BeforeAll` methods are inherited from superclasses as long as they are not _hidden_ or
- _overridden_. 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 _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.
** 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_ or
- _overridden_. Furthermore, `@AfterAll` methods from superclasses will be executed
- **after** `@AfterAll` methods in subclasses.
+* `@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.
** 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.
* `@BeforeEach` methods are inherited from superclasses as long as they are not
- _overridden_. Furthermore, `@BeforeEach` methods from superclasses will be executed
- **before** `@BeforeEach` methods in subclasses.
+ _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.
** 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_. Furthermore, `@AfterEach` methods from superclasses will be executed
- **after** `@AfterEach` methods in subclasses.
+ _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.
** 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.
@@ -884,7 +988,6 @@ image::extensions_BrokenLifecycleMethodConfigDemo.png[caption='',title='BrokenLi
[TIP]
====
Due to the aforementioned behavior, the JUnit Team recommends that developers declare at
-most one of each type of _lifecycle method_ (see <>)
-per test class or test interface unless there are no dependencies between such lifecycle
-methods.
+most one of each type of _lifecycle method_ (see <>) per test
+class or test interface unless there are no dependencies between such lifecycle methods.
====
diff --git a/documentation/src/docs/asciidoc/user-guide/images/writing-tests_nested_test_ide.png b/documentation/src/docs/asciidoc/user-guide/images/writing-tests_nested_test_ide.png
new file mode 100644
index 000000000000..8a5dd0a1d158
Binary files /dev/null and b/documentation/src/docs/asciidoc/user-guide/images/writing-tests_nested_test_ide.png differ
diff --git a/documentation/src/docs/asciidoc/user-guide/launcher-api.adoc b/documentation/src/docs/asciidoc/user-guide/launcher-api.adoc
deleted file mode 100644
index a1964e34e59c..000000000000
--- a/documentation/src/docs/asciidoc/user-guide/launcher-api.adoc
+++ /dev/null
@@ -1,191 +0,0 @@
-[[launcher-api]]
-=== JUnit Platform Launcher API
-
-One of the prominent goals of JUnit 5 is to make the interface between JUnit and its
-programmatic clients – build tools and IDEs – more powerful and stable. The purpose is to
-decouple the internals of discovering and executing tests from all the filtering and
-configuration that's necessary from the outside.
-
-JUnit 5 introduces the concept of a `Launcher` that can be used to discover, filter, and
-execute tests. Moreover, third party test libraries – like Spock, Cucumber, and FitNesse
-– can plug into the JUnit Platform's launching infrastructure by providing a custom
-`{TestEngine}`.
-
-The launcher API is in the `{junit-platform-launcher}` module.
-
-An example consumer of the launcher API is the `{ConsoleLauncher}` in the
-`{junit-platform-console}` project.
-
-[[launcher-api-discovery]]
-==== Discovering Tests
-
-Introducing _test discovery_ as a dedicated feature of the platform itself will
-(hopefully) free IDEs and build tools from most of the difficulties they had to go
-through to identify test classes and test methods in the past.
-
-Usage Example:
-
-[source,java,indent=0]
-----
-include::{testDir}/example/UsingTheLauncherDemo.java[tags=imports]
-----
-
-[source,java,indent=0]
-----
-include::{testDir}/example/UsingTheLauncherDemo.java[tags=discovery]
-----
-
-There's currently the possibility to select classes, methods, and all classes in a
-package or even search for all tests in the classpath. Discovery takes place across all
-participating test engines.
-
-The resulting `TestPlan` is a hierarchical (and read-only) description of all engines,
-classes, and test methods that fit the `LauncherDiscoveryRequest`. The client can
-traverse the tree, retrieve details about a node, and get a link to the original source
-(like class, method, or file position). Every node in the test plan has a _unique ID_
-that can be used to invoke a particular test or group of tests.
-
-Clients can register one or more `{LauncherDiscoveryListener}` implementations to get
-insights into events that occur during test discovery via the
-`{LauncherDiscoveryRequestBuilder}`. The builder registers a default listener that can be
-changed via the `junit.platform.discovery.listener.default` configuration parameter. If
-the parameter is not set, test discovery will be aborted after the first failure is
-encountered.
-
-[[launcher-api-execution]]
-==== Executing Tests
-
-To execute tests, clients can use the same `LauncherDiscoveryRequest` as in the discovery
-phase or create a new request. Test progress and reporting can be achieved by registering
-one or more `{TestExecutionListener}` implementations with the `Launcher` as in the
-following example.
-
-[source,java,indent=0]
-----
-include::{testDir}/example/UsingTheLauncherDemo.java[tags=execution]
-----
-
-There is no return value for the `execute()` method, but you can easily use a listener to
-aggregate the final results in an object of your own. For examples see the
-`{SummaryGeneratingListener}` and `{LegacyXmlReportGeneratingListener}`.
-
-[[launcher-api-engines-custom]]
-==== Plugging in your own Test Engine
-
-JUnit currently provides two `{TestEngine}` implementations.
-
-* `{junit-jupiter-engine}`: The core of JUnit Jupiter.
-* `{junit-vintage-engine}`: A thin layer on top of JUnit 4 to allow running _vintage_
- tests with the launcher infrastructure.
-
-Third parties may also contribute their own `TestEngine` by implementing the interfaces
-in the {junit-platform-engine} module and _registering_ their engine. By default, engine
-registration is supported via Java's `java.util.ServiceLoader` mechanism. For example,
-the `junit-jupiter-engine` module registers its
-`org.junit.jupiter.engine.JupiterTestEngine` in a file named
-`org.junit.platform.engine.TestEngine` within the `/META-INF/services` in the
-`junit-jupiter-engine` JAR.
-
-NOTE: `{HierarchicalTestEngine}` is a convenient abstract base implementation (used by
-the `{junit-jupiter-engine}`) that only requires implementors to provide the logic for
-test discovery. It implements execution of `TestDescriptors` that implement the `Node`
-interface, including support for parallel execution.
-
-[[launcher-api-engines-custom-ids]]
-[WARNING]
-.The `junit-` prefix is reserved for TestEngines from the JUnit Team
-====
-The JUnit Platform `Launcher` enforces that only `TestEngine` implementations published
-by the JUnit Team may use the `junit-` prefix for their `TestEngine` IDs.
-
-* If any third-party `TestEngine` claims to be `junit-jupiter` or `junit-vintage`, an
- exception will be thrown, immediately halting execution of the JUnit Platform.
-* If any third-party `TestEngine` uses the `junit-` prefix for its ID, a warning message
- will be logged. Later releases of the JUnit Platform will throw an exception for such
- violations.
-====
-
-[[launcher-api-post-discovery-filters-custom]]
-==== Plugging in your own Post-Discovery Filters
-
-In addition to specifying post-discovery filters as part of a `{LauncherDiscoveryRequest}`
-passed to the `{Launcher}` API, by default custom `{PostDiscoveryFilter}` implementations
-will be discovered at runtime via Java's `java.util.ServiceLoader` mechanism and
-automatically applied by the `Launcher` in addition to those that are part of the request.
-For example, an `example.CustomTagFilter` class implementing `{PostDiscoveryFilter}` and
-declared within the `/META-INF/services/org.junit.platform.launcher.PostDiscoveryFilter`
-file is loaded and applied automatically.
-
-[[launcher-api-listeners-custom]]
-==== Plugging in your own Test Execution Listener
-
-In addition to the public `{Launcher}` API method for registering test execution
-listeners programmatically, by default custom `{TestExecutionListener}` implementations
-will be discovered at runtime via Java's `java.util.ServiceLoader` mechanism and
-automatically registered with the `Launcher` created via the `LauncherFactory`. For
-example, an `example.TestInfoPrinter` class implementing `{TestExecutionListener}` and
-declared within the
-`/META-INF/services/org.junit.platform.launcher.TestExecutionListener` file is loaded and
-registered automatically.
-
-[[launcher-api-listeners-custom-deactivation]]
-==== Deactivating Test Execution Listeners
-
-Sometimes it can be useful to run a test suite _without_ certain execution listeners being
-active. For example, you might have custom a `TestExecutionListener` that sends the test
-results to an external system for reporting purposes, and while debugging you might not
-want these _debug_ results to be reported. To do this, provide a pattern for the
-`junit.platform.execution.listeners.deactivate` _configuration parameter_ to specify which
-execution listeners should be deactivated (i.e. not registered) for the current test run.
-
-[NOTE]
-====
-Only listeners registered via the `ServiceLoader` mechanism within the
-`/META-INF/services/org.junit.platform.launcher.TestExecutionListener` file can be
-deactivated. In other words, any `TestExecutionListener` registered explicitly via the
-`LauncherDiscoveryRequest` cannot be deactivated via the
-`junit.platform.execution.listeners.deactivate` _configuration parameter_.
-
-In addition, since execution listeners are registered before the test run starts, the
-`junit.platform.execution.listeners.deactivate` _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`.
-====
-
-[[launcher-api-listeners-custom-deactivation-pattern]]
-===== Pattern Matching Syntax
-
-Refer to <> for details.
-
-[[launcher-api-listeners-reporting]]
-==== JUnit Platform Reporting
-
-The `junit-platform-reporting` artifact contains `{TestExecutionListener}`
-implementations that generate test reports. These listeners are typically used by IDEs
-and build tools. The package `org.junit.platform.reporting.legacy.xml` currently contains
-the following implementation.
-
-* `{LegacyXmlReportGeneratingListener}` generates a separate XML report for each root in
- the `{TestPlan}`. Note that the generated XML format is compatible with the de facto
- standard for JUnit 4 based test reports that was made popular by the Ant build system.
- The `LegacyXmlReportGeneratingListener` is used by the
- <> as well.
-
-NOTE: The `{junit-platform-launcher}` module also contains `{TestExecutionListener}`
-implementations that can be used for reporting purposes. See `{LoggingListener}` and
-`{SummaryGeneratingListener}` for details.
-
-[[launcher-api-launcher-config]]
-==== Configuring the Launcher
-
-If you require fine-grained control over automatic detection and registration of test
-engines and test execution listeners, you may create an instance of `LauncherConfig` and
-supply that to the `LauncherFactory.create(LauncherConfig)` method. Typically an instance
-of `LauncherConfig` is created via the built-in fluent _builder_ API, as demonstrated in
-the following example.
-
-[source,java,indent=0]
-----
-include::{testDir}/example/UsingTheLauncherDemo.java[tags=launcherConfig]
-----
diff --git a/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc b/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc
index b3fb1ded6ed8..98e0b056af93 100644
--- a/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc
@@ -1,26 +1,26 @@
[[migrating-from-junit4]]
== Migrating from JUnit 4
-Although the JUnit Jupiter programming model and extension model will not support JUnit 4
+Although the JUnit Jupiter programming model and extension model do not support JUnit 4
features such as `Rules` and `Runners` natively, it is not expected that source code
maintainers will need to update all of their existing tests, test extensions, and custom
build test infrastructure to migrate to JUnit Jupiter.
Instead, JUnit provides a gentle migration path via a _JUnit Vintage test engine_ which
-allows existing tests based on JUnit 3 and JUnit 4 to be executed using the JUnit
-Platform infrastructure. Since all classes and annotations specific to JUnit Jupiter
-reside under a new `org.junit.jupiter` base package, having both JUnit 4 and JUnit
-Jupiter in the classpath does not lead to any conflicts. It is therefore safe to maintain
-existing JUnit 4 tests alongside JUnit Jupiter tests. Furthermore, since the JUnit team
-will continue to provide maintenance and bug fix releases for the JUnit 4.x baseline,
-developers have plenty of time to migrate to JUnit Jupiter on their own schedule.
+allows existing tests based on JUnit 3 and JUnit 4 to be executed using the JUnit Platform
+infrastructure. Since all classes and annotations specific to JUnit Jupiter reside under
+the `org.junit.jupiter` base package, having both JUnit 4 and JUnit Jupiter in the
+classpath does not lead to any conflicts. It is therefore safe to maintain existing JUnit
+4 tests alongside JUnit Jupiter tests. Furthermore, since the JUnit team will continue to
+provide maintenance and bug fix releases for the JUnit 4.x baseline, developers have
+plenty of time to migrate to JUnit Jupiter on their own schedule.
[[migrating-from-junit4-running]]
=== Running JUnit 4 Tests on the JUnit Platform
-Just make sure that the `junit-vintage-engine` artifact is in your test runtime path. In
-that case JUnit 3 and JUnit 4 tests will automatically be picked up by the JUnit Platform
+Make sure that the `junit-vintage-engine` artifact is in your test runtime path. In that
+case JUnit 3 and JUnit 4 tests will automatically be picked up by the JUnit Platform
launcher.
See the example projects in the {junit5-samples-repo}[`junit5-samples`] repository to
@@ -30,11 +30,11 @@ find out how this is done with Gradle and Maven.
==== Categories Support
For test classes or methods that are annotated with `@Category`, the _JUnit Vintage test
-engine_ exposes the category's fully qualified class name as a tag of the corresponding
-test identifier. For example, if a test method is annotated with
-`@Category(Example.class)`, it will be tagged with `"com.acme.Example"`. Similar to the
-`Categories` runner in JUnit 4, this information can be used to filter the discovered
-tests before executing them (see <> for details).
+engine_ exposes the category's fully qualified class name as a <>
+for the corresponding test class or test method. For example, if a test method is
+annotated with `@Category(Example.class)`, it will be tagged with `"com.acme.Example"`.
+Similar to the `Categories` runner in JUnit 4, this information can be used to filter the
+discovered tests before executing them (see <> for details).
[[migrating-from-junit4-tips]]
@@ -61,8 +61,15 @@ tests to JUnit Jupiter.
* `@Category` no longer exists; use `@Tag` instead.
* `@RunWith` no longer exists; superseded by `@ExtendWith`.
* `@Rule` and `@ClassRule` no longer exist; superseded by `@ExtendWith` and
- `@RegisterExtension`
+ `@RegisterExtension`.
- See also <>.
+* `@Test(expected = ...)` and the `ExpectedException` rule no longer exist; use
+ `Assertions.assertThrows(...)` instead.
+ - See <> if you still need to use
+ `ExpectedException`.
+* Assertions and assumptions in JUnit Jupiter accept the failure message as their last
+ argument instead of the first one.
+ - See <> for details.
[[migrating-from-junit4-rule-support]]
@@ -95,12 +102,9 @@ all rule migration support extensions: `VerifierSupport`, `ExternalResourceSuppo
`@EnableJUnit4MigrationSupport` which registers migration support for rules _and_ JUnit
4's `@Ignore` annotation (see <>).
-However, if you intend to develop a new extension for JUnit 5 please use the new
+However, if you intend to develop a new extension for JUnit Jupiter please use the new
extension model of JUnit Jupiter instead of the rule-based model of JUnit 4.
-WARNING: JUnit 4 `Rule` support in JUnit Jupiter is currently an _experimental_ feature.
-Consult the table in <> for detail.
-
[[migrating-from-junit4-ignore-annotation-support]]
=== JUnit 4 @Ignore Support
@@ -122,5 +126,34 @@ automatically registers the `IgnoreCondition` along with
include::{testDir}/example/IgnoredTestsDemo.java[tags=user_guide]
----
-WARNING: JUnit 4 `@Ignore` support in JUnit Jupiter is currently an _experimental_
-feature. Consult the table in <> for detail.
+
+[[migrating-from-junit4-failure-message-arguments]]
+=== Failure Message Arguments
+
+The `Assumptions` and `Assertions` classes in JUnit Jupiter declare arguments in a
+different order than in JUnit 4. In JUnit 4 assertion and assumption methods accept the
+failure message as the first argument; whereas, in JUnit Jupiter assertion and assumption
+methods accept the failure message as the last argument.
+
+For instance, the method `assertEquals` in JUnit 4 is declared as `assertEquals(String
+message, Object expected, Object actual)`, but in JUnit Jupiter it is declared as
+`assertEquals(Object expected, Object actual, String message)`. The rationale for this is
+that a failure message is _optional_, and optional arguments should be declared after
+required arguments in a method signature.
+
+The methods affected by this change are the following:
+
+- Assertions
+ * `assertTrue`
+ * `assertFalse`
+ * `assertNull`
+ * `assertNotNull`
+ * `assertEquals`
+ * `assertNotEquals`
+ * `assertArrayEquals`
+ * `assertSame`
+ * `assertNotSame`
+ * `assertThrows`
+- Assumptions
+ * `assumeTrue`
+ * `assumeFalse`
diff --git a/documentation/src/docs/asciidoc/user-guide/overview.adoc b/documentation/src/docs/asciidoc/user-guide/overview.adoc
index afad42176c2f..d06a5eb53eda 100644
--- a/documentation/src/docs/asciidoc/user-guide/overview.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/overview.adoc
@@ -23,20 +23,20 @@ The **JUnit Platform** serves as a foundation for <> on the JVM. It also defines the `{TestEngine}` API for developing a testing
framework that runs on the platform. Furthermore, the platform provides a
<> to launch the platform from the
-command line and a <> for
-running any `TestEngine` on the platform in a JUnit 4 based environment. First-class
-support for the JUnit Platform also exists in popular IDEs (see
-<>, <>,
-<>, and <>) and build tools (see
-<>, <>, and
-<>).
-
-**JUnit Jupiter** is the combination of the new <> and
+command line and the <> for running a custom test suite using
+one or more test engines on the platform. First-class support for the JUnit Platform also
+exists in popular IDEs (see <>,
+<>, <>, and
+<>) and build tools (see <>,
+<>, and <>).
+
+**JUnit Jupiter** is the combination of the <> and
<> for writing tests and extensions in JUnit 5. The Jupiter
sub-project provides a `TestEngine` for running Jupiter based tests on the platform.
**JUnit Vintage** provides a `TestEngine` for running JUnit 3 and JUnit 4 based tests on
-the platform.
+the platform. It requires JUnit 4.12 or later to be present on the class path or module
+path.
[[overview-java-versions]]
=== Supported Java Versions
@@ -47,7 +47,7 @@ has been compiled with previous versions of the JDK.
[[overview-getting-help]]
=== Getting Help
-Ask JUnit 5 related questions on {StackOverflow} or chat with us on {Gitter}.
+Ask JUnit 5 related questions on {StackOverflow} or chat with the community on {Gitter}.
[[overview-getting-started]]
=== Getting Started
diff --git a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc
index 5adb80db0fc7..1900545858fc 100644
--- a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc
@@ -28,35 +28,48 @@ include the corresponding versions of the `junit-platform-launcher`,
[source,groovy]
[subs=attributes+]
----
-// Only needed to run tests in a version of IntelliJ IDEA that bundles older versions
-testRuntimeOnly("org.junit.platform:junit-platform-launcher:{platform-version}")
-testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:{jupiter-version}")
-testRuntimeOnly("org.junit.vintage:junit-vintage-engine:{vintage-version}")
+testImplementation(platform("org.junit:junit-bom:{bom-version}"))
+testRuntimeOnly("org.junit.platform:junit-platform-launcher") {
+ because("Only needed to run tests in a version of IntelliJ IDEA that bundles older versions")
+}
+testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
+testRuntimeOnly("org.junit.vintage:junit-vintage-engine")
----
.Additional Maven Dependencies
[source,xml]
[subs=attributes+]
----
-
-
- org.junit.platform
- junit-platform-launcher
- {platform-version}
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- {jupiter-version}
- test
-
-
- org.junit.vintage
- junit-vintage-engine
- {vintage-version}
- test
-
+
+
+
+
+ org.junit.platform
+ junit-platform-launcher
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ test
+
+
+
+
+
+ org.junit
+ junit-bom
+ {bom-version}
+ pom
+ import
+
+
+
----
[[running-tests-ide-eclipse]]
@@ -109,37 +122,31 @@ your IDE has built-in support for JUnit 4.
[[running-tests-build-gradle]]
==== Gradle
-[WARNING]
-.The JUnit Platform Gradle Plugin has been discontinued
-====
-The `junit-platform-gradle-plugin` developed by the JUnit team was deprecated in JUnit
-Platform 1.2 and discontinued in 1.3. Please switch to Gradle's standard `test` task.
-====
-
Starting with https://docs.gradle.org/4.6/release-notes.html[version 4.6], Gradle provides
https://docs.gradle.org/current/userguide/java_testing.html#using_junit5[native support]
-for executing tests on the JUnit Platform. To enable it, you just need to specify
+for executing tests on the JUnit Platform. To enable it, you need to specify
`useJUnitPlatform()` within a `test` task declaration in `build.gradle`:
-[source,java,indent=0]
+[source,groovy,indent=0]
[subs=attributes+]
----
test {
- useJUnitPlatform()
+ useJUnitPlatform()
}
----
-Filtering by tags or engines is also supported:
+Filtering by <>,
+<>, or engines is also supported:
-[source,java,indent=0]
+[source,groovy,indent=0]
[subs=attributes+]
----
test {
- useJUnitPlatform {
- includeTags 'fast', 'smoke & feature-a'
- // excludeTags 'slow', 'ci'
- includeEngines 'junit-jupiter'
- // excludeEngines 'junit-vintage'
+ useJUnitPlatform {
+ includeTags("fast", "smoke & feature-a")
+ // excludeTags("slow", "ci")
+ includeEngines("junit-jupiter")
+ // excludeEngines("junit-vintage")
}
}
----
@@ -148,6 +155,26 @@ Please refer to the
https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_test[official Gradle documentation]
for a comprehensive list of options.
+[[running-tests-build-gradle-bom]]
+===== Aligning dependency versions
+
+Unless you're using Spring Boot which defines its own way of managing dependencies, it is
+recommended to use the JUnit Platform BOM to align the versions of all JUnit 5 artifacts.
+
+[source,groovy,indent=0]
+[subs=attributes+]
+----
+dependencies {
+ testImplementation(platform("org.junit:junit-bom:{bom-version}"))
+}
+----
+
+Using the BOM allows you to omit the version when declaring dependencies on all artifacts
+with the `org.junit.platform`, `org.junit.jupiter`, and `org.junit.vintage` group IDs.
+
+TIP: See <> for details on how to override the version
+of JUnit used in your Spring Boot application.
+
[[running-tests-build-gradle-config-params]]
===== Configuration Parameters
@@ -157,15 +184,13 @@ discovery and execution. However, you can provide configuration parameters withi
build script via system properties (as shown below) or via the
`junit-platform.properties` file.
-[source,java,indent=0]
+[source,groovy,indent=0]
----
test {
// ...
- systemProperty 'junit.jupiter.conditions.deactivate', '*'
- systemProperties = [
- 'junit.jupiter.extensions.autodetection.enabled': 'true',
- 'junit.jupiter.testinstance.lifecycle.default': 'per_class'
- ]
+ systemProperty("junit.jupiter.conditions.deactivate", "*")
+ systemProperty("junit.jupiter.extensions.autodetection.enabled", true)
+ systemProperty("junit.jupiter.testinstance.lifecycle.default", "per_class")
// ...
}
----
@@ -176,15 +201,13 @@ test {
In order to run any tests at all, a `TestEngine` implementation must be on the classpath.
To configure support for JUnit Jupiter based tests, configure a `testImplementation` dependency
-on the JUnit Jupiter API and a `testRuntimeOnly` dependency on the JUnit Jupiter `TestEngine`
-implementation similar to the following.
+on the dependency-aggregating JUnit Jupiter artifact similar to the following.
-[source,java,indent=0]
+[source,groovy,indent=0]
[subs=attributes+]
----
dependencies {
- testImplementation("org.junit.jupiter:junit-jupiter-api:{jupiter-version}")
- testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:{jupiter-version}")
+ testImplementation("org.junit.jupiter:junit-jupiter:{jupiter-version}") // version can be omitted when using the BOM
}
----
@@ -192,12 +215,12 @@ The JUnit Platform can run JUnit 4 based tests as long as you configure a `testI
dependency on JUnit 4 and a `testRuntimeOnly` dependency on the JUnit Vintage `TestEngine`
implementation similar to the following.
-[source,java,indent=0]
+[source,groovy,indent=0]
[subs=attributes+]
----
dependencies {
testImplementation("junit:junit:{junit4-version}")
- testRuntimeOnly("org.junit.vintage:junit-vintage-engine:{vintage-version}")
+ testRuntimeOnly("org.junit.vintage:junit-vintage-engine:{vintage-version}") // version can be omitted when using the BOM
}
----
@@ -215,11 +238,11 @@ qualified class name_ of the `{LogManager}` implementation to use. The example b
demonstrates how to configure Log4j{nbsp}2.x (see {Log4j_JDK_Logging_Adapter} for
details).
-[source,java,indent=0]
+[source,groovy,indent=0]
[subs=attributes+]
----
test {
- systemProperty 'java.util.logging.manager', 'org.apache.logging.log4j.jul.LogManager'
+ systemProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager")
}
----
@@ -231,14 +254,6 @@ additional dependency to the runtime classpath.
[[running-tests-build-maven]]
==== Maven
-[WARNING]
-.The JUnit Platform Maven Surefire Provider has been discontinued
-====
-The `junit-platform-surefire-provider`, which was originally developed by the JUnit team,
-was deprecated in JUnit Platform 1.3 and discontinued in 1.4. Please use Maven Surefire's
-native support instead.
-====
-
Starting with https://issues.apache.org/jira/browse/SUREFIRE-1330[version 2.22.0], Maven
Surefire and Maven Failsafe provide
https://maven.apache.org/surefire/maven-surefire-plugin/examples/junit-platform.html[native support]
@@ -246,6 +261,58 @@ for executing tests on the JUnit Platform. The `pom.xml` file in the
`{junit5-jupiter-starter-maven}` project demonstrates how to use the Maven Surefire plugin
and can serve as a starting point for configuring your Maven build.
+[WARNING]
+.Use Maven Surefire/Failsafe 3.0.0-M4 or later to avoid interoperability issues
+====
+Maven Surefire/Failsafe 3.0.0-M4
+https://issues.apache.org/jira/browse/SUREFIRE-1585[introduced support] for aligning the
+version of the JUnit Platform Launcher it uses with the JUnit Platform version found on
+the test runtime classpath. Therefore, it is recommended to use version 3.0.0-M4 or later
+to avoid interoperability issues.
+
+Alternatively, you can add a test dependency on the matching version of the JUnit Platform
+Launcher to your Maven build as follows.
+
+[source,xml]
+[subs=attributes+]
+----
+
+ org.junit.platform
+ junit-platform-launcher
+ {platform-version}
+ test
+
+----
+====
+
+[[running-tests-build-maven-bom]]
+===== Aligning dependency versions
+
+Unless you're using Spring Boot which defines its own way of managing dependencies, it is
+recommended to use the JUnit Platform BOM to align the versions of all JUnit 5 artifacts.
+
+[source,xml,indent=0]
+[subs=attributes+]
+----
+
+
+
+ org.junit
+ junit-bom
+ {bom-version}
+ pom
+ import
+
+
+
+----
+
+Using the BOM allows you to omit the version when declaring dependencies on all artifacts
+with the `org.junit.platform`, `org.junit.jupiter`, and `org.junit.vintage` group IDs.
+
+TIP: See <> for details on how to override the version
+of JUnit used in your Spring Boot application.
+
[[running-tests-build-maven-engines-configure]]
===== Configuring Test Engines
@@ -259,6 +326,17 @@ following.
[source,xml,indent=0]
[subs=attributes+]
----
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ {jupiter-version}
+ test
+
+
+
@@ -272,23 +350,6 @@ following.
-
-
-
- org.junit.jupiter
- junit-jupiter-api
- {jupiter-version}
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- {jupiter-version}
- test
-
-
-
-
----
Maven Surefire and Maven Failsafe can run JUnit 4 based tests alongside Jupiter tests as
@@ -298,19 +359,6 @@ long as you configure `test` scoped dependencies on JUnit 4 and the JUnit Vintag
[source,xml,indent=0]
[subs=attributes+]
----
-
-
-
-
- maven-surefire-plugin
- {surefire-version}
-
-
- maven-failsafe-plugin
- {surefire-version}
-
-
-
@@ -323,12 +371,25 @@ long as you configure `test` scoped dependencies on JUnit 4 and the JUnit Vintag
org.junit.vintage
junit-vintage-engine
- {vintage-version}
+ {vintage-version}
test
+
+
+
+ maven-surefire-plugin
+ {surefire-version}
+
+
+ maven-failsafe-plugin
+ {surefire-version}
+
+
+
+
----
[[running-tests-build-maven-filter-test-class-names]]
@@ -337,10 +398,10 @@ long as you configure `test` scoped dependencies on JUnit 4 and the JUnit Vintag
The Maven Surefire Plugin will scan for test classes whose fully qualified names match
the following patterns.
-- `+**/Test*.java+`
-- `+**/*Test.java+`
-- `+**/*Tests.java+`
-- `+**/*TestCase.java+`
+- `+++**/Test*.java+++`
+- `+++**/*Test.java+++`
+- `+++**/*Tests.java+++`
+- `+++**/*TestCase.java+++`
Moreover, it will exclude all nested classes (including static member classes) by default.
@@ -376,8 +437,9 @@ documentation for Maven Surefire for details.
[[running-tests-build-maven-filter-tags]]
===== Filtering by Tags
-You can filter tests by tags or <> using
-the following configuration properties.
+You can filter tests by <> or
+<> using the following configuration
+properties.
- to include _tags_ or _tag expressions_, use `groups`.
- to exclude _tags_ or _tag expressions_, use `excludedGroups`.
@@ -394,7 +456,7 @@ the following configuration properties.
acceptance | !feature-a
integration, regression
-
+
@@ -436,18 +498,18 @@ below) or via the `junit-platform.properties` file.
[[running-tests-build-ant]]
==== Ant
-Starting with version `1.10.3` of link:https://ant.apache.org/[Ant], a new
-link:https://ant.apache.org/manual/Tasks/junitlauncher.html[`junitlauncher`] task has
-been introduced to provide native support for launching tests on the JUnit Platform. The
-`junitlauncher` task is solely responsible for launching the JUnit Platform and passing
-it the selected collection of tests. The JUnit Platform then delegates to registered test
-engines to discover and execute the tests.
+Starting with version `1.10.3`, link:https://ant.apache.org/[Ant] has a
+link:https://ant.apache.org/manual/Tasks/junitlauncher.html[`junitlauncher`] task that
+provides native support for launching tests on the JUnit Platform. The `junitlauncher`
+task is solely responsible for launching the JUnit Platform and passing it the selected
+collection of tests. The JUnit Platform then delegates to registered test engines to
+discover and execute the tests.
-The `junitlauncher` task attempts to align as close as possible with native Ant
+The `junitlauncher` task attempts to align as closely as possible with native Ant
constructs such as
link:https://ant.apache.org/manual/Types/resources.html#collection[resource collections]
-for allowing users to select the tests that they want executed by test engines. This
-gives the task a consistent and natural feel when compared to many other core Ant tasks.
+for allowing users to select the tests that they want executed by test engines. This gives
+the task a consistent and natural feel when compared to many other core Ant tasks.
Starting with version `1.10.6` of Ant, the `junitlauncher` task supports
link:https://ant.apache.org/manual/Tasks/junitlauncher.html#fork[forking the tests in a separate JVM].
@@ -510,6 +572,48 @@ For further details on usage and configuration options please refer to the offic
documentation for the
link:https://ant.apache.org/manual/Tasks/junitlauncher.html[`junitlauncher` task].
+[[running-tests-build-spring-boot]]
+==== Spring Boot
+
+link:https://spring.io/projects/spring-boot[Spring Boot] provides automatic support for
+managing the version of JUnit used in your project. In addition, the
+`spring-boot-starter-test` artifact automatically includes testing libraries such as JUnit
+Jupiter, AssertJ, Mockito, etc.
+
+If your build relies on dependency management support from Spring Boot, you should not
+import the <> in your build script since that
+will result in duplicate (and potentially conflicting) management of JUnit dependencies.
+
+If you need to override the version of a dependency used in your Spring Boot application,
+you have to override the exact name of the
+link:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#appendix.dependency-versions.properties[version property]
+defined in the BOM used by the Spring Boot plugin. For example, the name of the JUnit
+Jupiter version property in Spring Boot is `junit-jupiter.version`. The mechanism for
+changing a dependency version is documented for both
+link:https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#managing-dependencies.dependency-management-plugin.customizing[Gradle]
+and
+link:https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/#using.parent-pom[Maven].
+
+With Gradle you can override the JUnit Jupiter version by including the following in your
+`build.gradle` file.
+
+[source,groovy,indent=0]
+[subs=attributes+]
+----
+ ext['junit-jupiter.version'] = '{jupiter-version}'
+----
+
+With Maven you can override the JUnit Jupiter version by including the following in your
+`pom.xml` file.
+
+[source,xml,indent=0]
+[subs=attributes+]
+----
+
+ {jupiter-version}
+
+----
+
[[running-tests-console-launcher]]
=== Console Launcher
@@ -520,14 +624,17 @@ 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
https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone[junit-platform-console-standalone]
-directory. You can https://docs.oracle.com/javase/tutorial/deployment/jar/run.html[run]
-the standalone `ConsoleLauncher` as shown below.
+directory. It includes the following dependencies:
+
+include::{standaloneConsoleLauncherShadowedArtifactsFile}[]
-`java -jar junit-platform-console-standalone-{platform-version}.jar <<>>`
+You can https://docs.oracle.com/javase/tutorial/deployment/jar/run.html[run] the
+standalone `ConsoleLauncher` as shown below.
-Here's an example of its output:
+[source,console,subs=attributes+]
+----
+$ java -jar junit-platform-console-standalone-{platform-version}.jar execute
-....
├─ JUnit Vintage
│ └─ example.JUnit4Tests
│ └─ standardJUnit4Test ✔
@@ -553,21 +660,49 @@ Test run finished after 64 ms
[ 0 tests aborted ]
[ 5 tests successful ]
[ 0 tests failed ]
-....
+----
+
+You can also run the standalone `ConsoleLauncher` as shown below (for example, to include
+all jars in a directory):
+
+[source,console,subs=attributes+]
+----
+$ java -cp classes:testlib/* org.junit.platform.console.ConsoleLauncher
+----
.Exit Code
NOTE: The `{ConsoleLauncher}` exits with a status code of `1` if any containers or tests
failed. If no tests are discovered and the `--fail-if-no-tests` command-line option is
-supplied, the `ConsoleLauncher` exits with a status code of `2`. Otherwise the exit code
+supplied, the `ConsoleLauncher` exits with a status code of `2`. Otherwise, the exit code
is `0`.
[[running-tests-console-launcher-options]]
-==== Options
+==== Subcommands and Options
+
+The `{ConsoleLauncher}` provides the following subcommands:
----
include::{consoleLauncherOptionsFile}[]
----
+===== Discovering tests
+
+----
+include::{consoleLauncherDiscoverOptionsFile}[]
+----
+
+===== Executing tests
+
+----
+include::{consoleLauncherExecuteOptionsFile}[]
+----
+
+===== Listing test engines
+
+----
+include::{consoleLauncherEnginesOptionsFile}[]
+----
+
[[running-tests-console-launcher-argument-files]]
==== Argument Files (@-files)
@@ -596,10 +731,50 @@ You can pass a real parameter with an initial `@` character by escaping it with
additional `@` symbol. For example, `@@somearg` will become `@somearg` and will not be
subject to expansion.
+[[running-tests-console-launcher-color-customization]]
+==== Color customization
+
+The colors used in the output of the `{ConsoleLauncher}` can be customized.
+The option `--single-color` will apply a built-in monochrome style, while
+`--color-palette` will accept a properties file to override the
+https://en.wikipedia.org/wiki/ANSI_escape_code#Colors[ANSI SGR] color styling.
+The properties file below demonstrates the default style:
+
+[source,properties,indent=0]
+----
+SUCCESSFUL = 32
+ABORTED = 33
+FAILED = 31
+SKIPPED = 35
+CONTAINER = 35
+TEST = 34
+DYNAMIC = 35
+REPORTED = 37
+----
+
[[running-tests-junit-platform-runner]]
=== Using JUnit 4 to run the JUnit Platform
+[WARNING]
+.The `JUnitPlatform` runner has been deprecated
+====
+The `JUnitPlatform` runner was developed by the JUnit team as an interim solution for
+running test suites and tests on the JUnit Platform in a JUnit 4 environment.
+
+In recent years, all mainstream build tools and IDEs provide built-in support for running
+tests directly on the JUnit Platform.
+
+In addition, the introduction of `@Suite` support provided by the
+`junit-platform-suite-engine` module makes the `JUnitPlatform` runner obsolete. See
+<> for details.
+
+The `JUnitPlatform` runner and `@UseTechnicalNames` annotation have therefore been
+deprecated in JUnit Platform 1.8 and will be removed in JUnit Platform 2.0.
+
+If you are using the `JUnitPlatform` runner, please migrate to the `@Suite` support.
+====
+
The `JUnitPlatform` runner is a JUnit 4 based `Runner` which enables you to run any test
whose programming model is supported on the JUnit Platform in a JUnit 4 environment --
for example, a JUnit Jupiter test class.
@@ -609,8 +784,7 @@ build systems that support JUnit 4 but do not yet support the JUnit Platform dir
NOTE: Since the JUnit Platform has features that JUnit 4 does not have, the runner is
only able to support a subset of the JUnit Platform functionality, especially with regard
-to reporting (see <>). But for the
-time being the `JUnitPlatform` runner is an easy way to get started.
+to reporting (see <>).
[[running-tests-junit-platform-runner-setup]]
==== Setup
@@ -618,6 +792,7 @@ time being the `JUnitPlatform` runner is an easy way to get started.
You need the following artifacts and their dependencies on the classpath. See
<> for details regarding group IDs, artifact IDs, and versions.
+[[running-tests-junit-platform-runner-setup-explicit-dependencies]]
===== Explicit Dependencies
* `junit-platform-runner` in _test_ scope: location of the `JUnitPlatform` runner
@@ -627,9 +802,11 @@ You need the following artifacts and their dependencies on the classpath. See
* `junit-jupiter-engine` in _test runtime_ scope: implementation of the `TestEngine` API
for JUnit Jupiter
+[[running-tests-junit-platform-runner-setup-transitive-dependencies]]
===== Transitive Dependencies
* `junit-platform-suite-api` in _test_ scope
+* `junit-platform-suite-commons` in _test_ scope
* `junit-platform-launcher` in _test_ scope
* `junit-platform-engine` in _test_ scope
* `junit-platform-commons` in _test_ scope
@@ -639,14 +816,14 @@ You need the following artifacts and their dependencies on the classpath. See
==== Display Names vs. Technical Names
To define a custom _display name_ for the class run via `@RunWith(JUnitPlatform.class)`
-simply annotate the class with `@SuiteDisplayName` and provide a custom value.
+annotate the class with `@SuiteDisplayName` and provide a custom value.
By default, _display names_ will be used for test artifacts; however, when the
`JUnitPlatform` runner is used to execute tests with a build tool such as Gradle or
Maven, the generated test report often needs to include the _technical names_ of test
artifacts — for example, fully qualified class names — instead of shorter display names
like the simple name of a test class or a custom display name containing special
-characters. To enable technical names for reporting purposes, simply declare the
+characters. To enable technical names for reporting purposes, declare the
`@UseTechnicalNames` annotation alongside `@RunWith(JUnitPlatform.class)`.
Note that the presence of `@UseTechnicalNames` overrides any custom display name
@@ -696,8 +873,8 @@ infrastructure.
In addition to instructing the platform which test classes and test engines to include,
which packages to scan, etc., it is sometimes necessary to provide additional custom
-configuration parameters that are specific to a particular test engine or registered
-extension. For example, the JUnit Jupiter `TestEngine` supports _configuration
+configuration parameters that are specific to a particular test engine, listener, or
+registered extension. For example, the JUnit Jupiter `TestEngine` supports _configuration
parameters_ for the following use cases.
- <>
@@ -736,32 +913,64 @@ parameters_ used for the following features.
- <>
- <>
+- <>
If the value for the given _configuration parameter_ consists solely of an asterisk
-(`+*+`), the pattern will match against all candidate classes. Otherwise, the value will
-be treated as a comma-separated list of patterns where each pattern will be matched
+(`+++*+++`), the pattern will match against all candidate classes. Otherwise, the value
+will be treated as a comma-separated list of patterns where each pattern will be matched
against the fully qualified class name (_FQCN_) of each candidate class. Any dot (`.`) in
a pattern will match against a dot (`.`) or a dollar sign (`$`) in a FQCN. Any asterisk
-(`+*+`) will match against one or more characters in a FQCN. All other characters in a
+(`+++*+++`) will match against one or more characters in a FQCN. All other characters in a
pattern will be matched one-to-one against a FQCN.
Examples:
-- `+*+`: matches all candidate classes.
-- `+org.junit.*+`: matches all candidate classes under the `org.junit` base package and
+- `+++*+++`: matches all candidate classes.
+- `+++org.junit.*+++`: matches all candidate classes under the `org.junit` base package and
any of its subpackages.
-- `+*.MyCustomImpl+`: matches every candidate class whose simple class name is exactly
+- `+++*.MyCustomImpl+++`: matches every candidate class whose simple class name is exactly
`MyCustomImpl`.
-- `+*System*+`: matches every candidate class whose FQCN contains `System`.
-- `+*System*+, +*Unit*+`: matches every candidate class whose FQCN contains `System` or
- `Unit`.
+- `+++*System*+++`: matches every candidate class whose FQCN contains `System`.
+- `+++*System*+++, +++*Unit*+++`: matches every candidate class whose FQCN contains
+ `System` or `Unit`.
- `org.example.MyCustomImpl`: matches the candidate class whose FQCN is exactly
`org.example.MyCustomImpl`.
- `org.example.MyCustomImpl, org.example.TheirCustomImpl`: matches candidate classes whose
FQCN is exactly `org.example.MyCustomImpl` or `org.example.TheirCustomImpl`.
+[[running-tests-tags]]
+=== Tags
+
+Tags are a JUnit Platform concept for marking and filtering tests. The programming model
+for adding tags to containers and tests is defined by the testing framework. For example,
+in JUnit Jupiter based tests, the `@Tag` annotation (see
+<>) should be used. For JUnit 4 based tests, the
+Vintage engine maps `@Category` annotations to tags (see
+<>). Other testing frameworks may define their
+own annotation or other means for users to specify tags.
+
+[[running-tests-tag-syntax-rules]]
+==== Syntax Rules for Tags
+
+Regardless how a tag is specified, the JUnit Platform enforces the following rules:
+
+* A tag must not be `null` or _blank_.
+* A _trimmed_ tag must not contain whitespace.
+* A _trimmed_ tag must not contain ISO control characters.
+* A _trimmed_ tag must not contain any of the following _reserved characters_.
+- `,`: _comma_
+- `(`: _left parenthesis_
+- `)`: _right parenthesis_
+- `&`: _ampersand_
+- `|`: _vertical bar_
+- `!`: _exclamation point_
+
+NOTE: In the above context, "trimmed" means that leading and trailing whitespace
+characters have been removed.
+
[[running-tests-tag-expressions]]
-=== Tag Expressions
+==== Tag Expressions
+
Tag expressions are boolean expressions with the operators `!`, `&` and `|`. In addition,
`(` and `)` can be used to adjust for operator precedence.
@@ -788,19 +997,19 @@ expressions can be useful.
| Tag Expression
| Selection
-| +product+
+| `+++product+++`
| all tests for *product*
-| +catalog \| shipping+
+| `+++catalog \| shipping+++`
| all tests for *catalog* plus all tests for *shipping*
-| +catalog & shipping+
+| `+++catalog & shipping+++`
| all tests for the intersection between *catalog* and *shipping*
-| +product & !end-to-end+
+| `+++product & !end-to-end+++`
| all tests for *product*, but not the _end-to-end_ tests
-| +(micro \| integration) & (product \| shipping)+
+| `+++(micro \| integration) & (product \| shipping)+++`
| all _micro_ or _integration_ tests for *product* or *shipping*
|===
@@ -808,7 +1017,7 @@ expressions can be useful.
=== Capturing Standard Output/Error
Since version 1.3, the JUnit Platform provides opt-in support for capturing output
-printed to `System.out` and `System.err`. To enable it, simply set the
+printed to `System.out` and `System.err`. To enable it, set the
`junit.platform.output.capture.stdout` and/or `junit.platform.output.capture.stderr`
<> to `true`. In addition, you may
configure the maximum number of buffered bytes to be used per executed test or container
@@ -825,12 +1034,72 @@ because particularly when
<> it would be impossible
to attribute it to a specific test or container.
-WARNING: Capturing output is currently an _experimental_ feature. You're invited to give
-it a try and provide feedback to the JUnit team so they can improve and eventually
-<> this feature.
-
-[[running-tests-flight-recorder]]
-=== Flight Recorder Support
+[[running-tests-listeners]]
+=== Using Listeners and Interceptors
+
+The JUnit Platform provides the following listener APIs that allow JUnit, third parties,
+and custom user code to react to events fired at various points during the discovery and
+execution of a `TestPlan`.
+
+* `{LauncherSessionListener}`: receives events when a `{LauncherSession}` is opened and
+ closed.
+* `{LauncherInterceptor}`: intercepts test discovery and execution in the context of a
+ `LauncherSession`.
+* `{LauncherDiscoveryListener}`: receives events that occur during test discovery.
+* `{TestExecutionListener}`: receives events that occur during test execution.
+
+The `LauncherSessionListener` API is typically implemented by build tools or IDEs and
+registered automatically for you in order to support some feature of the build tool or IDE.
+
+The `LauncherDiscoveryListener` and `TestExecutionListener` APIs are often implemented in
+order to produce some form of report or to display a graphical representation of the test
+plan in an IDE. Such listeners may be implemented and automatically registered by a build
+tool or IDE, or they may be included in a third-party library – potentially registered
+for you automatically. You can also implement and register your own listeners.
+
+For details on registering and configuring listeners, see the following sections of this
+guide.
+
+* <>
+* <>
+* <>
+* <>
+* <>
+* <>
+
+The JUnit Platform provides the following listeners which you may wish to use with your
+test suite.
+
+<> ::
+ `{LegacyXmlReportGeneratingListener}` can be used via the
+ <> or registered manually to generate XML reports
+ compatible with the de facto standard for JUnit 4 based test reports.
++
+`{OpenTestReportGeneratingListener}` generates an XML report in the event-based format
+specified by {OpenTestReporting}. It is auto-registered and can be enabled and
+configured via <>.
++
+See <> for details.
+
+<> ::
+ `FlightRecordingExecutionListener` and `FlightRecordingDiscoveryListener` that generate
+ Java Flight Recorder events during test discovery and execution.
+
+`{LoggingListener}` ::
+ `TestExecutionListener` for logging informational messages for all events via a
+ `BiConsumer` that consumes `Throwable` and `Supplier`.
+
+`{SummaryGeneratingListener}` ::
+ `TestExecutionListener` that generates a summary of the test execution which can be
+ printed via a `PrintWriter`.
+
+`{UniqueIdTrackingListener}` ::
+ `TestExecutionListener` that that tracks the unique IDs of all tests that were skipped
+ or executed during the execution of the `TestPlan` and generates a file containing the
+ unique IDs once execution of the `TestPlan` has finished.
+
+[[running-tests-listeners-flight-recorder]]
+==== Flight Recorder Support
Since version 1.7, the JUnit Platform provides opt-in support for generating Flight
Recorder events. https://openjdk.java.net/jeps/328[JEP 328] describes the Java Flight
@@ -843,9 +1112,10 @@ to a problem.
In order to record Flight Recorder events generated while running tests, you need to:
-1. Provide module `org.junit.platform.jfr` (`junit-platform-jfr-{platform-version}.jar`)
+1. Ensure that you are using either Java 8 Update 262 or higher or Java 11 or later.
+2. Provide the `org.junit.platform.jfr` module (`junit-platform-jfr-{platform-version}.jar`)
on the class-path or module-path at test runtime.
-2. Start flight recording when launching a test run. Flight Recorder can be started via
+3. Start flight recording when launching a test run. Flight Recorder can be started via
java command line option:
-XX:StartFlightRecording:filename=...
@@ -857,6 +1127,22 @@ https://docs.oracle.com/en/java/javase/14/docs/specs/man/jfr.html[jfr]
command line tool shipped with recent JDKs or open the recording file with
https://jdk.java.net/jmc/[JDK Mission Control].
-WARNING: Flight Recorder support is currently an _experimental_ feature. You're
-invited to give it a try and provide feedback to the JUnit team so they can improve
-and eventually <> this feature.
+WARNING: Flight Recorder support is currently an _experimental_ feature. You're invited to
+give it a try and provide feedback to the JUnit team so they can improve and eventually
+<> this feature.
+
+[[stacktrace-pruning]]
+=== Stack Trace Pruning
+
+Since version 1.10, the JUnit Platform provides built-in support for pruning stack traces
+produced by failing tests. This feature is enabled by default but can be disabled by
+setting the `junit.platform.stacktrace.pruning.enabled` _configuration parameter_ to
+`false`.
+
+When enabled, all calls from the `org.junit`, `jdk.internal.reflect`, and `sun.reflect`
+packages are removed from the stack trace, unless the calls occur after the test itself
+or any of its ancestors. For that reason, calls to `{Assertions}` or `{Assumptions}` will
+never be excluded.
+
+In addition, all elements prior to and including the first call from the JUnit Platform
+Launcher will be removed.
diff --git a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc
index 3c6a686f3038..0a0862b6f2b8 100644
--- a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc
@@ -29,15 +29,16 @@ in the `junit-jupiter-api` module.
| `@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_.
-| `@TestMethodOrder` | Used to configure the <> for the annotated test class; similar to JUnit 4's `@FixMethodOrder`. Such annotations are _inherited_.
+| `@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 _hidden_ or _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 _hidden_ or _overridden_) and must be `static` (unless the "per-class" <> is used).
-| `@Nested` | Denotes that the annotated class is a non-static <>. `@BeforeAll` and `@AfterAll` methods cannot be used directly in a `@Nested` test class unless the "per-class" <> is used. Such annotations are not _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_.
@@ -96,27 +97,57 @@ void myFastTest() {
}
----
-[[writing-tests-classes-and-methods]]
-=== Test Classes and Methods
+[[writing-tests-definitions]]
+=== Definitions
+
+.Platform Concepts
+****
+Container::
+a node in the test tree that contains other containers or tests as its children (e.g. a _test class_).
-**Test Class**: any top-level class, `static` member class, or <> that contains at least one _test method_.
+Test::
+a node in the test tree that verifies expected behavior when executed (e.g. a `@Test` method).
+****
+.Jupiter Concepts
+****
+Lifecycle Method::
+any method that is directly annotated or meta-annotated with
+`@BeforeAll`, `@AfterAll`, `@BeforeEach`, or `@AfterEach`.
+
+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.
-**Test Method**: any instance method that is directly annotated or meta-annotated with
+Test Method::
+any instance method that is directly annotated or meta-annotated with
`@Test`, `@RepeatedTest`, `@ParameterizedTest`, `@TestFactory`, or `@TestTemplate`.
+With the exception of `@Test`, these create a _container_ in the test tree that groups
+_tests_ or, potentially (for `@TestFactory`), other _containers_.
+****
-**Lifecycle Method**: any method that is directly annotated or meta-annotated with
-`@BeforeAll`, `@AfterAll`, `@BeforeEach`, or `@AfterEach`.
+[[writing-tests-classes-and-methods]]
+=== Test Classes and Methods
Test methods and lifecycle methods may be declared locally within the current test class,
inherited from superclasses, or inherited from interfaces (see
<>). In addition, test methods and
-lifecycle methods must not be `abstract` and must not return a value.
+lifecycle methods must not be `abstract` and must not return a value (except `@TestFactory`
+methods which are required to return a value).
-NOTE: Test classes, test methods, and lifecycle methods are not required to be `public`,
-but they must _not_ be `private`.
+[NOTE]
+.Class and method visibility
+====
+Test classes, test methods, and lifecycle methods are not required to be `public`, but
+they must _not_ be `private`.
+
+It is generally recommended to omit the `public` modifier for test classes, test methods,
+and lifecycle methods unless there is a technical reason for doing so – for example, when
+a test class is extended by a test class in another package. Another technical reason for
+making classes and methods `public` is to simplify testing on the module path when using
+the Java Module System.
+====
The following test class demonstrates the use of `@Test` methods and all supported
lifecycle methods. For further information on runtime semantics, see
@@ -239,11 +270,10 @@ include::{testDir}/example/AssertionsDemo.java[tags=user_guide]
[WARNING]
.Preemptive Timeouts with `assertTimeoutPreemptively()`
====
-Contrary to <>, the various
-`assertTimeoutPreemptively()` methods in the `Assertions` class execute the provided
-`executable` or `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
-`executable` or `supplier` relies on `java.lang.ThreadLocal` storage.
+The various `assertTimeoutPreemptively()` methods in the `Assertions` class execute
+the provided `executable` or `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 `executable` or `supplier` relies on `java.lang.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
@@ -388,17 +418,26 @@ the `org.junit.jupiter.api.condition` package.
====
[[writing-tests-conditional-execution-os]]
-==== Operating System Conditions
+==== Operating System and Architecture Conditions
-A container or test may be enabled or disabled on a particular operating system via the
-`{EnabledOnOs}` and `{DisabledOnOs}` annotations.
+A container or test may be enabled or disabled on a particular operating system,
+architecture, or combination of both via the `{EnabledOnOs}` and `{DisabledOnOs}`
+annotations.
[[writing-tests-conditional-execution-os-demo]]
[source,java,indent=0]
+.Conditional execution based on operating system
----
include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_os]
----
+[[writing-tests-conditional-execution-architectures-demo]]
+[source,java,indent=0]
+.Conditional execution based on architecture
+----
+include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_architecture]
+----
+
[[writing-tests-conditional-execution-jre]]
==== Java Runtime Environment Conditions
@@ -414,6 +453,21 @@ half open ranges.
include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_jre]
----
+[[writing-tests-conditional-execution-native]]
+==== Native Image Conditions
+
+A container or test may be enabled or disabled within a
+https://www.graalvm.org/reference-manual/native-image/[GraalVM native image] via the
+`{EnabledInNativeImage}` and `{DisabledInNativeImage}` annotations. These annotations are
+typically used when running tests within a native image using the Gradle and Maven
+plug-ins from the GraalVM https://graalvm.github.io/native-build-tools/latest/[Native
+Build Tools] project.
+
+[source,java,indent=0]
+----
+include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_native]
+----
+
[[writing-tests-conditional-execution-system-properties]]
==== System Property Conditions
@@ -460,45 +514,67 @@ indirectly present, or meta-present on a given element.
[[writing-tests-conditional-execution-custom]]
==== Custom Conditions
-A container or test may be enabled or disabled based on the boolean return of a
-method via the `{EnabledIf}` and `{DisabledIf}` annotations. The method is provided to
-the annotation via its name, or its fully qualified name if located outside the test
-class.
-If needed, the condition method can take a single parameter of type `ExtensionContext`.
+As an alternative to implementing an <>, a
+container or test may be enabled or disabled based on a _condition method_ configured via
+the `{EnabledIf}` and `{DisabledIf}` annotations. A condition method must have a `boolean`
+return type and may accept either no arguments or a single `ExtensionContext` argument.
+
+The following test class demonstrates how to configure a local method named
+`customCondition` via `@EnabledIf` and `@DisabledIf`.
[source,java,indent=0]
----
include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_custom]
----
-NOTE: When `{EnabledIf}` or `{DisabledIf}` is used at class level, the condition method
-must always be `static`. Condition methods located in external classes must also be
-`static`. In any other case, you can use both static or instance methods.
+Alternatively, the condition method can be located outside the test class. In this case,
+it must be referenced by its _fully qualified name_ as demonstrated in the following
+example.
+
+[source,java,indent=0]
+----
+package example;
+
+include::{testDir}/example/ExternalCustomConditionDemo.java[tags=user_guide_external_custom_condition]
+----
+
+[NOTE]
+====
+There are several cases where a condition method would need to be `static`:
+- when `@EnabledIf` or `@DisabledIf` is used at class level
+- when `@EnabledIf` or `@DisabledIf` is used on a `@ParameterizedTest` or a
+ `@TestTemplate` method
+- when the condition method is located in an external class
-[[writing-tests-tagging-and-filtering]]
-=== Tagging and Filtering
+In any other case, you can use either static methods or instance methods as condition
+methods.
+====
-Test classes and methods can be tagged via the `@Tag` annotation. Those tags can later be
-used to filter <>.
+[TIP]
+====
+It is often the case that you can use an existing static method in a utility class as a
+custom condition.
-TIP: See also: <>
+For example, `java.awt.GraphicsEnvironment` provides a `public static boolean isHeadless()`
+method that can be used to determine if the current environment does not support a
+graphical display. Thus, if you have a test that depends on graphical support you can
+disable it when such support is unavailable as follows.
-==== Syntax Rules for Tags
+[source,java,indent=0]
+----
+@DisabledIf(value = "java.awt.GraphicsEnvironment#isHeadless",
+ disabledReason = "headless environment")
+----
+====
-* A tag must not be `null` or _blank_.
-* A _trimmed_ tag must not contain whitespace.
-* A _trimmed_ tag must not contain ISO control characters.
-* A _trimmed_ tag must not contain any of the following _reserved characters_.
- - `,`: _comma_
- - `(`: _left parenthesis_
- - `)`: _right parenthesis_
- - `&`: _ampersand_
- - `|`: _vertical bar_
- - `!`: _exclamation point_
+[[writing-tests-tagging-and-filtering]]
+=== Tagging and Filtering
-NOTE: In the above context, "trimmed" means that leading and trailing whitespace
-characters have been removed.
+Test classes and methods can be tagged via the `@Tag` annotation. Those tags can later be
+used to filter <>. Please refer to the
+<> section for more information about tag support in the JUnit
+Platform.
[source,java,indent=0]
----
@@ -511,11 +587,15 @@ custom annotations for tags.
[[writing-tests-test-execution-order]]
=== Test Execution Order
-By default, test methods will be ordered using an algorithm that is deterministic but
-intentionally nonobvious. This ensures that subsequent runs of a test suite execute test
-methods in the same order, thereby allowing for repeatable builds.
+By default, test classes and methods will be ordered using an algorithm that is
+deterministic but intentionally nonobvious. This ensures that subsequent runs of a test
+suite execute test classes and test methods in the same order, thereby allowing for
+repeatable builds.
-NOTE: See <> for a definition of _test method_.
+NOTE: See <> for a definition of _test method_ and _test class_.
+
+[[writing-tests-test-execution-order-methods]]
+==== Method Order
Although true _unit tests_ typically should not rely on the order in which they are
executed, there are times when it is necessary to enforce a specific test method execution
@@ -528,17 +608,18 @@ interface with `{TestMethodOrder}` and specify the desired `{MethodOrderer}`
implementation. You can implement your own custom `MethodOrderer` or use one of the
following built-in `MethodOrderer` implementations.
-* `{DisplayName}`: sorts test methods _alphanumerically_ based on their display names (see
- <>)
-* `{MethodName}`: sorts test methods _alphanumerically_ based on their method name and formal
- parameter lists.
-* `{OrderAnnotation}`: sorts test methods _numerically_ based on values specified via the
- `{Order}` annotation.
-* `{Random}`: orders test methods _pseudo-randomly_ and supports configuration of a custom
- _seed_.
-* `{Alphanumeric}`: sorts test methods _alphanumerically_ based on their names and formal
- parameter lists. **Deprecated in favor of `{MethodName}`. Will be removed in 6.0**
+* `{MethodOrderer_DisplayName}`: sorts test methods _alphanumerically_ based on their
+ display names (see <>)
+* `{MethodOrderer_MethodName}`: sorts test methods _alphanumerically_ based on their names
+ and formal parameter lists
+* `{MethodOrderer_OrderAnnotation}`: sorts test methods _numerically_ based on values
+ specified via the `{Order}` annotation
+* `{MethodOrderer_Random}`: orders test methods _pseudo-randomly_ and supports
+ configuration of a custom _seed_
+* `{MethodOrderer_Alphanumeric}`: sorts test methods _alphanumerically_ based on their
+ names and formal parameter lists; **deprecated in favor of `{MethodOrderer_MethodName}`,
+ to be removed in 6.0**
NOTE: See also: <>
@@ -550,8 +631,8 @@ order specified via the `@Order` annotation.
include::{testDir}/example/OrderedTestsDemo.java[tags=user_guide]
----
-[[writing-tests-test-execution-order-default]]
-==== Setting the Default Method Orderer
+[[writing-tests-test-execution-order-methods-default]]
+===== Setting the Default Method Orderer
You can use the `junit.jupiter.testmethod.order.default` <> to specify the fully qualified class name of the
@@ -560,9 +641,9 @@ via the `{TestMethodOrder}` annotation, the supplied class has to implement the
`MethodOrderer` interface. The default orderer will be used for all tests unless the
`@TestMethodOrder` annotation is present on an enclosing test class or test interface.
-For example, to use the `{OrderAnnotation}` method orderer by default, you should set the
-configuration parameter to the corresponding fully qualified class name (e.g., in
-`src/test/resources/junit-platform.properties`):
+For example, to use the `{MethodOrderer_OrderAnnotation}` method orderer by default, you
+should set the configuration parameter to the corresponding fully qualified class name
+(e.g., in `src/test/resources/junit-platform.properties`):
[source,properties,indent=0]
----
@@ -573,14 +654,81 @@ junit.jupiter.testmethod.order.default = \
Similarly, you can specify the fully qualified name of any custom class that implements
`MethodOrderer`.
+[[writing-tests-test-execution-order-classes]]
+==== Class Order
+
+Although test classes typically should not rely on the order in which they are executed,
+there are times when it is desirable to enforce a specific test class execution order. You
+may wish to execute test classes in a random order to ensure there are no accidental
+dependencies between test classes, or you may wish to order test classes to optimize build
+time as outlined in the following scenarios.
+
+* Run previously failing tests and faster tests first: "fail fast" mode
+* With parallel execution enabled, schedule longer tests first: "shortest test plan
+ execution duration" mode
+* Various other use cases
+
+To configure test class execution order _globally_ for the entire test suite, use the
+`junit.jupiter.testclass.order.default` <> to specify the fully qualified class name of the `{ClassOrderer}` you would
+like to use. The supplied class must implement the `ClassOrderer` interface.
+
+You can implement your own custom `ClassOrderer` or use one of the following built-in
+`ClassOrderer` implementations.
+
+* `{ClassOrderer_ClassName}`: sorts test classes _alphanumerically_ based on their fully
+ qualified class names
+* `{ClassOrderer_DisplayName}`: sorts test classes _alphanumerically_ based on their
+ display names (see <>)
+* `{ClassOrderer_OrderAnnotation}`: sorts test classes _numerically_ based on values
+ specified via the `{Order}` annotation
+* `{ClassOrderer_Random}`: orders test classes _pseudo-randomly_ and supports
+ configuration of a custom _seed_
+
+For example, for the `@Order` annotation to be honored on _test classes_, you should
+configure the `{ClassOrderer_OrderAnnotation}` class orderer using the configuration
+parameter with the corresponding fully qualified class name (e.g., in
+`src/test/resources/junit-platform.properties`):
+
+[source,properties,indent=0]
+----
+junit.jupiter.testclass.order.default = \
+ org.junit.jupiter.api.ClassOrderer$OrderAnnotation
+----
+
+The configured `ClassOrderer` will be applied to all top-level test classes (including
+`static` nested test classes) and `@Nested` test classes.
+
+NOTE: Top-level test classes will be ordered relative to each other; whereas, `@Nested`
+test classes will be ordered relative to other `@Nested` test classes sharing the same
+_enclosing class_.
+
+To configure test class execution order _locally_ for `@Nested` test classes, declare the
+`{TestClassOrder}` annotation on the enclosing class for the `@Nested` test classes you
+want to order, and supply a class reference to the `ClassOrderer` implementation you would
+like to use directly in the `@TestClassOrder` annotation. The configured `ClassOrderer`
+will be applied recursively to `@Nested` test classes and their `@Nested` test classes.
+Note that a local `@TestClassOrder` declaration always overrides an inherited
+`@TestClassOrder` declaration or a `ClassOrderer` configured globally via the
+`junit.jupiter.testclass.order.default` configuration parameter.
+
+The following example demonstrates how to guarantee that `@Nested` test classes are
+executed in the order specified via the `@Order` annotation.
+
+[source,java,indent=0]
+----
+include::{testDir}/example/OrderedNestedTestClassesDemo.java[tags=user_guide]
+----
+
[[writing-tests-test-instance-lifecycle]]
=== Test Instance Lifecycle
In order to allow individual test methods to be executed in isolation and to avoid
unexpected side effects due to mutable test instance state, JUnit creates a new instance
of each test class before executing each _test method_ (see
-<>). This "per-method" test instance lifecycle is the
-default behavior in JUnit Jupiter and is analogous to all previous versions of JUnit.
+<>). This "per-method" test instance lifecycle is the default
+behavior in JUnit Jupiter and is analogous to all previous versions of JUnit.
NOTE: Please note that the test class will still be instantiated if a given _test method_
is _disabled_ via a <> (e.g., `@Disabled`,
@@ -598,9 +746,13 @@ Specifically, with the "per-class" mode it becomes possible to declare `@BeforeA
"per-class" mode therefore also makes it possible to use `@BeforeAll` and `@AfterAll`
methods in `@Nested` test classes.
+NOTE: Beginning with Java 16, `@BeforeAll` and `@AfterAll` methods can be declared as
+`static` in `@Nested` test classes.
+
If you are authoring tests using the Kotlin programming language, you may also find it
-easier to implement `@BeforeAll` and `@AfterAll` methods by switching to the "per-class"
-test instance lifecycle mode.
+easier to implement non-static `@BeforeAll` and `@AfterAll` lifecycle methods as well as
+`@MethodSource` factory methods by switching to the "per-class" test instance lifecycle
+mode.
[[writing-tests-test-instance-lifecycle-changing-default]]
==== Changing the Default Test Instance Lifecycle
@@ -642,7 +794,9 @@ configuration file instead of via a JVM system property.
=== Nested Tests
`@Nested` tests give the test writer more capabilities to express the relationship among
-several groups of tests. Here's an elaborate example.
+several groups of tests. Such nested tests make use of Java's nested classes and
+facilitate hierarchical thinking about the test structure. Here's an elaborate example,
+both as source code and as a screenshot of the execution within an IDE.
[source,java,indent=0]
.Nested test suite for testing a stack
@@ -650,13 +804,29 @@ several groups of tests. Here's an elaborate example.
include::{testDir}/example/TestingAStackDemo.java[tags=user_guide]
----
+When executing this example in an IDE, the test execution tree in the GUI will look
+similar to the following image.
+
+image::writing-tests_nested_test_ide.png[caption='',title='Executing a nested test in an IDE']
+
+In this example, preconditions from outer tests are used in inner tests by defining
+hierarchical lifecycle methods for the setup code. For example, `createNewStack()` is a
+`@BeforeEach` lifecycle method that is used in the test class in which it is defined and
+in all levels in the nesting tree below the class in which it is defined.
+
+The fact that setup code from outer tests is run before inner tests are executed gives you
+the ability to run all tests independently. You can even run inner tests alone without
+running the outer tests, because the setup code from the outer tests is always executed.
+
NOTE: _Only non-static nested classes_ (i.e. _inner classes_) can serve as `@Nested` test
-classes. Nesting can be arbitrarily deep, and those inner classes are considered to be
-full members of the test class family with one exception: `@BeforeAll` and `@AfterAll`
-methods do not work _by default_. The reason is that Java does not allow `static` members
-in inner classes. However, this restriction can be circumvented by annotating a `@Nested`
-test class with `@TestInstance(Lifecycle.PER_CLASS)` (see
-<>).
+classes. Nesting can be arbitrarily deep, and those inner classes are subject to full
+lifecycle support with one exception: `@BeforeAll` and `@AfterAll` methods do not work _by
+default_. The reason is that Java does not allow `static` members in inner classes prior
+to Java 16. However, this restriction can be circumvented by annotating a `@Nested` test
+class with `@TestInstance(Lifecycle.PER_CLASS)` (see
+<>). If you are using Java 16 or higher,
+`@BeforeAll` and `@AfterAll` methods can be declared as `static` in `@Nested` test
+classes, and this restriction no longer applies.
[[writing-tests-dependency-injection]]
=== Dependency Injection for Constructors and Methods
@@ -669,8 +839,8 @@ constructors and methods.
`{ParameterResolver}` defines the API for test extensions that wish to _dynamically_
resolve parameters at runtime. If a _test class_ constructor, a _test method_, or a
-_lifecycle method_ (see <>) accepts a parameter, the
-parameter must be resolved at runtime by a registered `ParameterResolver`.
+_lifecycle method_ (see <>) accepts a parameter, the parameter
+must be resolved at runtime by a registered `ParameterResolver`.
There are currently three built-in resolvers that are registered automatically.
@@ -691,13 +861,13 @@ following demonstrates how to have `TestInfo` injected into a test constructor,
include::{testDir}/example/TestInfoDemo.java[tags=user_guide]
----
-* `{RepetitionInfoParameterResolver}`: if a method parameter in a `@RepeatedTest`,
- `@BeforeEach`, or `@AfterEach` method is of type `{RepetitionInfo}`, the
- `RepetitionInfoParameterResolver` will supply an instance of `RepetitionInfo`.
- `RepetitionInfo` can then be used to retrieve information about the current repetition
- and the total number of repetitions for the corresponding `@RepeatedTest`. Note,
- however, that `RepetitionInfoParameterResolver` is not registered outside the context
- of a `@RepeatedTest`. See <>.
+* `{RepetitionExtension}`: if a method parameter in a `@RepeatedTest`, `@BeforeEach`, or
+ `@AfterEach` method is of type `{RepetitionInfo}`, the `RepetitionExtension` will supply
+ an instance of `RepetitionInfo`. `RepetitionInfo` can then be used to retrieve
+ information about the current repetition, the total number of repetitions, the number of
+ repetitions that have failed, and the failure threshold for the corresponding
+ `@RepeatedTest`. Note, however, that `RepetitionExtension` is not registered outside the
+ context of a `@RepeatedTest`. See <>.
* `{TestReporterParameterResolver}`: if a constructor or method parameter is of type
`{TestReporter}`, the `TestReporterParameterResolver` will supply an instance of
@@ -849,10 +1019,32 @@ void repeatedTest() {
}
----
-In addition to specifying the number of repetitions, a custom display name can be
-configured for each repetition via the `name` attribute of the `@RepeatedTest`
-annotation. Furthermore, the display name can be a pattern composed of a combination of
-static text and dynamic placeholders. The following placeholders are currently supported.
+Since JUnit Jupiter 5.10, `@RepeatedTest` can be configured with a failure threshold which
+signifies the number of failures after which remaining repetitions will be automatically
+skipped. Set the `failureThreshold` attribute to a positive number less than the total
+number of repetitions in order to skip the invocations of remaining repetitions after the
+specified number of failures has been encountered.
+
+For example, if you are using `@RepeatedTest` to repeatedly invoke a test that you suspect
+to be _flaky_, a single failure is sufficient to demonstrate that the test is flaky, and
+there is no need to invoke the remaining repetitions. To support that specific use case,
+set `failureThreshold = 1`. You can alternatively set the threshold to a number greater
+than 1 depending on your use case.
+
+By default, the `failureThreshold` attribute is set to `Integer.MAX_VALUE`, signaling that
+no failure threshold will be applied, which effectively means that the specified number of
+repetitions will be invoked regardless of whether any repetitions fail.
+
+WARNING: If the repetitions of a `@RepeatedTest` method are executed in parallel, no
+guarantees can be made regarding the failure threshold. It is therefore recommended that a
+`@RepeatedTest` method be annotated with `@Execution(SAME_THREAD)` when parallel execution
+is configured. See <> for further details.
+
+In addition to specifying the number of repetitions and failure threshold, a custom
+display name can be configured for each repetition via the `name` attribute of the
+`@RepeatedTest` annotation. Furthermore, the display name can be a pattern composed of a
+combination of static text and dynamic placeholders. The following placeholders are
+currently supported.
- `{displayName}`: display name of the `@RepeatedTest` method
- `{currentRepetition}`: the current repetition count
@@ -868,9 +1060,10 @@ latter is equal to `"{displayName} :: repetition {currentRepetition} of
{totalRepetitions}"` which results in display names for individual repetitions like
`repeatedTest() :: repetition 1 of 10`, `repeatedTest() :: repetition 2 of 10`, etc.
-In order to retrieve information about the current repetition and the total number of
-repetitions programmatically, a developer can choose to have an instance of
-`RepetitionInfo` injected into a `@RepeatedTest`, `@BeforeEach`, or `@AfterEach` method.
+In order to retrieve information about the current repetition, the total number of
+repetitions, the number of repetitions that have failed, and the failure threshold, a
+developer can choose to have an instance of `{RepetitionInfo}` injected into a
+`@RepeatedTest`, `@BeforeEach`, or `@AfterEach` method.
[[writing-tests-repeated-tests-examples]]
==== Repeated Test Examples
@@ -878,11 +1071,15 @@ repetitions programmatically, a developer can choose to have an instance of
The `RepeatedTestsDemo` class at the end of this section demonstrates several examples of
repeated tests.
-The `repeatedTest()` method is identical to example from the previous section; whereas,
+The `repeatedTest()` method is identical to the example from the previous section; whereas,
`repeatedTestWithRepetitionInfo()` demonstrates how to have an instance of
`RepetitionInfo` injected into a test to access the total number of repetitions for the
current repeated test.
+`repeatedTestWithFailureThreshold()` demonstrates how to set a failure threshold and
+simulates an unexpected failure for every second repetition. The resulting behavior can be
+viewed in the `ConsoleLauncher` output at the end of this section.
+
The next two methods demonstrate how to include a custom `@DisplayName` for the
`@RepeatedTest` method in the display name of each repetition. `customDisplayName()`
combines a custom display name with a custom pattern and then uses `TestInfo` to verify
@@ -918,6 +1115,10 @@ INFO: About to execute repetition 2 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 3 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 4 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 5 of 5 for repeatedTestWithRepetitionInfo
+INFO: About to execute repetition 1 of 8 for repeatedTestWithFailureThreshold
+INFO: About to execute repetition 2 of 8 for repeatedTestWithFailureThreshold
+INFO: About to execute repetition 3 of 8 for repeatedTestWithFailureThreshold
+INFO: About to execute repetition 4 of 8 for repeatedTestWithFailureThreshold
INFO: About to execute repetition 1 of 1 for customDisplayName
INFO: About to execute repetition 1 of 1 for customDisplayNameWithLongPattern
INFO: About to execute repetition 1 of 5 for repeatedTestInGerman
@@ -954,6 +1155,15 @@ When using the `ConsoleLauncher` with the unicode theme enabled, execution of
│ │ ├─ repetition 3 of 5 ✔
│ │ ├─ repetition 4 of 5 ✔
│ │ └─ repetition 5 of 5 ✔
+│ ├─ repeatedTestWithFailureThreshold(RepetitionInfo) ✔
+│ │ ├─ repetition 1 of 8 ✔
+│ │ ├─ repetition 2 of 8 ✘ Boom!
+│ │ ├─ repetition 3 of 8 ✔
+│ │ ├─ repetition 4 of 8 ✘ Boom!
+│ │ ├─ repetition 5 of 8 ↷ Failure threshold [2] exceeded
+│ │ ├─ repetition 6 of 8 ↷ Failure threshold [2] exceeded
+│ │ ├─ repetition 7 of 8 ↷ Failure threshold [2] exceeded
+│ │ └─ repetition 8 of 8 ↷ Failure threshold [2] exceeded
│ ├─ Repeat! ✔
│ │ └─ Repeat! 1/1 ✔
│ ├─ Details... ✔
@@ -995,9 +1205,6 @@ palindromes(String) ✔
└─ [3] candidate=able was I ere I saw elba ✔
....
-WARNING: Parameterized tests are currently an _experimental_ feature. Consult the table
-in <> for details.
-
[[writing-tests-parameterized-tests-setup]]
==== Required Setup
@@ -1027,6 +1234,21 @@ parameterized method at the same index in the method's formal parameter list. An
_aggregator_ is any parameter of type `ArgumentsAccessor` or any parameter annotated with
`@AggregateWith`.
+[NOTE]
+.AutoCloseable arguments
+====
+Arguments that implement `java.lang.AutoCloseable` (or `java.io.Closeable` which extends
+`java.lang.AutoCloseable`) will be automatically closed after `@AfterEach` methods and
+`AfterEachCallback` extensions have been called for the current parameterized test
+invocation.
+
+To prevent this from happening, set the `autoCloseArguments` attribute in
+`@ParameterizedTest` to `false`. Specifically, if an argument that implements
+`AutoCloseable` is reused for multiple invocations of the same parameterized test method,
+you must annotate the method with `@ParameterizedTest(autoCloseArguments = false)` to
+ensure that the argument is not closed between invocations.
+====
+
[[writing-tests-parameterized-tests-sources]]
==== Sources of Arguments
@@ -1074,11 +1296,13 @@ for parameterized tests that accept a single argument.
* `{NullSource}`: provides a single `null` argument to the annotated `@ParameterizedTest`
method.
- `@NullSource` cannot be used for a parameter that has a primitive type.
-* `{EmptySource}`: provides a single _empty_ argument to the annotated `@ParameterizedTest`
- method for parameters of the following types: `java.lang.String`, `java.util.List`,
- `java.util.Set`, `java.util.Map`, primitive arrays (e.g., `int[]`, `char[][]`, etc.),
- object arrays (e.g.,`String[]`, `Integer[][]`, etc.).
- - Subtypes of the supported types are not supported.
+* `{EmptySource}`: provides a single _empty_ argument to the annotated
+ `@ParameterizedTest` method for parameters of the following types: `java.lang.String`,
+ `java.util.Collection` (and concrete subtypes with a `public` no-arg constructor),
+ `java.util.List`, `java.util.Set`, `java.util.SortedSet`, `java.util.NavigableSet`,
+ `java.util.Map` (and concrete subtypes with a `public` no-arg constructor),
+ `java.util.SortedMap`, `java.util.NavigableMap`, primitive arrays (e.g., `int[]`,
+ `char[][]`, etc.), object arrays (e.g., `String[]`, `Integer[][]`, etc.).
* `{NullAndEmptySource}`: a _composed annotation_ that combines the functionality of
`@NullSource` and `@EmptySource`.
@@ -1161,7 +1385,7 @@ or external classes.
Factory methods within the test class must be `static` unless the test class is annotated
with `@TestInstance(Lifecycle.PER_CLASS)`; whereas, factory methods in external classes
-must always be `static`. In addition, such factory methods must not accept any arguments.
+must always be `static`.
Each factory method must generate a _stream_ of _arguments_, and each set of arguments
within the stream will be provided as the physical arguments for individual invocations
@@ -1211,8 +1435,8 @@ interface. In addition, `Arguments.of(Object...)` may be used as an alternative
include::{testDir}/example/ParameterizedTestDemo.java[tags=multi_arg_MethodSource_example]
----
-An external, `static` _factory_ method can be referenced by providing its _fully
-qualified method name_ as demonstrated in the following example.
+An external, `static` _factory_ method can be referenced by providing its _fully qualified
+method name_ as demonstrated in the following example.
[source,java,indent=0]
----
@@ -1221,11 +1445,29 @@ package example;
include::{testDir}/example/ExternalMethodSourceDemo.java[tags=external_MethodSource_example]
----
+Factory methods can declare parameters, which will be provided by registered
+implementations of the `ParameterResolver` extension API. In the following example, the
+factory method is referenced by its name since there is only one such method in the test
+class. If there are several local methods with the same name, parameters can also be
+provided to differentiate them – for example, `@MethodSource("factoryMethod()")` or
+`@MethodSource("factoryMethod(java.lang.String)")`. Alternatively, the factory method
+can be referenced by its fully qualified method name, e.g.
+`@MethodSource("example.MyTests#factoryMethod(java.lang.String)")`.
+
+[source,java,indent=0]
+----
+include::{testDir}/example/MethodSourceParameterResolutionDemo.java[tags=parameter_resolution_MethodSource_example]
+----
+
+
[[writing-tests-parameterized-tests-sources-CsvSource]]
===== @CsvSource
-`@CsvSource` allows you to express argument lists as comma-separated values (i.e.,
-`String` literals).
+`@CsvSource` allows you to express argument lists as comma-separated values (i.e., CSV
+`String` literals). Each string provided via the `value` attribute in `@CsvSource`
+represents a CSV record and results in one invocation of the parameterized test. The first
+record may optionally be used to supply CSV headers (see the Javadoc for the
+`useHeadersInDisplayName` attribute for details and an example).
[source,java,indent=0]
----
@@ -1237,33 +1479,115 @@ The default delimiter is a comma (`,`), but you can use another character by set
`String` delimiter instead of a single character. However, both delimiter attributes
cannot be set simultaneously.
-`@CsvSource` uses a single quote `'` as its quote character. See the `'lemon, lime'` value
-in the example above and in the table below. An empty, quoted value `''` results in an
-empty `String` unless the `emptyValue` attribute is set; whereas, an entirely _empty_
-value is interpreted as a `null` reference. By specifying one or more `nullValues`, a
-custom value can be interpreted as a `null` reference (see the `NIL` example in the table
-below). An `ArgumentConversionException` is thrown if the target type of a `null`
-reference is a primitive type.
+By default, `@CsvSource` uses a single quote (`'`) as its quote character, but this can be
+changed via the `quoteCharacter` attribute. See the `'lemon, lime'` value in the example
+above and in the table below. An empty, quoted value (`''`) results in an empty `String`
+unless the `emptyValue` attribute is set; whereas, an entirely _empty_ value is
+interpreted as a `null` reference. By specifying one or more `nullValues`, a custom value
+can be interpreted as a `null` reference (see the `NIL` example in the table below). An
+`ArgumentConversionException` is thrown if the target type of a `null` reference is a
+primitive type.
NOTE: An _unquoted_ empty value will always be converted to a `null` reference regardless
of any custom values configured via the `nullValues` attribute.
+Except within a quoted string, leading and trailing whitespace in a CSV column is trimmed
+by default. This behavior can be changed by setting the
+`ignoreLeadingAndTrailingWhitespace` attribute to `true`.
+
[cols="50,50"]
|===
-| Example Input | Resulting Argument List
-
-| `@CsvSource({ "apple, banana" })` | `"apple"`, `"banana"`
-| `@CsvSource({ "apple, 'lemon, lime'" })` | `"apple"`, `"lemon, lime"`
-| `@CsvSource({ "apple, ''" })` | `"apple"`, `""`
-| `@CsvSource({ "apple, " })` | `"apple"`, `null`
-| `@CsvSource(value = { "apple, banana, NIL" }, nullValues = "NIL")` | `"apple"`, `"banana"`, `null`
+| Example Input | Resulting Argument List
+
+| `@CsvSource({ "apple, banana" })` | `"apple"`, `"banana"`
+| `@CsvSource({ "apple, 'lemon, lime'" })` | `"apple"`, `"lemon, lime"`
+| `@CsvSource({ "apple, ''" })` | `"apple"`, `""`
+| `@CsvSource({ "apple, " })` | `"apple"`, `null`
+| `@CsvSource(value = { "apple, banana, NIL" }, nullValues = "NIL")` | `"apple"`, `"banana"`, `null`
+| `@CsvSource(value = { " apple , banana" }, ignoreLeadingAndTrailingWhitespace = false)` | `" apple "`, `" banana"`
|===
+If the programming language you are using supports _text blocks_ -- for example, Java SE
+15 or higher -- you can alternatively use the `textBlock` attribute of `@CsvSource`. Each
+record within a text block represents a CSV record and results in one invocation of the
+parameterized test. The first record may optionally be used to supply CSV headers by
+setting the `useHeadersInDisplayName` attribute to `true` as in the example below.
+
+Using a text block, the previous example can be implemented as follows.
+
+[source,java,indent=0]
+----
+@ParameterizedTest(name = "[{index}] {arguments}")
+@CsvSource(useHeadersInDisplayName = true, textBlock = """
+ FRUIT, RANK
+ apple, 1
+ banana, 2
+ 'lemon, lime', 0xF1
+ strawberry, 700_000
+ """)
+void testWithCsvSource(String fruit, int rank) {
+ // ...
+}
+----
+
+The generated display names for the previous example include the CSV header names.
+
+----
+[1] FRUIT = apple, RANK = 1
+[2] FRUIT = banana, RANK = 2
+[3] FRUIT = lemon, lime, RANK = 0xF1
+[4] FRUIT = strawberry, RANK = 700_000
+----
+
+In contrast to CSV records supplied via the `value` attribute, a text block can contain
+comments. Any line beginning with a `+++#+++` symbol will be treated as a comment and
+ignored. Note, however, that the `+++#+++` symbol must be the first character on the line
+without any leading whitespace. It is therefore recommended that the closing text block
+delimiter (`"""`) be placed either at the end of the last line of input or on the
+following line, left aligned with the rest of the input (as can be seen in the example
+below which demonstrates formatting similar to a table).
+
+[source,java,indent=0]
+----
+@ParameterizedTest
+@CsvSource(delimiter = '|', quoteCharacter = '"', textBlock = """
+ #-----------------------------
+ # FRUIT | RANK
+ #-----------------------------
+ apple | 1
+ #-----------------------------
+ banana | 2
+ #-----------------------------
+ "lemon lime" | 0xF1
+ #-----------------------------
+ strawberry | 700_000
+ #-----------------------------
+ """)
+void testWithCsvSource(String fruit, int rank) {
+ // ...
+}
+----
+
+[NOTE]
+====
+Java's https://docs.oracle.com/en/java/javase/15/text-blocks/index.html[text block]
+feature automatically removes _incidental whitespace_ when the code is compiled.
+However other JVM languages such as Groovy and Kotlin do not. Thus, if you are using a
+programming language other than Java and your text block contains comments or new lines
+within quoted strings, you will need to ensure that there is no leading whitespace within
+your text block.
+====
+
[[writing-tests-parameterized-tests-sources-CsvFileSource]]
===== @CsvFileSource
-`@CsvFileSource` lets you use CSV files from the classpath or the local file system. Each
-line from a CSV file results in one invocation of the parameterized test.
+`@CsvFileSource` lets you use comma-separated value (CSV) files from the classpath or the
+local file system. Each record from a CSV file results in one invocation of the
+parameterized test. The first record may optionally be used to supply CSV headers. You can
+instruct JUnit to ignore the headers via the `numLinesToSkip` attribute. If you would like
+for the headers to be used in the display names, you can set the `useHeadersInDisplayName`
+attribute to `true`. The examples below demonstrate the use of `numLinesToSkip` and
+`useHeadersInDisplayName`.
The default delimiter is a comma (`,`), but you can use another character by setting the
`delimiter` attribute. Alternatively, the `delimiterString` attribute allows you to use a
@@ -1271,8 +1595,8 @@ The default delimiter is a comma (`,`), but you can use another character by set
cannot be set simultaneously.
.Comments in CSV files
-NOTE: Any line beginning with a `#` symbol will be interpreted as a comment and will be
-ignored.
+NOTE: Any line beginning with a `+++#+++` symbol will be interpreted as a comment and will
+be ignored.
[source,java,indent=0]
----
@@ -1285,17 +1609,42 @@ include::{testDir}/example/ParameterizedTestDemo.java[tags=CsvFileSource_example
include::{testResourcesDir}/two-column.csv[]
----
-In contrast to the syntax used in `@CsvSource`, `@CsvFileSource` uses a double quote `"`
-as the quote character. See the `"United States of America"` value in the example above.
-An empty, quoted value `""` results in an empty `String` unless the `emptyValue` attribute
-is set; whereas, an entirely _empty_ value is interpreted as a `null` reference. By
-specifying one or more `nullValues`, a custom value can be interpreted as a `null`
-reference. An `ArgumentConversionException` is thrown if the target type of a `null`
-reference is a primitive type.
+The following listing shows the generated display names for the first two parameterized
+test methods above.
+
+----
+[1] country=Sweden, reference=1
+[2] country=Poland, reference=2
+[3] country=United States of America, reference=3
+[4] country=France, reference=700_000
+----
+
+The following listing shows the generated display names for the last parameterized test
+method above that uses CSV header names.
+
+----
+[1] COUNTRY = Sweden, REFERENCE = 1
+[2] COUNTRY = Poland, REFERENCE = 2
+[3] COUNTRY = United States of America, REFERENCE = 3
+[4] COUNTRY = France, REFERENCE = 700_000
+----
+
+In contrast to the default syntax used in `@CsvSource`, `@CsvFileSource` uses a double
+quote (`+++"+++`) as the quote character by default, but this can be changed via the
+`quoteCharacter` attribute. See the `"United States of America"` value in the example
+above. An empty, quoted value (`+++""+++`) results in an empty `String` unless the
+`emptyValue` attribute is set; whereas, an entirely _empty_ value is interpreted as a
+`null` reference. By specifying one or more `nullValues`, a custom value can be
+interpreted as a `null` reference. An `ArgumentConversionException` is thrown if the
+target type of a `null` reference is a primitive type.
NOTE: An _unquoted_ empty value will always be converted to a `null` reference regardless
of any custom values configured via the `nullValues` attribute.
+Except within a quoted string, leading and trailing whitespace in a CSV column is trimmed
+by default. This behavior can be changed by setting the
+`ignoreLeadingAndTrailingWhitespace` attribute to `true`.
+
[[writing-tests-parameterized-tests-sources-ArgumentsSource]]
===== @ArgumentsSource
@@ -1313,6 +1662,9 @@ include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsSource_examp
include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsProvider_example]
----
+If you wish to implement a custom `ArgumentsProvider` that also consumes an annotation
+(like built-in providers such as `{ValueArgumentsProvider}` or `{CsvArgumentsProvider}`),
+you have the possibility to extend the `{AnnotationBasedArgumentsProvider}` class.
[[writing-tests-parameterized-tests-argument-conversion]]
==== Argument Conversion
@@ -1353,7 +1705,7 @@ integral types: `byte`, `short`, `int`, `long`, and their boxed counterparts.
|===
| Target Type | Example
-| `boolean`/`Boolean` | `"true"` -> `true`
+| `boolean`/`Boolean` | `"true"` -> `true` _(only accepts values 'true' or 'false', case-insensitive)_
| `byte`/`Byte` | `"15"`, `"0xF"`, or `"017"` -> `(byte) 15`
| `char`/`Character` | `"o"` -> `'o'`
| `short`/`Short` | `"15"`, `"0xF"`, or `"017"` -> `(short) 15`
@@ -1369,7 +1721,7 @@ integral types: `byte`, `short`, `int`, `long`, and their boxed counterparts.
| `java.math.BigDecimal` | `"123.456e789"` -> `new BigDecimal("123.456e789")`
| `java.math.BigInteger` | `"1234567890123456789"` -> `new BigInteger("1234567890123456789")`
| `java.net.URI` | `"https://junit.org/"` -> `URI.create("https://junit.org/")`
-| `java.net.URL` | `"https://junit.org/"` -> `new URL("https://junit.org/")`
+| `java.net.URL` | `"https://junit.org/"` -> `URI.create("https://junit.org/").toURL()`
| `java.nio.charset.Charset` | `"UTF-8"` -> `Charset.forName("UTF-8")`
| `java.nio.file.Path` | `"/path/to/file"` -> `Paths.get("/path/to/file")`
| `java.time.Duration` | `"PT3S"` -> `Duration.ofSeconds(3)`
@@ -1460,6 +1812,10 @@ composed annotation `JavaTimeConversionPattern`.
include::{testDir}/example/ParameterizedTestDemo.java[tags=explicit_java_time_converter]
----
+If you wish to implement a custom `ArgumentConverter` that also consumes an annotation
+(like `JavaTimeArgumentConverter`), you have the possibility to extend the
+`{AnnotationBasedArgumentConverter}` class.
+
[[writing-tests-parameterized-tests-argument-aggregation]]
==== Argument Aggregation
@@ -1472,6 +1828,9 @@ this API, you can access the provided arguments through a single argument passed
test method. In addition, type conversion is supported as discussed in
<>.
+Besides, you can retrieve the current test invocation index with
+`ArgumentsAccessor.getInvocationIndex()`.
+
[source,java,indent=0]
----
include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsAccessor_example]
@@ -1548,7 +1907,8 @@ Display name of container ✔
└─ 3 ==> the rank of 'lemon, lime' is 3 ✔
....
-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.
+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.
@@ -1568,6 +1928,37 @@ 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.
+
+[source,java,indent=0]
+----
+include::{testDir}/example/ParameterizedTestDemo.java[tags=named_arguments]
+----
+
+....
+A parameterized test with named arguments ✔
+├─ 1: An important file ✔
+└─ 2: Another file ✔
+....
+
+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
+the `junit-platform.properties` file as demonstrated in the following example (see
+<> for other options).
+
+[source,properties,indent=0]
+----
+junit.jupiter.params.displayname.default = {index}
+----
+
+The display name for a parameterized test is determined according to the following
+precedence rules:
+
+1. `name` attribute in `@ParameterizedTest`, if present
+2. value of the `junit.jupiter.params.displayname.default` configuration parameter, if present
+3. `DEFAULT_DISPLAY_NAME` constant defined in `@ParameterizedTest`
[[writing-tests-parameterized-tests-lifecycle-interop]]
==== Lifecycle and Interoperability
@@ -1662,12 +2053,11 @@ and dynamic tests.
The first method returns an invalid return type. Since an invalid return type cannot be
detected at compile time, a `JUnitException` is thrown when it is detected at runtime.
-The next five methods are very simple examples that demonstrate the generation of a
-`Collection`, `Iterable`, `Iterator`, or `Stream` of `DynamicTest` instances. Most of
-these examples do not really exhibit dynamic behavior but merely demonstrate the
-supported return types in principle. However, `dynamicTestsFromStream()` and
-`dynamicTestsFromIntStream()` demonstrate how easy it is to generate dynamic tests for a
-given set of strings or a range of input numbers.
+The next six methods demonstrate the generation of a `Collection`, `Iterable`, `Iterator`,
+array, or `Stream` of `DynamicTest` instances. Most of these examples do not really
+exhibit dynamic behavior but merely demonstrate the supported return types in principle.
+However, `dynamicTestsFromStream()` and `dynamicTestsFromIntStream()` demonstrate how to
+generate dynamic tests for a given set of strings or a range of input numbers.
The next method is truly dynamic in nature. `generateRandomNumberOfTests()` implements an
`Iterator` that generates random numbers, a display name generator, and a test executor
@@ -1717,6 +2107,10 @@ implementations.
refer to the Javadoc for `DiscoverySelectors.selectMethod(String)` for the supported
formats for a FQMN.
+`ClassSource` ::
+ If the `URI` contains the `class` scheme and the fully qualified class name --
+ for example, `class:org.junit.Foo?line=42`.
+
`UriSource` ::
If none of the above `TestSource` implementations are applicable.
@@ -1724,10 +2118,6 @@ implementations.
[[writing-tests-declarative-timeouts]]
=== Timeouts
-.Declarative timeouts are an experimental feature
-WARNING: You're invited to give it a try and provide feedback to the JUnit team so they
-can improve and eventually <> this feature.
-
The `@Timeout` annotation allows one to declare that a test, test factory, test template,
or lifecycle method should fail if its execution time exceeds a given duration. The time
unit for the duration defaults to seconds but is configurable.
@@ -1739,12 +2129,6 @@ The following example shows how `@Timeout` is applied to lifecycle and test meth
include::{testDir}/example/TimeoutDemo.java[tags=user_guide]
----
-Contrary to the `assertTimeoutPreemptively()` assertion, the execution of the annotated
-method proceeds in the main thread of the test. If the timeout is exceeded, the main
-thread is interrupted from another thread. This is done to ensure interoperability with
-frameworks such as Spring that make use of mechanisms that are sensitive to the currently
-running thread — for example, `ThreadLocal` transaction management.
-
To apply the same timeout to all test methods within a test class and all of its `@Nested`
classes, you can declare the `@Timeout` annotation at the class level. It will then be
applied to all test, test factory, and test template methods within that class and its
@@ -1760,8 +2144,32 @@ within the specified duration but does not verify the execution time of each ind
If `@Timeout` is present on a `@TestTemplate` method — for example, a `@RepeatedTest` or
`@ParameterizedTest` — each invocation will have the given timeout applied to it.
+[[writing-tests-declarative-timeouts-thread-mode]]
+==== Thread mode
+
+The timeout can be applied using one of the following three thread modes: `SAME_THREAD`,
+`SEPARATE_THREAD`, or `INFERRED`.
+
+When `SAME_THREAD` is used, the execution of the annotated method proceeds in the main
+thread of the test. If the timeout is exceeded, the main thread is interrupted from
+another thread. This is done to ensure interoperability with frameworks such as Spring
+that make use of mechanisms that are sensitive to the currently running thread — for
+example, `ThreadLocal` transaction management.
+
+On the contrary when `SEPARATE_THREAD` is used, like the `assertTimeoutPreemptively()`
+assertion, the execution of the annotated method proceeds in a separate thread, this
+can lead to undesirable side effects, see <>.
+
+When `INFERRED` (default) thread mode is used, the thread mode is resolved via the
+`junit.jupiter.execution.timeout.thread.mode.default` configuration parameter. If the
+provided configuration parameter is invalid or not present then `SAME_THREAD` is used as
+fallback.
+
+[[writing-tests-declarative-timeouts-default-timeouts]]
+==== Default Timeouts
+
The following <> can be used to
-specify global timeouts for all methods of a certain category unless they or an enclosing
+specify default timeouts for all methods of a certain category unless they or an enclosing
test class is annotated with `@Timeout`:
`junit.jupiter.execution.timeout.default`::
@@ -1846,16 +2254,13 @@ JUnit Jupiter supports the `junit.jupiter.execution.timeout.mode` configuration
to configure when timeouts are applied. There are three modes: `enabled`, `disabled`,
and `disabled_on_debug`. The default mode is `enabled`.
A VM runtime is considered to run in debug mode when one of its input parameters starts
-with `-agentlib:jdwp`. This heuristic is queried by the `disabled_on_debug` mode.
+with `-agentlib:jdwp` or `-Xrunjdwp`.
+This heuristic is queried by the `disabled_on_debug` mode.
[[writing-tests-parallel-execution]]
=== Parallel Execution
-.Parallel test execution is an experimental feature
-WARNING: You're invited to give it a try and provide feedback to the JUnit team so they
-can improve and eventually <> this feature.
-
By default, JUnit Jupiter tests are run sequentially in a single thread. Running tests in
parallel -- for example, to speed up execution -- is available as an opt-in feature since
version 5.3. To enable parallel execution, set the
@@ -1891,11 +2296,17 @@ 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 `{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}` (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.
+
+When parallel execution is enabled and a default `{ClassOrderer}` is registered (see
+<> for details), top-level test classes will
+initially be sorted accordingly and scheduled in that order. However, they are not
+guaranteed to be started in exactly that order since the threads they are executed on are
+not controlled directly by JUnit.
All nodes of the test tree that are configured with the `CONCURRENT` execution mode will
be executed fully in parallel according to the provided
@@ -1985,10 +2396,14 @@ configuration parameter to one of the following options.
Computes the desired parallelism based on the number of available processors/cores
multiplied by the `junit.jupiter.execution.parallel.config.dynamic.factor`
configuration parameter (defaults to `1`).
+ The optional `junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor`
+ configuration parameter can be used to limit the maximum number of threads.
`fixed`::
Uses the mandatory `junit.jupiter.execution.parallel.config.fixed.parallelism`
configuration parameter as the desired parallelism.
+ The optional `junit.jupiter.execution.parallel.config.fixed.max-pool-size`
+ configuration parameter can be used to limit the maximum number of threads.
`custom`::
Allows you to specify a custom `{ParallelExecutionConfigurationStrategy}`
@@ -1999,13 +2414,102 @@ If no configuration strategy is set, JUnit Jupiter uses the `dynamic` configurat
strategy with a factor of `1`. Consequently, the desired parallelism will be equal to the
number of available processors/cores.
-.Parallelism does not imply maximum number of concurrent threads
-NOTE: JUnit Jupiter does not guarantee that the number of concurrently executing tests
-will not exceed the configured parallelism. For example, when using one of the
-synchronization mechanisms described in the next section, the `ForkJoinPool` that is used
-behind the scenes may spawn additional threads to ensure execution continues with
-sufficient parallelism. Thus, if you require such guarantees in a test class, please use
-your own means of controlling concurrency.
+.Parallelism alone does not imply maximum number of concurrent threads
+NOTE: By default JUnit Jupiter does not guarantee that the number of concurrently
+executing tests will not exceed the configured parallelism. For example, when using one
+of the synchronization mechanisms described in the next section, the `ForkJoinPool` that
+is used behind the scenes may spawn additional threads to ensure execution continues with
+sufficient parallelism.
+If you require such guarantees, with Java 9+, it is possible to limit the maximum number
+of concurrent threads by controlling the maximum pool size of the `dynamic`, `fixed` and
+`custom` strategies.
+
+[[writing-tests-parallel-execution-config-properties]]
+===== Relevant properties
+
+The following table lists relevant properties for configuring parallel execution. See
+<> for details on how to set such properties.
+
+[cols="d,d,a,d"]
+|===
+|Property |Description |Supported Values |Default Value
+
+| ```junit.jupiter.execution.parallel.enabled```
+| Enable parallel test execution
+|
+ * `true`
+ * `false`
+| ```false```
+
+| ```junit.jupiter.execution.parallel.mode.default```
+| Default execution mode of nodes in the test tree
+|
+ * `concurrent`
+ * `same_thread`
+| ```same_thread```
+
+| ```junit.jupiter.execution.parallel.mode.classes.default```
+| Default execution mode of top-level classes
+|
+ * `concurrent`
+ * `same_thread`
+| ```same_thread```
+
+| ```junit.jupiter.execution.parallel.config.strategy```
+| Execution strategy for desired parallelism and maximum pool size
+|
+ * `dynamic`
+ * `fixed`
+ * `custom`
+| ```dynamic```
+
+| ```junit.jupiter.execution.parallel.config.dynamic.factor```
+| Factor to be multiplied by the number of available processors/cores to determine the
+ desired parallelism for the ```dynamic``` configuration strategy
+| a positive decimal number
+| ```1.0```
+
+| ```junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor```
+| Factor to be multiplied by the number of available processors/cores and the value of
+ `junit.jupiter.execution.parallel.config.dynamic.factor` to determine the desired
+ parallelism for the ```dynamic``` configuration strategy
+| a positive decimal number, must be greater than or equal to `1.0`
+| 256 + the value of `junit.jupiter.execution.parallel.config.dynamic.factor` multiplied
+ by the number of available processors/cores
+
+| ```junit.jupiter.execution.parallel.config.dynamic.saturate```
+| Disable saturation of the underlying fork-join pool for the ```dynamic``` configuration
+strategy
+|
+* `true`
+* `false`
+| ```true```
+
+| ```junit.jupiter.execution.parallel.config.fixed.parallelism```
+| Desired parallelism for the ```fixed``` configuration strategy
+| a positive integer
+| no default value
+
+| ```junit.jupiter.execution.parallel.config.fixed.max-pool-size```
+| Desired maximum pool size of the underlying fork-join pool for the ```fixed```
+ configuration strategy
+| a positive integer, must be greater than or equal to `junit.jupiter.execution.parallel.config.fixed.parallelism`
+| 256 + the value of `junit.jupiter.execution.parallel.config.fixed.parallelism`
+
+| ```junit.jupiter.execution.parallel.config.fixed.saturate```
+| Disable saturation of the underlying fork-join pool for the ```fixed``` configuration
+ strategy
+|
+ * `true`
+ * `false`
+| ```true```
+
+| ```junit.jupiter.execution.parallel.config.custom.class```
+| Fully qualified class name of the _ParallelExecutionConfigurationStrategy_ to be
+ used for the ```custom``` configuration strategy
+| for example, _org.example.CustomStrategy_
+| no default value
+|===
[[writing-tests-parallel-execution-synchronization]]
==== Synchronization
@@ -2058,13 +2562,9 @@ another dependency.
[[writing-tests-built-in-extensions-TempDirectory]]
==== The TempDirectory Extension
-.`@TempDir` is an experimental feature
-WARNING: You're invited to give it a try and provide feedback to the JUnit team so they
-can improve and eventually <> this feature.
-
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
-default. To use it, annotate a non-private field of type `java.nio.file.Path` or
+default. To use it, annotate a non-final, unassigned field of type `java.nio.file.Path` or
`java.io.File` with `{TempDir}` or add a parameter of type `java.nio.file.Path` or
`java.io.File` annotated with `@TempDir` to a lifecycle method or test method.
@@ -2078,16 +2578,117 @@ its content.
include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_parameter_injection]
----
-WARNING: `@TempDir` is not supported on constructor parameters. If you wish to retain a
-single reference to a temp directory across lifecycle methods and the current test method,
-please use field injection, by annotating a non-private instance field with `@TempDir`.
+You can inject multiple temporary directories by specifying multiple annotated parameters.
+
+[source,java,indent=0]
+.A test method that requires multiple temporary directories
+----
+include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_multiple_directories]
+----
+
+WARNING: To revert to the old behavior of using a single temporary directory for the
+entire test class or method (depending on which level the annotation is used), you can set
+the `junit.jupiter.tempdir.scope` configuration parameter to `per_context`. However,
+please note that this option is deprecated and will be removed in a future release.
+
+`@TempDir` is not supported on constructor parameters. If you wish to retain a single
+reference to a temp directory across lifecycle methods and the current test method, please
+use field injection by annotating an instance field with `@TempDir`.
The following example stores a _shared_ temporary directory in a `static` field. This
allows the same `sharedTempDir` to be used in all lifecycle methods and test methods of
-the test class.
+the test class. For better isolation, you should use an instance field so that each test
+method uses a separate directory.
[source,java,indent=0]
.A test class that shares a temporary directory across test methods
----
include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_field_injection]
----
+
+The `@TempDir` annotation has an optional `cleanup` attribute that can be set to either
+`NEVER`, `ON_SUCCESS`, or `ALWAYS`. If the cleanup mode is set to `NEVER`, temporary
+directories are not deleted after a test completes. If it is set to `ON_SUCCESS`,
+temporary directories are deleted only after a test completed successfully.
+
+The default cleanup mode is `ALWAYS`. You can use the
+`junit.jupiter.tempdir.cleanup.mode.default`
+<> to override this default.
+
+[source,java,indent=0]
+.A test class with a temporary directory that doesn't get cleaned up
+----
+include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_cleanup_mode]
+----
+
+`@TempDir` supports the programmatic creation of temporary directories via the optional
+`factory` attribute. This is typically used to gain control over the temporary directory
+creation, like defining the parent directory or the file system that should be used.
+
+Factories can be created by implementing `TempDirFactory`. Implementations must provide a
+no-args constructor and should not make any assumptions regarding when and how many times
+they are instantiated, but they can assume that their `createTempDirectory(...)` and
+`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 following example defines a factory that uses the test name as the directory name
+prefix instead of the `junit` constant value.
+
+[source,java,indent=0]
+.A test class with a temporary directory having the test name as the directory name prefix
+----
+include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_factory_name_prefix]
+----
+
+It's also possible to use an in-memory file system like `{Jimfs}` for the creation of the
+temporary directory. The following example demonstrates how to achieve that.
+
+[source,java,indent=0]
+.A test class with a temporary directory created with the Jimfs in-memory file system
+----
+include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_factory_jimfs]
+----
+
+`@TempDir` can also be used as a <> to
+reduce repetition. The following code listing shows how to create a custom `@JimfsTempDir`
+annotation that can be used as a drop-in replacement for
+`@TempDir(factory = JimfsTempDirFactory.class)`.
+
+[source,java,indent=0]
+.A custom annotation meta-annotated with `@TempDir`
+----
+include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_composed_annotation]
+----
+
+The following example demonstrates how to use the custom `@JimfsTempDir` annotation.
+
+[source,java,indent=0]
+.A test class using the custom annotation
+----
+include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_composed_annotation_usage]
+----
+
+Meta-annotations or additional annotations on the field or parameter the `TempDir`
+annotation is declared on might expose additional attributes to configure the factory.
+Such annotations and related attributes can be accessed via the `AnnotatedElementContext`
+parameter of `createTempDirectory`.
+
+You can use the `junit.jupiter.tempdir.factory.default`
+<> to specify the fully qualified
+class name of the `TempDirFactory` you would like to use by default. Just like for
+factories configured via the `factory` attribute of the `@TempDir` annotation,
+the supplied class has to implement the `TempDirFactory` interface. The default factory
+will be used for all `@TempDir` annotations unless the `factory` attribute of the
+annotation specifies a different factory.
+
+In summary, the factory for a temporary directory is determined according to the
+following precedence rules:
+
+1. The `factory` attribute of the `@TempDir` annotation, if present
+2. The default `TempDirFactory` configured via the configuration
+parameter, if present
+3. Otherwise, `org.junit.jupiter.api.io.TempDirFactory$Standard` will be used.
diff --git a/documentation/src/javadoc/junit-stylesheet.css b/documentation/src/javadoc/junit-stylesheet.css
index 4ad313bfeb8c..19fe4f0cee0a 100644
--- a/documentation/src/javadoc/junit-stylesheet.css
+++ b/documentation/src/javadoc/junit-stylesheet.css
@@ -36,96 +36,120 @@ pre, code, tt, dt code, table tr td dt code {
background-color:#25a162;
}
-.topNav {
+.top-nav {
background-color:#25a162;
}
-.bottomNav {
+.bottom-nav {
background-color:#25a162;
}
-.subNav {
+.sub-nav {
background-color:#f5f5f5;
}
-.topNav a:hover, .bottomNav a:hover {
+.top-nav a:hover, .bottom-nav a:hover {
text-decoration:underline;
color:inherit;
}
-.navBarCell1Rev {
+.nav-bar-cell1-rev {
background-color:#fff;
color:#dc524a;
border-radius: 6px;
}
-.indexNav {
+.index-nav {
background-color:#eee;
}
-div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 {
- background-color:#ddd;
- border:1px solid #ddd;
+body.class-declaration-page .summary h2,
+body.class-declaration-page .details h2,
+body.class-use-page h2,
+body.module-declaration-page .block-list h2 {
+ font-style: italic;
+ padding:0;
+ margin:15px 0;
}
-
-ul.blockList ul.blockList ul.blockList li.blockList h3 {
+body.class-declaration-page .summary h3,
+body.class-declaration-page .details h3,
+body.class-declaration-page .summary .inherited-list h2,
+div.details ul.block-list ul.block-list ul.block-list li.block-list h4,
+ul.block-list ul.block-list ul.block-list li.block-list h3 {
background-color:#ddd;
border:1px solid #ddd;
}
-.constantsSummary caption a:link, .constantsSummary caption a:visited,
-.useSummary caption a:link, .useSummary caption a:visited {
+.constants-summary caption a:link, .constants-summary caption a:visited,
+.use-summary caption a:link, .use-summary caption a:visited {
color:#fff;
}
-.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span,
-.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span,
-.requiresSummary caption span, .packagesSummary caption span, .providesSummary caption span,
-.usesSummary caption span,
-.memberSummary caption span.activeTableTab span, .packagesSummary caption span.activeTableTab span,
-.overviewSummary caption span.activeTableTab span, .typeSummary caption span.activeTableTab span,
-.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd,
-.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd,
-.requiresSummary .tabEnd, .packagesSummary .tabEnd, .providesSummary .tabEnd, .usesSummary .tabEnd,
-.memberSummary .activeTableTab .tabEnd, .packagesSummary .activeTableTab .tabEnd,
-.overviewSummary .activeTableTab .tabEnd, .typeSummary .activeTableTab .tabEnd {
+.overview-summary caption span, .member-summary caption span, .type-summary caption span,
+.use-summary caption span, .constants-summary caption span, .deprecated-summary caption span,
+.requires-summary caption span, .packages-summary caption span, .provides-summary caption span,
+.uses-summary caption span,
+.member-summary caption span.active-table-tab span, .packages-summary caption span.active-table-tab span,
+.overview-summary caption span.active-table-tab span, .type-summary caption span.active-table-tab span,
+div.table-tabs > button.active-table-tab
+{
background-color:#dc524a;
color: #fff;
}
-.memberSummary caption span.tableTab span, .packagesSummary caption span.tableTab span,
-.overviewSummary caption span.tableTab span, .typeSummary caption span.tableTab span,
-.memberSummary .tableTab .tabEnd, .packagesSummary .tableTab .tabEnd,
-.overviewSummary .tableTab .tabEnd, .typeSummary .tableTab .tabEnd,
-.ui-autocomplete-category {
+.ui-state-active,
+.ui-widget-content .ui-state-active,
+.ui-widget-header .ui-state-active,
+a.ui-button:active,
+.ui-button:active,
+.ui-button.ui-state-active:hover {
+ /* Overrides the color of selection used in jQuery UI */
+ background: #dc524a !important;
+ color: #fff !important;
+}
+
+main a[href*="://"]::after,
+main a[href*="://"]:hover::after,
+main a[href*="://"]:focus::after {
+ background-image:url('data:image/svg+xml; utf8, \
+ ');
+}
+
+.member-summary caption span.table-tab span, .packages-summary caption span.table-tab span,
+.overview-summary caption span.table-tab span, .type-summary caption span.table-tab span,
+.ui-autocomplete-category,
+div.table-tabs > button.table-tab {
background-color:#aaa;
color: #fff;
}
-th.colFirst, th.colSecond, th.colLast, th.colConstructorName, th.colDeprecatedItemName, .constantsSummary th,
-.packagesSummary th {
+th.col-first, th.col-second, th.col-last, th.col-constructor-name, th.col-deprecated-item-name, .constants-summary th,
+.packages-summary th {
background:#eee;
}
-.tableSubHeadingColor {
+.table-sub-heading-color {
background-color:#eee;
}
-.altColor, .altColor th {
+.alt-color, .alt-color th {
background-color:#fff;
}
-.rowColor, .rowColor th {
+.row-color, .row-color th {
background-color:#eee;
}
.block {
- margin:0px 10px 5px 0px;
+ margin:0 10px 5px 0;
}
-th.colFirst, th.colSecond, th.colLast, th.colConstructorName, th.colDeprecatedItemName, .constantsSummary th,
-.packagesSummary th, .overviewSummary td, .memberSummary td, .typeSummary td,
-.useSummary td, .constantsSummary td, .deprecatedSummary td,
-.requiresSummary td, .packagesSummary td, .providesSummary td, .usesSummary td {
+th.col-first, th.col-second, th.col-last, th.col-constructor-name, th.col-deprecated-item-name, .constants-summary th,
+.packages-summary th, .overview-summary td, .member-summary td, .type-summary td,
+.use-summary td, .constants-summary td, .deprecated-summary td,
+.requires-summary td, .packages-summary td, .provides-summary td, .uses-summary td {
padding-left:7px;
}
diff --git a/documentation/src/main/java/example/domain/Person.java b/documentation/src/main/java/example/domain/Person.java
index f34c00d1b1f6..b628febd36cf 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/main/java/example/registration/WebClient.java b/documentation/src/main/java/example/registration/WebClient.java
index 26088b910f47..b907c2c58e95 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/main/java/example/registration/WebResponse.java b/documentation/src/main/java/example/registration/WebResponse.java
index 0cd2fb438f20..598239f44c24 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/main/java/example/registration/WebServerExtension.java b/documentation/src/main/java/example/registration/WebServerExtension.java
index ed9a0c5b7e80..80fefe787b89 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/main/java/example/util/Calculator.java b/documentation/src/main/java/example/util/Calculator.java
index 8f6cc877d020..98291f6a78fe 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/main/java/example/util/ListWriter.java b/documentation/src/main/java/example/util/ListWriter.java
index b2f0e26177d0..88fb73137ff6 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/main/java/example/util/StringUtils.java b/documentation/src/main/java/example/util/StringUtils.java
index e489cd7be79c..b622aa3efb90 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/AssertionsDemo.java b/documentation/src/test/java/example/AssertionsDemo.java
index 22311e974840..476434da44f6 100644
--- a/documentation/src/test/java/example/AssertionsDemo.java
+++ b/documentation/src/test/java/example/AssertionsDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -27,6 +27,7 @@
import example.domain.Person;
import example.util.Calculator;
+import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
class AssertionsDemo {
@@ -91,6 +92,9 @@ void exceptionTesting() {
assertEquals("/ by zero", exception.getMessage());
}
+ // end::user_guide[]
+ @Tag("timeout")
+ // tag::user_guide[]
@Test
void timeoutNotExceeded() {
// The following assertion succeeds.
@@ -99,6 +103,9 @@ void timeoutNotExceeded() {
});
}
+ // end::user_guide[]
+ @Tag("timeout")
+ // tag::user_guide[]
@Test
void timeoutNotExceededWithResult() {
// The following assertion succeeds, and returns the supplied object.
@@ -108,6 +115,9 @@ void timeoutNotExceededWithResult() {
assertEquals("a result", actualResult);
}
+ // end::user_guide[]
+ @Tag("timeout")
+ // tag::user_guide[]
@Test
void timeoutNotExceededWithMethod() {
// The following assertion invokes a method reference and returns an object.
@@ -116,6 +126,7 @@ void timeoutNotExceededWithMethod() {
}
// end::user_guide[]
+ @Tag("timeout")
@extensions.ExpectToFail
// tag::user_guide[]
@Test
@@ -129,6 +140,7 @@ void timeoutExceeded() {
}
// end::user_guide[]
+ @Tag("timeout")
@extensions.ExpectToFail
// tag::user_guide[]
@Test
diff --git a/documentation/src/test/java/example/AssumptionsDemo.java b/documentation/src/test/java/example/AssumptionsDemo.java
index 75914749a7bf..41438feabb2f 100644
--- a/documentation/src/test/java/example/AssumptionsDemo.java
+++ b/documentation/src/test/java/example/AssumptionsDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/ConditionalTestExecutionDemo.java b/documentation/src/test/java/example/ConditionalTestExecutionDemo.java
index fcd7a2dcc4be..146443c260c1 100644
--- a/documentation/src/test/java/example/ConditionalTestExecutionDemo.java
+++ b/documentation/src/test/java/example/ConditionalTestExecutionDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -28,12 +28,14 @@
import org.junit.jupiter.api.condition.DisabledIf;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.junit.jupiter.api.condition.DisabledInNativeImage;
import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+import org.junit.jupiter.api.condition.EnabledInNativeImage;
import org.junit.jupiter.api.condition.EnabledOnJre;
import org.junit.jupiter.api.condition.EnabledOnOs;
@@ -71,6 +73,32 @@ void notOnWindows() {
}
// end::user_guide_os[]
+ // tag::user_guide_architecture[]
+ @Test
+ @EnabledOnOs(architectures = "aarch64")
+ void onAarch64() {
+ // ...
+ }
+
+ @Test
+ @DisabledOnOs(architectures = "x86_64")
+ void notOnX86_64() {
+ // ...
+ }
+
+ @Test
+ @EnabledOnOs(value = MAC, architectures = "aarch64")
+ void onNewMacs() {
+ // ...
+ }
+
+ @Test
+ @DisabledOnOs(value = MAC, architectures = "aarch64")
+ void notOnNewMacs() {
+ // ...
+ }
+ // end::user_guide_architecture[]
+
// tag::user_guide_jre[]
@Test
@EnabledOnJre(JAVA_8)
@@ -127,6 +155,20 @@ void notFromJava8to11() {
}
// end::user_guide_jre[]
+ // tag::user_guide_native[]
+ @Test
+ @EnabledInNativeImage
+ void onlyWithinNativeImage() {
+ // ...
+ }
+
+ @Test
+ @DisabledInNativeImage
+ void neverWithinNativeImage() {
+ // ...
+ }
+ // end::user_guide_native[]
+
// tag::user_guide_system_property[]
@Test
@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")
diff --git a/documentation/src/test/java/example/CustomLauncherInterceptor.java b/documentation/src/test/java/example/CustomLauncherInterceptor.java
new file mode 100644
index 000000000000..149cf7e45440
--- /dev/null
+++ b/documentation/src/test/java/example/CustomLauncherInterceptor.java
@@ -0,0 +1,55 @@
+/*
+ * 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 example;
+
+// tag::user_guide[]
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import org.junit.platform.launcher.LauncherInterceptor;
+
+public class CustomLauncherInterceptor implements LauncherInterceptor {
+
+ private final URLClassLoader customClassLoader;
+
+ public CustomLauncherInterceptor() throws Exception {
+ ClassLoader parent = Thread.currentThread().getContextClassLoader();
+ customClassLoader = new URLClassLoader(new URL[] { URI.create("some.jar").toURL() }, parent);
+ }
+
+ @Override
+ public T intercept(Invocation invocation) {
+ Thread currentThread = Thread.currentThread();
+ ClassLoader originalClassLoader = currentThread.getContextClassLoader();
+ currentThread.setContextClassLoader(customClassLoader);
+ try {
+ return invocation.proceed();
+ }
+ finally {
+ currentThread.setContextClassLoader(originalClassLoader);
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ customClassLoader.close();
+ }
+ catch (IOException e) {
+ throw new UncheckedIOException("Failed to close custom class loader", e);
+ }
+ }
+}
+// end::user_guide[]
diff --git a/documentation/src/test/java/example/CustomTestEngine.java b/documentation/src/test/java/example/CustomTestEngine.java
index b12a8e862c49..a1d0eb31bb47 100644
--- a/documentation/src/test/java/example/CustomTestEngine.java
+++ b/documentation/src/test/java/example/CustomTestEngine.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/DisabledClassDemo.java b/documentation/src/test/java/example/DisabledClassDemo.java
index 761547508255..a2453a2aac27 100644
--- a/documentation/src/test/java/example/DisabledClassDemo.java
+++ b/documentation/src/test/java/example/DisabledClassDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/DisabledTestsDemo.java b/documentation/src/test/java/example/DisabledTestsDemo.java
index 93e11e251b51..e1a7f6c0ad68 100644
--- a/documentation/src/test/java/example/DisabledTestsDemo.java
+++ b/documentation/src/test/java/example/DisabledTestsDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/DisplayNameDemo.java b/documentation/src/test/java/example/DisplayNameDemo.java
index b30092e8d7d5..c9ee6fed5ddc 100644
--- a/documentation/src/test/java/example/DisplayNameDemo.java
+++ b/documentation/src/test/java/example/DisplayNameDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/DisplayNameGeneratorDemo.java b/documentation/src/test/java/example/DisplayNameGeneratorDemo.java
index a59f4fd3f157..0ccfb762e24b 100644
--- a/documentation/src/test/java/example/DisplayNameGeneratorDemo.java
+++ b/documentation/src/test/java/example/DisplayNameGeneratorDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -15,6 +15,7 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
+import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;
import org.junit.jupiter.api.IndicativeSentencesGeneration;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -40,7 +41,7 @@ void if_it_is_negative(int year) {
}
@Nested
- @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class)
+ @IndicativeSentencesGeneration(separator = " -> ", generator = ReplaceUnderscores.class)
class A_year_is_a_leap_year {
@Test
diff --git a/documentation/src/test/java/example/DocumentationTestSuite.java b/documentation/src/test/java/example/DocumentationTestSuite.java
index 1b2f9d575883..b3bc6dd0029b 100644
--- a/documentation/src/test/java/example/DocumentationTestSuite.java
+++ b/documentation/src/test/java/example/DocumentationTestSuite.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -10,14 +10,13 @@
package example;
-import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.ExcludeTags;
import org.junit.platform.suite.api.IncludeClassNamePatterns;
import org.junit.platform.suite.api.SelectPackages;
-import org.junit.runner.RunWith;
+import org.junit.platform.suite.api.Suite;
/**
- * Logging Configuration
+ * Logging Configuration
*
* In order for our log4j2 configuration to be used in an IDE, you must
* set the following system property before running any tests — for
@@ -29,9 +28,9 @@
*
* @since 5.0
*/
-@RunWith(JUnitPlatform.class)
+@Suite
@SelectPackages("example")
@IncludeClassNamePatterns(".+(Tests|Demo)$")
@ExcludeTags("exclude")
-public class DocumentationTestSuite {
+class DocumentationTestSuite {
}
diff --git a/documentation/src/test/java/example/DynamicTestsDemo.java b/documentation/src/test/java/example/DynamicTestsDemo.java
index 8aa063a57dbe..8a8d9bef9ac0 100644
--- a/documentation/src/test/java/example/DynamicTestsDemo.java
+++ b/documentation/src/test/java/example/DynamicTestsDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -19,6 +19,7 @@
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;
@@ -33,6 +34,7 @@
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;
@@ -151,6 +153,21 @@ 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")
diff --git a/documentation/src/test/java/example/ExampleTestCase.java b/documentation/src/test/java/example/ExampleTestCase.java
index e08950b1df9b..2a898b33e08b 100644
--- a/documentation/src/test/java/example/ExampleTestCase.java
+++ b/documentation/src/test/java/example/ExampleTestCase.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/ExternalCustomConditionDemo.java b/documentation/src/test/java/example/ExternalCustomConditionDemo.java
new file mode 100644
index 000000000000..024b07d27854
--- /dev/null
+++ b/documentation/src/test/java/example/ExternalCustomConditionDemo.java
@@ -0,0 +1,34 @@
+/*
+ * 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 example;
+
+// tag::user_guide_external_custom_condition[]
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIf;
+
+class ExternalCustomConditionDemo {
+
+ @Test
+ @EnabledIf("example.ExternalCondition#customCondition")
+ void enabled() {
+ // ...
+ }
+
+}
+
+class ExternalCondition {
+
+ static boolean customCondition() {
+ return true;
+ }
+
+}
+// end::user_guide_external_custom_condition[]
diff --git a/documentation/src/test/java/example/ExternalMethodSourceDemo.java b/documentation/src/test/java/example/ExternalMethodSourceDemo.java
index cda486d775de..1d8299367e9b 100644
--- a/documentation/src/test/java/example/ExternalMethodSourceDemo.java
+++ b/documentation/src/test/java/example/ExternalMethodSourceDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/Fast.java b/documentation/src/test/java/example/Fast.java
index 911fdb2df0d7..1e7c78e6f6b6 100644
--- a/documentation/src/test/java/example/Fast.java
+++ b/documentation/src/test/java/example/Fast.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/FastTest.java b/documentation/src/test/java/example/FastTest.java
index f85066f7db29..af31b49d5699 100644
--- a/documentation/src/test/java/example/FastTest.java
+++ b/documentation/src/test/java/example/FastTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/HamcrestAssertionsDemo.java b/documentation/src/test/java/example/HamcrestAssertionsDemo.java
index 5f9cd857cb81..8fa219829ce3 100644
--- a/documentation/src/test/java/example/HamcrestAssertionsDemo.java
+++ b/documentation/src/test/java/example/HamcrestAssertionsDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/IgnoredTestsDemo.java b/documentation/src/test/java/example/IgnoredTestsDemo.java
index 6bddc9134c9f..96fca01c8ade 100644
--- a/documentation/src/test/java/example/IgnoredTestsDemo.java
+++ b/documentation/src/test/java/example/IgnoredTestsDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/JUnit4Tests.java b/documentation/src/test/java/example/JUnit4Tests.java
index 2caac8b99351..d229e639fa2d 100644
--- a/documentation/src/test/java/example/JUnit4Tests.java
+++ b/documentation/src/test/java/example/JUnit4Tests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/JUnitPlatformClassDemo.java b/documentation/src/test/java/example/JUnitPlatformClassDemo.java
index 09acf1b0e24d..0bf0aeb06220 100644
--- a/documentation/src/test/java/example/JUnitPlatformClassDemo.java
+++ b/documentation/src/test/java/example/JUnitPlatformClassDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -14,10 +14,12 @@
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
-import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
-@RunWith(JUnitPlatform.class)
+//end::user_guide[]
+@SuppressWarnings("deprecation")
+//tag::user_guide[]
+@RunWith(org.junit.platform.runner.JUnitPlatform.class)
public class JUnitPlatformClassDemo {
@Test
diff --git a/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java b/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java
index a5c698558967..b563204fff2b 100644
--- a/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java
+++ b/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -11,15 +11,15 @@
package example;
//tag::user_guide[]
-import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectPackages;
import org.junit.platform.suite.api.SuiteDisplayName;
import org.junit.runner.RunWith;
-@RunWith(JUnitPlatform.class)
+@RunWith(org.junit.platform.runner.JUnitPlatform.class)
@SuiteDisplayName("JUnit Platform Suite Demo")
@SelectPackages("example")
//end::user_guide[]
+@SuppressWarnings("deprecation")
@org.junit.platform.suite.api.ExcludeTags("exclude")
//tag::user_guide[]
public class JUnitPlatformSuiteDemo {
diff --git a/documentation/src/test/java/example/MethodSourceParameterResolutionDemo.java b/documentation/src/test/java/example/MethodSourceParameterResolutionDemo.java
new file mode 100644
index 000000000000..45b35dbcc964
--- /dev/null
+++ b/documentation/src/test/java/example/MethodSourceParameterResolutionDemo.java
@@ -0,0 +1,66 @@
+/*
+ * 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 example;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolver;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class MethodSourceParameterResolutionDemo {
+
+ // @formatter:off
+ // tag::parameter_resolution_MethodSource_example[]
+ @RegisterExtension
+ static final IntegerResolver integerResolver = new IntegerResolver();
+
+ @ParameterizedTest
+ @MethodSource("factoryMethodWithArguments")
+ void testWithFactoryMethodWithArguments(String argument) {
+ assertTrue(argument.startsWith("2"));
+ }
+
+ static Stream factoryMethodWithArguments(int quantity) {
+ return Stream.of(
+ arguments(quantity + " apples"),
+ arguments(quantity + " lemons")
+ );
+ }
+
+ static class IntegerResolver 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::parameter_resolution_MethodSource_example[]
+ // @formatter:on
+
+}
diff --git a/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java b/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java
index 6eea9d7210d6..77632abc3be5 100644
--- a/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java
+++ b/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/OrderedNestedTestClassesDemo.java b/documentation/src/test/java/example/OrderedNestedTestClassesDemo.java
new file mode 100644
index 000000000000..b5e987daca65
--- /dev/null
+++ b/documentation/src/test/java/example/OrderedNestedTestClassesDemo.java
@@ -0,0 +1,41 @@
+/*
+ * 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 example;
+
+// tag::user_guide[]
+import org.junit.jupiter.api.ClassOrderer;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestClassOrder;
+
+@TestClassOrder(ClassOrderer.OrderAnnotation.class)
+class OrderedNestedTestClassesDemo {
+
+ @Nested
+ @Order(1)
+ class PrimaryTests {
+
+ @Test
+ void test1() {
+ }
+ }
+
+ @Nested
+ @Order(2)
+ class SecondaryTests {
+
+ @Test
+ void test2() {
+ }
+ }
+}
+//end::user_guide[]
diff --git a/documentation/src/test/java/example/OrderedTestsDemo.java b/documentation/src/test/java/example/OrderedTestsDemo.java
index d54ff5b3fc82..2ee01d3335cf 100644
--- a/documentation/src/test/java/example/OrderedTestsDemo.java
+++ b/documentation/src/test/java/example/OrderedTestsDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/ParameterizedTestDemo.java b/documentation/src/test/java/example/ParameterizedTestDemo.java
index b0f2b76fde99..3afd887265e2 100644
--- a/documentation/src/test/java/example/ParameterizedTestDemo.java
+++ b/documentation/src/test/java/example/ParameterizedTestDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -15,10 +15,12 @@
import static org.junit.jupiter.api.Assertions.assertNotEquals;
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.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;
+import java.io.File;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -210,7 +212,8 @@ static Stream stringIntAndListProvider() {
@CsvSource({
"apple, 1",
"banana, 2",
- "'lemon, lime', 0xF1"
+ "'lemon, lime', 0xF1",
+ "strawberry, 700_000"
})
void testWithCsvSource(String fruit, int rank) {
assertNotNull(fruit);
@@ -233,6 +236,13 @@ void testWithCsvFileSourceFromFile(String country, int reference) {
assertNotNull(country);
assertNotEquals(0, reference);
}
+
+ @ParameterizedTest(name = "[{index}] {arguments}")
+ @CsvFileSource(resources = "/two-column.csv", useHeadersInDisplayName = true)
+ void testWithCsvFileSourceAndHeaders(String country, int reference) {
+ assertNotNull(country);
+ assertNotEquals(0, reference);
+ }
// end::CsvFileSource_example[]
// tag::ArgumentsSource_example[]
@@ -344,7 +354,7 @@ protected ToLengthArgumentConverter() {
@Override
protected Integer convert(String source) {
- return source.length();
+ return (source != null ? source.length() : 0);
}
}
@@ -440,4 +450,21 @@ void testWithCustomAggregatorAnnotation(@CsvToPerson Person person) {
void testWithCustomDisplayNames(String fruit, int rank) {
}
// end::custom_display_names[]
+
+ // @formatter:off
+ // tag::named_arguments[]
+ @DisplayName("A parameterized test with named arguments")
+ @ParameterizedTest(name = "{index}: {0}")
+ @MethodSource("namedArguments")
+ void testWithNamedArguments(File file) {
+ }
+
+ static Stream namedArguments() {
+ return Stream.of(
+ arguments(named("An important file", new File("path1"))),
+ arguments(named("Another file", new File("path2")))
+ );
+ }
+ // end::named_arguments[]
+ // @formatter:on
}
diff --git a/documentation/src/test/java/example/PollingTimeoutDemo.java b/documentation/src/test/java/example/PollingTimeoutDemo.java
index 2242b6b86c84..a10b7cec8edd 100644
--- a/documentation/src/test/java/example/PollingTimeoutDemo.java
+++ b/documentation/src/test/java/example/PollingTimeoutDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/RepeatedTestsDemo.java b/documentation/src/test/java/example/RepeatedTestsDemo.java
index 98d552952f4d..49bf59274aa7 100644
--- a/documentation/src/test/java/example/RepeatedTestsDemo.java
+++ b/documentation/src/test/java/example/RepeatedTestsDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -12,6 +12,7 @@
// tag::user_guide[]
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
import java.util.logging.Logger;
@@ -21,6 +22,10 @@
import org.junit.jupiter.api.RepetitionInfo;
import org.junit.jupiter.api.TestInfo;
+// end::user_guide[]
+// 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 {
private Logger logger = // ...
@@ -47,6 +52,18 @@ void repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) {
assertEquals(5, repetitionInfo.getTotalRepetitions());
}
+ // end::user_guide[]
+ // 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)
+ void repeatedTestWithFailureThreshold(RepetitionInfo repetitionInfo) {
+ // Simulate unexpected failure every second repetition
+ if (repetitionInfo.getCurrentRepetition() % 2 == 0) {
+ fail("Boom!");
+ }
+ }
+
@RepeatedTest(value = 1, name = "{displayName} {currentRepetition}/{totalRepetitions}")
@DisplayName("Repeat!")
void customDisplayName(TestInfo testInfo) {
diff --git a/documentation/src/test/java/example/SharedResourcesDemo.java b/documentation/src/test/java/example/SharedResourcesDemo.java
index 99d3727437cc..be3d12b3ce99 100644
--- a/documentation/src/test/java/example/SharedResourcesDemo.java
+++ b/documentation/src/test/java/example/SharedResourcesDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/SlowTests.java b/documentation/src/test/java/example/SlowTests.java
index a1fba4358f35..6fef77843145 100644
--- a/documentation/src/test/java/example/SlowTests.java
+++ b/documentation/src/test/java/example/SlowTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -10,15 +10,16 @@
package example;
-// tag::user_guide[]
import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
+@Tag("exclude")
@Disabled
class SlowTests {
@@ -123,4 +124,3 @@ private void foo() {
IntStream.range(1, 100_000_000).mapToDouble(i -> Math.pow(i, i)).map(Math::sqrt).max();
}
}
-// end::user_guide[]
diff --git a/documentation/src/test/java/example/StandardTests.java b/documentation/src/test/java/example/StandardTests.java
index 91bd871f9ffa..4a6a660ea932 100644
--- a/documentation/src/test/java/example/StandardTests.java
+++ b/documentation/src/test/java/example/StandardTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/SuiteDemo.java b/documentation/src/test/java/example/SuiteDemo.java
new file mode 100644
index 000000000000..617806b3d035
--- /dev/null
+++ b/documentation/src/test/java/example/SuiteDemo.java
@@ -0,0 +1,28 @@
+/*
+ * 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 example;
+
+//tag::user_guide[]
+import org.junit.platform.suite.api.IncludeClassNamePatterns;
+import org.junit.platform.suite.api.SelectPackages;
+import org.junit.platform.suite.api.Suite;
+import org.junit.platform.suite.api.SuiteDisplayName;
+
+@Suite
+@SuiteDisplayName("JUnit Platform Suite Demo")
+@SelectPackages("example")
+@IncludeClassNamePatterns(".*Tests")
+//end::user_guide[]
+@org.junit.platform.suite.api.ExcludeTags("exclude")
+//tag::user_guide[]
+class SuiteDemo {
+}
+//end::user_guide[]
diff --git a/documentation/src/test/java/example/TaggingDemo.java b/documentation/src/test/java/example/TaggingDemo.java
index 6ab3e162badf..ebe9594b1c2d 100644
--- a/documentation/src/test/java/example/TaggingDemo.java
+++ b/documentation/src/test/java/example/TaggingDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/TempDirectoryDemo.java b/documentation/src/test/java/example/TempDirectoryDemo.java
index e656b44142dc..027242993e58 100644
--- a/documentation/src/test/java/example/TempDirectoryDemo.java
+++ b/documentation/src/test/java/example/TempDirectoryDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -12,15 +12,30 @@
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS;
import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
+import com.google.common.jimfs.Configuration;
+import com.google.common.jimfs.Jimfs;
+
+import example.TempDirectoryDemo.InMemoryTempDirDemo.JimfsTempDirFactory;
import example.util.ListWriter;
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.io.TempDir;
+import org.junit.jupiter.api.io.TempDirFactory;
class TempDirectoryDemo {
@@ -35,6 +50,19 @@ void writeItemsToFile(@TempDir Path tempDir) throws IOException {
}
// end::user_guide_parameter_injection[]
+ // tag::user_guide_multiple_directories[]
+ @Test
+ void copyFileFromSourceToTarget(@TempDir Path source, @TempDir Path target) throws IOException {
+ Path sourceFile = source.resolve("test.txt");
+ new ListWriter(sourceFile).write("a", "b", "c");
+
+ Path targetFile = Files.copy(sourceFile, target.resolve("test.txt"));
+
+ assertNotEquals(sourceFile, targetFile);
+ assertEquals(singletonList("a,b,c"), Files.readAllLines(targetFile));
+ }
+ // end::user_guide_multiple_directories[]
+
static
// tag::user_guide_field_injection[]
class SharedTempDirectoryDemo {
@@ -59,4 +87,87 @@ void anotherTestThatUsesTheSameTempDir() {
}
// end::user_guide_field_injection[]
+ static
+ // tag::user_guide_cleanup_mode[]
+ class CleanupModeDemo {
+
+ @Test
+ void fileTest(@TempDir(cleanup = ON_SUCCESS) Path tempDir) {
+ // perform test
+ }
+
+ }
+ // end::user_guide_cleanup_mode[]
+
+ static
+ // tag::user_guide_factory_name_prefix[]
+ class TempDirFactoryDemo {
+
+ @Test
+ void factoryTest(@TempDir(factory = Factory.class) Path tempDir) {
+ assertTrue(tempDir.getFileName().toString().startsWith("factoryTest"));
+ }
+
+ static class Factory implements TempDirFactory {
+
+ @Override
+ public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)
+ throws IOException {
+ return Files.createTempDirectory(extensionContext.getRequiredTestMethod().getName());
+ }
+
+ }
+
+ }
+ // end::user_guide_factory_name_prefix[]
+
+ static
+ // tag::user_guide_factory_jimfs[]
+ class InMemoryTempDirDemo {
+
+ @Test
+ void test(@TempDir(factory = JimfsTempDirFactory.class) Path tempDir) {
+ // perform test
+ }
+
+ static class JimfsTempDirFactory implements TempDirFactory {
+
+ private final FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix());
+
+ @Override
+ public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)
+ throws IOException {
+ return Files.createTempDirectory(fileSystem.getPath("/"), "junit");
+ }
+
+ @Override
+ public void close() throws IOException {
+ fileSystem.close();
+ }
+
+ }
+
+ }
+ // end::user_guide_factory_jimfs[]
+
+ // tag::user_guide_composed_annotation[]
+ @Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.PARAMETER })
+ @Retention(RetentionPolicy.RUNTIME)
+ @TempDir(factory = JimfsTempDirFactory.class)
+ @interface JimfsTempDir {
+ }
+ // end::user_guide_composed_annotation[]
+
+ static
+ // tag::user_guide_composed_annotation_usage[]
+ class JimfsTempDirAnnotationDemo {
+
+ @Test
+ void test(@JimfsTempDir Path tempDir) {
+ // perform test
+ }
+
+ }
+ // end::user_guide_composed_annotation_usage[]
+
}
diff --git a/documentation/src/test/java/example/TestInfoDemo.java b/documentation/src/test/java/example/TestInfoDemo.java
index 806397b9408d..ac8f044edf17 100644
--- a/documentation/src/test/java/example/TestInfoDemo.java
+++ b/documentation/src/test/java/example/TestInfoDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/TestReporterDemo.java b/documentation/src/test/java/example/TestReporterDemo.java
index 5638d24f19d8..dbd78d94aa94 100644
--- a/documentation/src/test/java/example/TestReporterDemo.java
+++ b/documentation/src/test/java/example/TestReporterDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/TestTemplateDemo.java b/documentation/src/test/java/example/TestTemplateDemo.java
index e017af9d7db0..5f0c56a24a37 100644
--- a/documentation/src/test/java/example/TestTemplateDemo.java
+++ b/documentation/src/test/java/example/TestTemplateDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/TestingAStackDemo.java b/documentation/src/test/java/example/TestingAStackDemo.java
index c9cc1e0adc06..ab7b8339c989 100644
--- a/documentation/src/test/java/example/TestingAStackDemo.java
+++ b/documentation/src/test/java/example/TestingAStackDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/TimeoutDemo.java b/documentation/src/test/java/example/TimeoutDemo.java
index 5959befda41f..82e89ce48479 100644
--- a/documentation/src/test/java/example/TimeoutDemo.java
+++ b/documentation/src/test/java/example/TimeoutDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -13,9 +13,12 @@
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.api.Timeout.ThreadMode;
+@Tag("timeout")
// tag::user_guide[]
class TimeoutDemo {
@@ -26,9 +29,15 @@ void setUp() {
}
@Test
- @Timeout(value = 100, unit = TimeUnit.MILLISECONDS)
- void failsIfExecutionTimeExceeds100Milliseconds() {
- // fails if execution time exceeds 100 milliseconds
+ @Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
+ void failsIfExecutionTimeExceeds500Milliseconds() {
+ // fails if execution time exceeds 500 milliseconds
+ }
+
+ @Test
+ @Timeout(value = 500, unit = TimeUnit.MILLISECONDS, threadMode = ThreadMode.SEPARATE_THREAD)
+ void failsIfExecutionTimeExceeds500MillisecondsInSeparateThread() {
+ // fails if execution time exceeds 500 milliseconds, the test code is executed in a separate thread
}
}
diff --git a/documentation/src/test/java/example/UsingTheLauncherDemo.java b/documentation/src/test/java/example/UsingTheLauncherDemo.java
index e7e2dbe001b3..403e9084c610 100644
--- a/documentation/src/test/java/example/UsingTheLauncherDemo.java
+++ b/documentation/src/test/java/example/UsingTheLauncherDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -19,8 +19,14 @@
import java.nio.file.Path;
import java.nio.file.Paths;
+import org.junit.platform.engine.FilterResult;
+import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.launcher.Launcher;
+import org.junit.platform.launcher.LauncherDiscoveryListener;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
+import org.junit.platform.launcher.LauncherSession;
+import org.junit.platform.launcher.LauncherSessionListener;
+import org.junit.platform.launcher.PostDiscoveryFilter;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherConfig;
@@ -51,13 +57,16 @@ void discovery() {
)
.build();
- Launcher launcher = LauncherFactory.create();
+ try (LauncherSession session = LauncherFactory.openSession()) {
+ TestPlan testPlan = session.getLauncher().discover(request);
- TestPlan testPlan = launcher.discover(request);
+ // ... discover additional test plans or execute tests
+ }
// end::discovery[]
// @formatter:on
}
+ @org.junit.jupiter.api.Tag("exclude")
@org.junit.jupiter.api.Test
@SuppressWarnings("unused")
void execution() {
@@ -73,16 +82,22 @@ void execution() {
)
.build();
- Launcher launcher = LauncherFactory.create();
-
- // Register a listener of your choice
SummaryGeneratingListener listener = new SummaryGeneratingListener();
- launcher.registerTestExecutionListeners(listener);
- launcher.execute(request);
+ try (LauncherSession session = LauncherFactory.openSession()) {
+ Launcher launcher = session.getLauncher();
+ // Register a listener of your choice
+ launcher.registerTestExecutionListeners(listener);
+ // Discover tests and build a test plan
+ TestPlan testPlan = launcher.discover(request);
+ // Execute test plan
+ launcher.execute(testPlan);
+ // Alternatively, execute the request directly
+ launcher.execute(request);
+ }
TestExecutionSummary summary = listener.getSummary();
- // Do something with the TestExecutionSummary.
+ // Do something with the summary...
// end::execution[]
// @formatter:on
@@ -96,19 +111,25 @@ void launcherConfig() {
// tag::launcherConfig[]
LauncherConfig launcherConfig = LauncherConfig.builder()
.enableTestEngineAutoRegistration(false)
+ .enableLauncherSessionListenerAutoRegistration(false)
+ .enableLauncherDiscoveryListenerAutoRegistration(false)
+ .enablePostDiscoveryFilterAutoRegistration(false)
.enableTestExecutionListenerAutoRegistration(false)
.addTestEngines(new CustomTestEngine())
+ .addLauncherSessionListeners(new CustomLauncherSessionListener())
+ .addLauncherDiscoveryListeners(new CustomLauncherDiscoveryListener())
+ .addPostDiscoveryFilters(new CustomPostDiscoveryFilter())
.addTestExecutionListeners(new LegacyXmlReportGeneratingListener(reportsDir, out))
.addTestExecutionListeners(new CustomTestExecutionListener())
.build();
- Launcher launcher = LauncherFactory.create(launcherConfig);
-
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectPackage("com.example.mytests"))
.build();
- launcher.execute(request);
+ try (LauncherSession session = LauncherFactory.openSession(launcherConfig)) {
+ session.getLauncher().execute(request);
+ }
// end::launcherConfig[]
// @formatter:on
}
@@ -120,3 +141,16 @@ class MyTestClass {
class CustomTestExecutionListener implements TestExecutionListener {
}
+
+class CustomLauncherSessionListener implements LauncherSessionListener {
+}
+
+class CustomLauncherDiscoveryListener implements LauncherDiscoveryListener {
+}
+
+class CustomPostDiscoveryFilter implements PostDiscoveryFilter {
+ @Override
+ public FilterResult apply(TestDescriptor object) {
+ return FilterResult.included("includes everything");
+ }
+}
diff --git a/documentation/src/test/java/example/callbacks/AbstractDatabaseTests.java b/documentation/src/test/java/example/callbacks/AbstractDatabaseTests.java
index aec3d20ec30a..a6eef4489d07 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/callbacks/BrokenLifecycleMethodConfigDemo.java b/documentation/src/test/java/example/callbacks/BrokenLifecycleMethodConfigDemo.java
index 7d3ed08f75eb..03da5ff8f086 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java b/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java
index aadda9995f2c..792c778810c3 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/callbacks/Extension1.java b/documentation/src/test/java/example/callbacks/Extension1.java
index a02451794e86..f0f5e697ba8a 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/callbacks/Extension2.java b/documentation/src/test/java/example/callbacks/Extension2.java
index ea0cf0467eb7..a7cc878f1f76 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/callbacks/Logger.java b/documentation/src/test/java/example/callbacks/Logger.java
index e3e3a76dcb65..ab2271256b3f 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/defaultmethods/ComparableContract.java b/documentation/src/test/java/example/defaultmethods/ComparableContract.java
index 0002836ebada..f72d80f8894c 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/defaultmethods/EqualsContract.java b/documentation/src/test/java/example/defaultmethods/EqualsContract.java
index 47e1252d6236..36e30258ca92 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/defaultmethods/StringTests.java b/documentation/src/test/java/example/defaultmethods/StringTests.java
index 965100debfbd..1449f28f8dbd 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/defaultmethods/Testable.java b/documentation/src/test/java/example/defaultmethods/Testable.java
index 273a225c20ff..84bf397b87eb 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/exception/IgnoreIOExceptionExtension.java b/documentation/src/test/java/example/exception/IgnoreIOExceptionExtension.java
index 28505bf3e7a9..f2010d1b8efb 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/exception/IgnoreIOExceptionTests.java b/documentation/src/test/java/example/exception/IgnoreIOExceptionTests.java
index f4de1484fad7..ca706586e858 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/exception/MultipleHandlersTestCase.java b/documentation/src/test/java/example/exception/MultipleHandlersTestCase.java
index 48ee9ec88d7d..3db997fe97a0 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/exception/RecordStateOnErrorExtension.java b/documentation/src/test/java/example/exception/RecordStateOnErrorExtension.java
index 3f0422107036..ed4c76979b32 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/extensions/Random.java b/documentation/src/test/java/example/extensions/Random.java
new file mode 100644
index 000000000000..4b9a904d0bb1
--- /dev/null
+++ b/documentation/src/test/java/example/extensions/Random.java
@@ -0,0 +1,26 @@
+/*
+ * 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 example.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.extension.ExtendWith;
+
+//tag::user_guide[]
+@Target({ ElementType.FIELD, ElementType.PARAMETER })
+@Retention(RetentionPolicy.RUNTIME)
+@ExtendWith(RandomNumberExtension.class)
+public @interface Random {
+}
+//end::user_guide[]
diff --git a/documentation/src/test/java/example/extensions/RandomNumberDemo.java b/documentation/src/test/java/example/extensions/RandomNumberDemo.java
new file mode 100644
index 000000000000..8dd274bda73c
--- /dev/null
+++ b/documentation/src/test/java/example/extensions/RandomNumberDemo.java
@@ -0,0 +1,44 @@
+/*
+ * 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 example.extensions;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+// tag::user_guide[]
+class RandomNumberDemo {
+
+ // Use static randomNumber0 field anywhere in the test class,
+ // including @BeforeAll or @AfterEach lifecycle methods.
+ @Random
+ private static Integer randomNumber0;
+
+ // Use randomNumber1 field in test methods and @BeforeEach
+ // or @AfterEach lifecycle methods.
+ @Random
+ private int randomNumber1;
+
+ RandomNumberDemo(@Random int randomNumber2) {
+ // Use randomNumber2 in constructor
+ }
+
+ @BeforeEach
+ void beforeEach(@Random int randomNumber3) {
+ // Use randomNumber3 in @BeforeEach method.
+ }
+
+ @Test
+ void test(@Random int randomNumber4) {
+ // Use randomNumber4 in test method.
+ }
+
+}
+// end::user_guide[]
diff --git a/documentation/src/test/java/example/extensions/RandomNumberExtension.java b/documentation/src/test/java/example/extensions/RandomNumberExtension.java
new file mode 100644
index 000000000000..3bbece08e206
--- /dev/null
+++ b/documentation/src/test/java/example/extensions/RandomNumberExtension.java
@@ -0,0 +1,94 @@
+/*
+ * 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 example.extensions;
+
+// tag::user_guide[]
+
+import static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedFields;
+
+import java.lang.reflect.Field;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolver;
+import org.junit.jupiter.api.extension.TestInstancePostProcessor;
+import org.junit.platform.commons.support.ModifierSupport;
+
+// end::user_guide[]
+// @formatter:off
+// tag::user_guide[]
+class RandomNumberExtension
+ implements BeforeAllCallback, TestInstancePostProcessor, ParameterResolver {
+
+ private final java.util.Random random = new java.util.Random(System.nanoTime());
+
+ /**
+ * Inject a random integer into static fields that are annotated with
+ * {@code @Random} and can be assigned an integer value.
+ */
+ @Override
+ public void beforeAll(ExtensionContext context) {
+ Class> testClass = context.getRequiredTestClass();
+ injectFields(testClass, null, ModifierSupport::isStatic);
+ }
+
+ /**
+ * Inject a random integer into non-static fields that are annotated with
+ * {@code @Random} and can be assigned an integer value.
+ */
+ @Override
+ public void postProcessTestInstance(Object testInstance, ExtensionContext context) {
+ Class> testClass = context.getRequiredTestClass();
+ injectFields(testClass, testInstance, ModifierSupport::isNotStatic);
+ }
+
+ /**
+ * Determine if the parameter is annotated with {@code @Random} and can be
+ * assigned an integer value.
+ */
+ @Override
+ public boolean supportsParameter(ParameterContext pc, ExtensionContext ec) {
+ return pc.isAnnotated(Random.class) && isInteger(pc.getParameter().getType());
+ }
+
+ /**
+ * Resolve a random integer.
+ */
+ @Override
+ public Integer resolveParameter(ParameterContext pc, ExtensionContext ec) {
+ return this.random.nextInt();
+ }
+
+ private void injectFields(Class> testClass, Object testInstance,
+ Predicate predicate) {
+
+ predicate = predicate.and(field -> isInteger(field.getType()));
+ findAnnotatedFields(testClass, Random.class, predicate)
+ .forEach(field -> {
+ try {
+ field.setAccessible(true);
+ field.set(testInstance, this.random.nextInt());
+ }
+ catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ });
+ }
+
+ private static boolean isInteger(Class> type) {
+ return int.class.isAssignableFrom(type);
+ }
+
+}
+// end::user_guide[]
+// @formatter:on
diff --git a/documentation/src/test/java/example/interceptor/SwingEdtInterceptor.java b/documentation/src/test/java/example/interceptor/SwingEdtInterceptor.java
index 78f21e8e3b66..b12094f382b9 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/registration/DocumentationDemo.java b/documentation/src/test/java/example/registration/DocumentationDemo.java
index fe542cf37c28..973b4f1967e3 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/registration/WebServerDemo.java b/documentation/src/test/java/example/registration/WebServerDemo.java
index c99d9c0e70f6..25a6b2797305 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java b/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java
new file mode 100644
index 000000000000..595be12b4717
--- /dev/null
+++ b/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java
@@ -0,0 +1,92 @@
+/*
+ * 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 example.session;
+
+//tag::user_guide[]
+import static java.net.InetAddress.getLoopbackAddress;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.InetSocketAddress;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import com.sun.net.httpserver.HttpServer;
+
+import org.junit.platform.launcher.LauncherSession;
+import org.junit.platform.launcher.LauncherSessionListener;
+import org.junit.platform.launcher.TestExecutionListener;
+import org.junit.platform.launcher.TestPlan;
+
+public class GlobalSetupTeardownListener implements LauncherSessionListener {
+
+ private Fixture fixture;
+
+ @Override
+ public void launcherSessionOpened(LauncherSession session) {
+ // Avoid setup for test discovery by delaying it until tests are about to be executed
+ session.getLauncher().registerTestExecutionListeners(new TestExecutionListener() {
+ @Override
+ public void testPlanExecutionStarted(TestPlan testPlan) {
+ //end::user_guide[]
+ if (!testPlan.getConfigurationParameters().getBoolean("enableHttpServer").orElse(false)) {
+ // avoid starting multiple HTTP servers unnecessarily from UsingTheLauncherDemo
+ return;
+ }
+ //tag::user_guide[]
+ if (fixture == null) {
+ fixture = new Fixture();
+ fixture.setUp();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void launcherSessionClosed(LauncherSession session) {
+ if (fixture != null) {
+ fixture.tearDown();
+ fixture = null;
+ }
+ }
+
+ static class Fixture {
+
+ private HttpServer server;
+ private ExecutorService executorService;
+
+ void setUp() {
+ try {
+ server = HttpServer.create(new InetSocketAddress(getLoopbackAddress(), 0), 0);
+ }
+ catch (IOException e) {
+ throw new UncheckedIOException("Failed to start HTTP server", e);
+ }
+ server.createContext("/test", exchange -> {
+ exchange.sendResponseHeaders(204, -1);
+ exchange.close();
+ });
+ executorService = Executors.newCachedThreadPool();
+ server.setExecutor(executorService);
+ server.start(); // <1>
+ int port = server.getAddress().getPort();
+ System.setProperty("http.server.host", getLoopbackAddress().getHostAddress()); // <2>
+ System.setProperty("http.server.port", String.valueOf(port)); // <3>
+ }
+
+ void tearDown() {
+ server.stop(0); // <4>
+ executorService.shutdownNow();
+ }
+ }
+
+}
+//end::user_guide[]
diff --git a/documentation/src/test/java/example/session/HttpTests.java b/documentation/src/test/java/example/session/HttpTests.java
new file mode 100644
index 000000000000..cbdf5367cdfd
--- /dev/null
+++ b/documentation/src/test/java/example/session/HttpTests.java
@@ -0,0 +1,37 @@
+/*
+ * 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 example.session;
+
+//tag::user_guide[]
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+
+import org.junit.jupiter.api.Test;
+
+class HttpTests {
+
+ @Test
+ void respondsWith204() throws Exception {
+ String host = System.getProperty("http.server.host"); // <1>
+ String port = System.getProperty("http.server.port"); // <2>
+ URL url = URI.create("http://" + host + ":" + port + "/test").toURL();
+
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+ int responseCode = connection.getResponseCode(); // <3>
+
+ assertEquals(204, responseCode); // <4>
+ }
+}
+//end::user_guide[]
diff --git a/documentation/src/test/java/example/testinterface/TestInterfaceDemo.java b/documentation/src/test/java/example/testinterface/TestInterfaceDemo.java
index 86b64961afb3..b24ab3413733 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/testinterface/TestInterfaceDynamicTestsDemo.java b/documentation/src/test/java/example/testinterface/TestInterfaceDynamicTestsDemo.java
index ee611239e61d..694192dbabf0 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/testinterface/TestLifecycleLogger.java b/documentation/src/test/java/example/testinterface/TestLifecycleLogger.java
index d6dae294e16a..b269bfb69a1b 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/testinterface/TimeExecutionLogger.java b/documentation/src/test/java/example/testinterface/TimeExecutionLogger.java
index 944e7c72ada7..b3a53feae91a 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/testkit/EngineTestKitAllEventsDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitAllEventsDemo.java
index 21f05d439caf..5df68019e433 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-2020 the original author or authors.
+ * 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
@@ -41,7 +41,7 @@ class EngineTestKitAllEventsDemo {
void verifyAllJupiterEvents() {
Writer writer = // create a java.io.Writer for debug output
// end::user_guide[]
- // For the demo, we are simply swallowing the debug output.
+ // For the demo, we are swallowing the debug output.
new StringWriter();
// tag::user_guide[]
diff --git a/documentation/src/test/java/example/testkit/EngineTestKitFailedMethodDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitFailedMethodDemo.java
index 78d2173f2ebd..40ea51ba8a80 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/testkit/EngineTestKitSkippedMethodDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitSkippedMethodDemo.java
index 708dfe598c38..1d06770f1a25 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/testkit/EngineTestKitStatisticsDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitStatisticsDemo.java
index ef3ca601494b..b9576352389b 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/timing/TimingExtension.java b/documentation/src/test/java/example/timing/TimingExtension.java
index 6c4fcc73027e..b6b51d61ce99 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/example/timing/TimingExtensionTests.java b/documentation/src/test/java/example/timing/TimingExtensionTests.java
index b9b11a1582d6..ee84b196b066 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-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/extensions/ExpectToFail.java b/documentation/src/test/java/extensions/ExpectToFail.java
index c325a81de5db..8dd21a717caa 100644
--- a/documentation/src/test/java/extensions/ExpectToFail.java
+++ b/documentation/src/test/java/extensions/ExpectToFail.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/org/junit/api/tools/AbstractApiReportWriter.java b/documentation/src/test/java/org/junit/api/tools/AbstractApiReportWriter.java
index ed59e28e792b..6b9a143ca463 100644
--- a/documentation/src/test/java/org/junit/api/tools/AbstractApiReportWriter.java
+++ b/documentation/src/test/java/org/junit/api/tools/AbstractApiReportWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/org/junit/api/tools/ApiReport.java b/documentation/src/test/java/org/junit/api/tools/ApiReport.java
index 067d5318f8eb..b21e63e8face 100644
--- a/documentation/src/test/java/org/junit/api/tools/ApiReport.java
+++ b/documentation/src/test/java/org/junit/api/tools/ApiReport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/org/junit/api/tools/ApiReportGenerator.java b/documentation/src/test/java/org/junit/api/tools/ApiReportGenerator.java
index 963d0c4553cb..7bdb6c21b40d 100644
--- a/documentation/src/test/java/org/junit/api/tools/ApiReportGenerator.java
+++ b/documentation/src/test/java/org/junit/api/tools/ApiReportGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/org/junit/api/tools/ApiReportWriter.java b/documentation/src/test/java/org/junit/api/tools/ApiReportWriter.java
index 8fcadb342744..c231d1b4ba43 100644
--- a/documentation/src/test/java/org/junit/api/tools/ApiReportWriter.java
+++ b/documentation/src/test/java/org/junit/api/tools/ApiReportWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/org/junit/api/tools/AsciidocApiReportWriter.java b/documentation/src/test/java/org/junit/api/tools/AsciidocApiReportWriter.java
index 98f5f8fb2a51..05748d36aee3 100644
--- a/documentation/src/test/java/org/junit/api/tools/AsciidocApiReportWriter.java
+++ b/documentation/src/test/java/org/junit/api/tools/AsciidocApiReportWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/org/junit/api/tools/HtmlApiReportWriter.java b/documentation/src/test/java/org/junit/api/tools/HtmlApiReportWriter.java
index b260f8ff1ce3..48193368370e 100644
--- a/documentation/src/test/java/org/junit/api/tools/HtmlApiReportWriter.java
+++ b/documentation/src/test/java/org/junit/api/tools/HtmlApiReportWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/java/org/junit/api/tools/MarkdownApiReportWriter.java b/documentation/src/test/java/org/junit/api/tools/MarkdownApiReportWriter.java
index 65f0f918b2f8..6294ac58179b 100644
--- a/documentation/src/test/java/org/junit/api/tools/MarkdownApiReportWriter.java
+++ b/documentation/src/test/java/org/junit/api/tools/MarkdownApiReportWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/kotlin/example/FibonacciCalculator.kt b/documentation/src/test/kotlin/example/FibonacciCalculator.kt
index ae16322fc335..33ddbe089ff1 100644
--- a/documentation/src/test/kotlin/example/FibonacciCalculator.kt
+++ b/documentation/src/test/kotlin/example/FibonacciCalculator.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt b/documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt
index 23c901fa1672..0ea73f7f99d8 100644
--- a/documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt
+++ b/documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
@@ -13,15 +13,16 @@ package example
import example.domain.Person
import example.util.Calculator
-import java.time.Duration
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertAll
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.assertTimeout
import org.junit.jupiter.api.assertTimeoutPreemptively
+import java.time.Duration
class KotlinAssertionsDemo {
@@ -48,7 +49,8 @@ class KotlinAssertionsDemo {
@Test
fun `grouped assertions`() {
- assertAll("Person properties",
+ assertAll(
+ "Person properties",
{ assertEquals("Jane", person.firstName) },
{ assertEquals("Doe", person.lastName) }
)
@@ -56,7 +58,8 @@ class KotlinAssertionsDemo {
@Test
fun `grouped assertions from a stream`() {
- assertAll("People with first name starting with J",
+ assertAll(
+ "People with first name starting with J",
people
.stream()
.map {
@@ -68,11 +71,15 @@ class KotlinAssertionsDemo {
@Test
fun `grouped assertions from a collection`() {
- assertAll("People with last name of Doe",
+ assertAll(
+ "People with last name of Doe",
people.map { { assertEquals("Doe", it.lastName) } }
)
}
+ // end::user_guide[]
+ @Tag("timeout")
+ // tag::user_guide[]
@Test
fun `timeout not exceeded testing`() {
val fibonacciCalculator = FibonacciCalculator()
@@ -83,6 +90,7 @@ class KotlinAssertionsDemo {
}
// end::user_guide[]
+ @Tag("timeout")
@extensions.ExpectToFail
// tag::user_guide[]
@Test
diff --git a/documentation/src/test/kotlin/example/registration/KotlinWebServerDemo.kt b/documentation/src/test/kotlin/example/registration/KotlinWebServerDemo.kt
index 73b36a59365e..52a390fe7e0f 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-2020 the original author or authors.
+ * 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
@@ -17,11 +17,11 @@ import org.junit.jupiter.api.extension.RegisterExtension
class KotlinWebServerDemo {
companion object {
- @JvmField
+ @JvmStatic
@RegisterExtension
val server = WebServerExtension.builder()
- .enableSecurity(false)
- .build()
+ .enableSecurity(false)
+ .build()
}
@Test
diff --git a/documentation/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener b/documentation/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener
new file mode 100644
index 000000000000..f6d297627eeb
--- /dev/null
+++ b/documentation/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener
@@ -0,0 +1 @@
+example.session.GlobalSetupTeardownListener
diff --git a/documentation/src/test/resources/two-column.csv b/documentation/src/test/resources/two-column.csv
index 2ef0d215adac..7ebb4c545f1b 100644
--- a/documentation/src/test/resources/two-column.csv
+++ b/documentation/src/test/resources/two-column.csv
@@ -1,4 +1,5 @@
-Country, reference
+COUNTRY, REFERENCE
Sweden, 1
Poland, 2
"United States of America", 3
+France, 700_000
diff --git a/gradle.properties b/gradle.properties
index 30e3b4488249..8bbcba21a037 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,58 +1,32 @@
group = org.junit
-version = 5.7.0
+version = 5.10.0
jupiterGroup = org.junit.jupiter
platformGroup = org.junit.platform
-platformVersion = 1.7.0
+platformVersion = 1.10.0
vintageGroup = org.junit.vintage
-vintageVersion = 5.7.0
+vintageVersion = 5.10.0
defaultBuiltBy = JUnit Team
# We need more metaspace due to apparent memory leak in Asciidoctor/JRuby
-org.gradle.jvmargs=-Xmx1g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError
+# The exports are needed due to https://github.com/diffplug/spotless/issues/834
+org.gradle.jvmargs=-Xmx1g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError \
+ --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
+ --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
+ --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
+ --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
+ --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.kotlin.dsl.allWarningsAsErrors=true
-# Dependencies
-apiguardian.version=1.1.0
-assertj.version=3.16.1
-junit4.version=4.13
-junit4Min.version=4.12
-opentest4j.version=1.2.0
-picocli.version=4.5.0
-univocity-parsers.version=2.9.0
+# Test Distribution
+gradle.internal.testdistribution.writeTraceFile=true
-# Test Dependencies
-archunit.version=0.14.1
-bartholdy.version=0.2.3
-classgraph.version=4.8.87
-commons-io.version=2.7
-kotlinx-coroutines-core.version=1.3.9
-groovy.version=3.0.5
-log4j.version=2.13.3
-mockito.version=3.5.0
-slf4j.version=1.7.30
-spock.version=1.3-groovy-2.5
-
-# Tools
-checkstyle.version=8.31
-jacoco.version=0.8.5
-jmh.version=1.25
-ktlint.version=0.35.0
-surefire.version=2.22.2
-bnd.version=5.1.2
-
-# Plugins
-gradle.enterprise.plugin.version=3.3.1
-versioning.plugin.version=2.14.0
-versions.plugin.version=0.29.0
-spotless.plugin.version=5.1.1
-git-publish.plugin.version=2.1.3
-kotlin.plugin.version=1.4.0
-asciidoctor-pdf.version=1.5.3
-asciidoctor.plugin.version=3.2.0
-jmh.plugin.version=0.5.0
-nohttp.plugin.version=0.0.5.RELEASE
+# Omit automatic compile dependency on kotlin-stdlib
+# https://kotlinlang.org/docs/gradle.html#dependency-on-the-standard-library
+kotlin.stdlib.default.dependency=false
diff --git a/gradle/build-scan-user-data.gradle b/gradle/build-scan-user-data.gradle
deleted file mode 100644
index 8436692dc512..000000000000
--- a/gradle/build-scan-user-data.gradle
+++ /dev/null
@@ -1,215 +0,0 @@
-// Source: https://github.com/gradle/gradle-build-scan-snippets/blob/master/guided-trials-default-custom-user-data/default-custom-user-data.gradle
-
-tagOs()
-tagIde()
-tagCiOrLocal()
-addCiMetadata()
-addGitMetadata()
-addTestParallelization()
-
-// Add here other scripts, if needed
-//apply from:"${rootProject.projectDir}/<>"
-
-void tagOs() {
- buildScan.tag System.getProperty('os.name')
-}
-
-void tagIde() {
- if (project.hasProperty('android.injected.invoked.from.ide')) {
- buildScan.tag 'Android Studio'
- } else if (System.getProperty('idea.version')) {
- buildScan.tag 'IntelliJ IDEA'
- } else if (System.getProperty('eclipse.buildId')) {
- buildScan.tag 'Eclipse'
- } else if (!isCi()) {
- buildScan.tag 'Cmd Line'
- }
-}
-
-void tagCiOrLocal() {
- buildScan.tag(isCi() ? 'CI' : 'LOCAL')
-}
-
-void addCiMetadata() {
- if (isJenkins()) {
- if (System.getenv('BUILD_URL')) {
- buildScan.link 'Jenkins build', System.getenv('BUILD_URL')
- }
- if (System.getenv('BUILD_NUMBER')) {
- buildScan.value 'CI build number', System.getenv('BUILD_NUMBER')
- }
- if (System.getenv('NODE_NAME')) {
- def agentName = System.getenv('NODE_NAME') == 'master' ? 'master-node' : System.getenv('NODE_NAME')
- buildScan.tag agentName
- buildScan.value 'CI node name', agentName
- }
- if (System.getenv('JOB_NAME')) {
- def jobNameLabel = 'CI job'
- def jobName = System.getenv('JOB_NAME')
- buildScan.value jobNameLabel, jobName
- addCustomValueSearchLink 'CI job build scans', [(jobNameLabel): jobName]
- }
- if (System.getenv('STAGE_NAME')) {
- def stageNameLabel = 'CI stage'
- def stageName = System.getenv('STAGE_NAME')
- buildScan.value stageNameLabel, stageName
- addCustomValueSearchLink 'CI stage build scans', [(stageNameLabel): stageName]
- }
- }
-
- if (isTeamCity()) {
- def teamCityConfigurationFileProp = 'teamcity.configuration.properties.file'
- if (project.hasProperty(teamCityConfigurationFileProp)) {
- def properties = new Properties()
- properties.load(new FileInputStream("${project.property(teamCityConfigurationFileProp)}"))
- def teamCityServerUrl = properties.getProperty("teamcity.serverUrl")
- if (teamCityServerUrl && project.hasProperty('build.number') && project.hasProperty('teamcity.buildType.id')) {
- def teamCityBuildNumber = project.property('build.number')
- def teamCityBuildTypeId = project.property('teamcity.buildType.id')
- buildScan.link 'TeamCity build', "${appendIfMissing(teamCityServerUrl, '/')}viewLog.html?buildNumber=${teamCityBuildNumber}&buildTypeId=${teamCityBuildTypeId}"
- }
- }
- if (project.hasProperty('build.number')) {
- buildScan.value 'CI build number', project.property('build.number')
- }
- if (project.hasProperty('agent.name')) {
- def agentName = project.property('agent.name')
- buildScan.tag agentName
- buildScan.value 'CI agent name', agentName
- }
- }
-
- if (isCircleCI()) {
- if (System.getenv('CIRCLE_BUILD_URL')) {
- buildScan.link 'CircleCI build', System.getenv('CIRCLE_BUILD_URL')
- }
- if (System.getenv('CIRCLE_BUILD_NUM')) {
- buildScan.value 'CI build number', System.getenv('CIRCLE_BUILD_NUM')
- }
- if (System.getenv('CIRCLE_JOB')) {
- def jobLabel = 'CI job'
- def job = System.getenv('CIRCLE_JOB')
- buildScan.value jobLabel, job
- addCustomValueSearchLink 'CI job build scans', [(jobLabel): job]
- }
- if (System.getenv('CIRCLE_WORKFLOW_ID')) {
- def workflowIdLabel = 'CI workflow'
- def workflowId = System.getenv('CIRCLE_WORKFLOW_ID')
- buildScan.value workflowIdLabel, workflowId
- addCustomValueSearchLink 'CI workflow build scans', [(workflowIdLabel): workflowId]
- }
- }
-
- if (isBamboo()) {
- if (System.getenv('bamboo_resultsUrl')) {
- buildScan.link 'Bamboo build', System.getenv('bamboo_resultsUrl')
- }
- if (System.getenv('bamboo_buildNumber')) {
- buildScan.value 'CI build number', System.getenv('bamboo_buildNumber')
- }
- if (System.getenv('bamboo_planName')) {
- def planNameLabel = 'CI plan'
- def planName = System.getenv('bamboo_planName')
- buildScan.value planNameLabel, planName
- addCustomValueSearchLink 'CI plan build scans', [(planNameLabel): planName]
- }
- if (System.getenv('bamboo_buildPlanName')) {
- def jobNameLabel = 'CI job'
- def jobName = System.getenv('bamboo_buildPlanName')
- buildScan.value jobNameLabel, jobName
- addCustomValueSearchLink 'CI job build scans', [(jobNameLabel): jobName]
- }
- if (System.getenv('bamboo_agentId')) {
- def agentId = System.getenv('bamboo_agentId')
- buildScan.tag agentId
- buildScan.value 'CI agent ID', agentId
- }
- }
-
- if (isGitHubActions()) {
- def repo = System.getenv('GITHUB_REPOSITORY')
- def runId = System.getenv('GITHUB_RUN_ID')
- if (repo && runId) {
- buildScan.link 'GitHub Actions run', "https://github.com/$repo/actions/runs/$runId"
- }
- if (System.getenv('GITHUB_WORKFLOW')) {
- def ghActionWorkflowLabel = 'GitHub workflow'
- def ghActionWorkflowName = System.getenv('GITHUB_WORKFLOW')
- buildScan.value ghActionWorkflowLabel, ghActionWorkflowName
- addCustomValueSearchLink 'GitHub workflow build scans', [(ghActionWorkflowLabel): ghActionWorkflowName]
- }
- }
-}
-
-void addGitMetadata() {
- buildScan.background {
- def gitCommitId = versioning.info.commit
- def gitBranchName = versioning.info.branch
- def gitDirty = versioning.info.dirty
-
- if (gitCommitId) {
- def commitIdLabel = 'Git commit id'
- value commitIdLabel, gitCommitId
- addCustomValueSearchLink 'Git commit id build scans', [(commitIdLabel): gitCommitId]
- link 'Github Source', "https://github.com/junit-team/junit5/tree/" + gitCommitId
- }
- if (gitBranchName) {
- tag gitBranchName
- value 'Git branch', gitBranchName
- }
- if (gitDirty) {
- tag 'Dirty'
- }
- }
-}
-
-void addTestParallelization() {
- allprojects { p ->
- p.tasks.withType(Test).configureEach { t -> doFirst { buildScan.value "${t.identityPath}#maxParallelForks", t.maxParallelForks.toString() } }
- }
-}
-
-static boolean isCi() {
- isJenkins() || isTeamCity() || isCircleCI() || isBamboo() || isGitHubActions()
-}
-
-static boolean isJenkins() {
- System.getenv('JENKINS_URL')
-}
-
-static boolean isTeamCity() {
- System.getenv('TEAMCITY_VERSION')
-}
-
-static boolean isCircleCI() {
- System.getenv('CIRCLECI')
-}
-
-static boolean isBamboo() {
- System.getenv('bamboo_resultsUrl')
-}
-
-static boolean isGitHubActions() {
- System.getenv('GITHUB_ACTIONS')
-}
-
-void addCustomValueSearchLink(String title, Map search) {
- if (buildScan.server) {
- buildScan.link title, customValueSearchUrl(search)
- }
-}
-
-String customValueSearchUrl(Map search) {
- def query = search.collect { name, value ->
- "search.names=${encodeURL(name)}&search.values=${encodeURL(value)}"
- }.join('&')
- "${appendIfMissing(buildScan.server, '/')}scans?$query#selection.buildScanB=%7BSCAN_ID%7D"
-}
-
-static String encodeURL(String url) {
- URLEncoder.encode(url, 'UTF-8')
-}
-
-static String appendIfMissing(String str, String suffix) {
- str.endsWith(suffix) ? str : str + suffix
-}
diff --git a/src/checkstyle/checkstyleMain.xml b/gradle/config/checkstyle/checkstyleMain.xml
similarity index 100%
rename from src/checkstyle/checkstyleMain.xml
rename to gradle/config/checkstyle/checkstyleMain.xml
diff --git a/src/checkstyle/checkstyleTest.xml b/gradle/config/checkstyle/checkstyleTest.xml
similarity index 100%
rename from src/checkstyle/checkstyleTest.xml
rename to gradle/config/checkstyle/checkstyleTest.xml
diff --git a/src/checkstyle/suppressions.xml b/gradle/config/checkstyle/suppressions.xml
similarity index 50%
rename from src/checkstyle/suppressions.xml
rename to gradle/config/checkstyle/suppressions.xml
index b15726f91ffb..05801fccce21 100644
--- a/src/checkstyle/suppressions.xml
+++ b/gradle/config/checkstyle/suppressions.xml
@@ -1,7 +1,7 @@
+ files="junit-platform-commons[\\/]src[\\/]main[\\/]java.+?[\\/]org[\\/]junit[\\/]platform[\\/]commons[\\/]util[\\/]*"/>
+ files="junit-platform-console[\\/]src[\\/]main[\\/]java.+?[\\/]org[\\/]junit[\\/]platform[\\/]console[\\/]*"/>
diff --git a/src/eclipse/junit-eclipse-formatter-settings.xml b/gradle/config/eclipse/junit-eclipse-formatter-settings.xml
similarity index 100%
rename from src/eclipse/junit-eclipse-formatter-settings.xml
rename to gradle/config/eclipse/junit-eclipse-formatter-settings.xml
diff --git a/gradle/config/eclipse/junit-eclipse.importorder b/gradle/config/eclipse/junit-eclipse.importorder
new file mode 100644
index 000000000000..56cb0619d774
--- /dev/null
+++ b/gradle/config/eclipse/junit-eclipse.importorder
@@ -0,0 +1,12 @@
+#Organize Import Order
+0=java
+1=javax
+2=jdk
+3=aQute
+4=junit
+5=de
+6=com
+7=example
+8=extensions
+9=io
+10=org
diff --git a/src/spotless/eclipse-public-license-2.0.java b/gradle/config/spotless/eclipse-public-license-2.0.java
similarity index 82%
rename from src/spotless/eclipse-public-license-2.0.java
rename to gradle/config/spotless/eclipse-public-license-2.0.java
index 5d687704ddb7..9bbea1d478a3 100644
--- a/src/spotless/eclipse-public-license-2.0.java
+++ b/gradle/config/spotless/eclipse-public-license-2.0.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * 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
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 000000000000..d69b3f911c10
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,81 @@
+[versions]
+ant = "1.10.13"
+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"
+junit4 = "4.13.2"
+junit4Osgi = "4.13.2_1"
+junit4Min = "4.12"
+ktlint = "0.48.2"
+log4j = "2.20.0"
+opentest4j = "1.3.0"
+openTestReporting = "0.1.0-M1"
+surefire = "3.1.2"
+xmlunit = "2.9.1"
+
+[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" }
+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" }
+groovy2-bom = { module = "org.codehaus.groovy:groovy-bom", version = "2.5.21" }
+hamcrest = { module = "org.hamcrest:hamcrest", version = "2.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" }
+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" }
+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" }
+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" }
+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" }
+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" }
+
+[bundles]
+ant = ["ant", "ant-junit", "ant-junitlauncher"]
+log4j = ["log4j-core", "log4j-jul"]
+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" }
+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" }
diff --git a/gradle/plugins/build-parameters/build.gradle.kts b/gradle/plugins/build-parameters/build.gradle.kts
new file mode 100644
index 000000000000..f70f2d8bc062
--- /dev/null
+++ b/gradle/plugins/build-parameters/build.gradle.kts
@@ -0,0 +1,75 @@
+plugins {
+ alias(libs.plugins.buildParameters)
+}
+
+group = "junitbuild"
+
+buildParameters {
+ pluginId("junitbuild.build-parameters")
+ bool("ci") {
+ description = "Whether or not this build is running in a CI environment"
+ 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()
+ }
+ string("url") {
+ description = "URL to the remote build cache"
+ fromEnvironment()
+ }
+ }
+ group("documentation") {
+ description = "Parameters controlling how the documentation is built"
+ bool("replaceCurrentDocs") {
+ description = "The documentation that is being deployed will replace what's currently deployed as 'current'"
+ 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("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"
+ }
+ }
+ }
+ group("testing") {
+ description = "Testing related parameters"
+ bool("enableJaCoCo") {
+ description = "Enables JaCoCo test coverage reporting"
+ defaultValue = false
+ }
+ bool("enableJFR") {
+ description = "Enables Java Flight Recorder functionality"
+ defaultValue = false
+ }
+ integer("retries") {
+ description = "Configures the number of times failing test are retried"
+ }
+ }
+}
diff --git a/gradle/plugins/build.gradle.kts b/gradle/plugins/build.gradle.kts
new file mode 100644
index 000000000000..f64d71d4260c
--- /dev/null
+++ b/gradle/plugins/build.gradle.kts
@@ -0,0 +1,3 @@
+plugins {
+ alias(libs.plugins.versions)
+}
diff --git a/gradle/plugins/common/build.gradle.kts b/gradle/plugins/common/build.gradle.kts
new file mode 100644
index 000000000000..0753c6a3099e
--- /dev/null
+++ b/gradle/plugins/common/build.gradle.kts
@@ -0,0 +1,19 @@
+plugins {
+ `kotlin-dsl`
+}
+
+repositories {
+ gradlePluginPortal()
+}
+
+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)
+}
diff --git a/gradle/plugins/common/src/main/kotlin/ConfigurationContainerExtensions.kt b/gradle/plugins/common/src/main/kotlin/ConfigurationContainerExtensions.kt
new file mode 100644
index 000000000000..ebe7399e9f15
--- /dev/null
+++ b/gradle/plugins/common/src/main/kotlin/ConfigurationContainerExtensions.kt
@@ -0,0 +1,14 @@
+
+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
new file mode 100644
index 000000000000..834374bf06c6
--- /dev/null
+++ b/gradle/plugins/common/src/main/kotlin/JavaLibraryExtension.kt
@@ -0,0 +1,7 @@
+import org.gradle.api.JavaVersion
+
+open class JavaLibraryExtension {
+ var mainJavaVersion: JavaVersion = JavaVersion.VERSION_1_8
+ var testJavaVersion: JavaVersion = JavaVersion.VERSION_17
+ var configureRelease: Boolean = true
+}
diff --git a/buildSrc/src/main/kotlin/License.kt b/gradle/plugins/common/src/main/kotlin/License.kt
similarity index 60%
rename from buildSrc/src/main/kotlin/License.kt
rename to gradle/plugins/common/src/main/kotlin/License.kt
index 4f4e78eaa5b2..9d83baeaf8c1 100644
--- a/buildSrc/src/main/kotlin/License.kt
+++ b/gradle/plugins/common/src/main/kotlin/License.kt
@@ -1,4 +1,4 @@
-import java.io.File
+import org.gradle.api.file.RegularFile
import java.net.URI
-data class License(val name: String, val url: URI, val headerFile: File)
+data class License(val name: String, val url: URI, val headerFile: RegularFile)
diff --git a/gradle/plugins/common/src/main/kotlin/ProjectExtensions.kt b/gradle/plugins/common/src/main/kotlin/ProjectExtensions.kt
new file mode 100644
index 000000000000..fc8a515977e7
--- /dev/null
+++ b/gradle/plugins/common/src/main/kotlin/ProjectExtensions.kt
@@ -0,0 +1,19 @@
+import org.gradle.api.Project
+import org.gradle.api.artifacts.VersionCatalog
+import org.gradle.api.artifacts.VersionCatalogsExtension
+import org.gradle.kotlin.dsl.the
+
+val Project.javaModuleName: String
+ get() = "org." + this.name.replace('-', '.')
+
+fun Project.requiredVersionFromLibs(name: String) =
+ libsVersionCatalog.findVersion(name).get().requiredVersion
+
+fun Project.dependencyFromLibs(name: String) =
+ libsVersionCatalog.findLibrary(name).get()
+
+fun Project.bundleFromLibs(name: String) =
+ libsVersionCatalog.findBundle(name).get()
+
+private val Project.libsVersionCatalog: VersionCatalog
+ get() = the().named("libs")
diff --git a/gradle/plugins/common/src/main/kotlin/TaskExtensions.kt b/gradle/plugins/common/src/main/kotlin/TaskExtensions.kt
new file mode 100644
index 000000000000..7ad4e7ab46cb
--- /dev/null
+++ b/gradle/plugins/common/src/main/kotlin/TaskExtensions.kt
@@ -0,0 +1,5 @@
+import org.gradle.api.Task
+import org.gradle.internal.os.OperatingSystem
+
+fun Task.trackOperationSystemAsInput() =
+ inputs.property("os", OperatingSystem.current().familyName)
diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.base-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.base-conventions.gradle.kts
new file mode 100644
index 000000000000..4bd5a4660201
--- /dev/null
+++ b/gradle/plugins/common/src/main/kotlin/junitbuild.base-conventions.gradle.kts
@@ -0,0 +1,6 @@
+plugins {
+ eclipse
+ idea
+ id("junitbuild.java-toolchain-conventions")
+ id("junitbuild.spotless-conventions")
+}
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
new file mode 100644
index 000000000000..ad58b4898de5
--- /dev/null
+++ b/gradle/plugins/common/src/main/kotlin/junitbuild.build-metadata.gradle.kts
@@ -0,0 +1,28 @@
+import java.time.Instant
+import java.time.OffsetDateTime
+import java.time.ZoneOffset
+import java.time.format.DateTimeFormatter
+
+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()
+
+ Instant.ofEpochSecond(sourceDateEpoch).atOffset(ZoneOffset.UTC)
+
+ } else {
+ 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 buildRevision: String by extra {
+ providers.exec {
+ commandLine("git", "rev-parse", "--verify", "HEAD")
+ }.standardOutput.asText.get()
+}
+val builtByValue by extra { project.findProperty("builtBy") ?: project.property("defaultBuiltBy") }
diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.dependency-update-check.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.dependency-update-check.gradle.kts
new file mode 100644
index 000000000000..f7bac1fb8efc
--- /dev/null
+++ b/gradle/plugins/common/src/main/kotlin/junitbuild.dependency-update-check.gradle.kts
@@ -0,0 +1,19 @@
+plugins {
+ id("com.github.ben-manes.versions")
+}
+
+tasks.dependencyUpdates {
+ checkConstraints = true
+ resolutionStrategy {
+ componentSelection {
+ all {
+ val rejected = listOf("alpha", "beta", "rc", "cr", "m", "preview", "b", "ea")
+ .map { qualifier -> Regex("(?i).*[.-]$qualifier[.\\d-+]*") }
+ .any { it.matches(candidate.version) }
+ if (rejected) {
+ reject("Release candidate")
+ }
+ }
+ }
+ }
+}
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
new file mode 100644
index 000000000000..91008c81b184
--- /dev/null
+++ b/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-aggregation-conventions.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+ id("junitbuild.jacoco-conventions")
+ `jacoco-report-aggregation`
+}
+
+reporting {
+ reports {
+ create("jacocoRootReport") {
+ testType = TestSuiteType.UNIT_TEST
+ }
+ }
+}
diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-conventions.gradle.kts
new file mode 100644
index 000000000000..ef29df71f8c0
--- /dev/null
+++ b/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-conventions.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+ jacoco
+ id("junitbuild.build-parameters")
+}
+
+jacoco {
+ toolVersion = requiredVersionFromLibs("jacoco")
+}
+
+tasks.withType().configureEach {
+ enabled = buildParameters.testing.enableJaCoCo
+}
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
new file mode 100644
index 000000000000..804de8388562
--- /dev/null
+++ b/gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-java-conventions.gradle.kts
@@ -0,0 +1,31 @@
+import org.gradle.api.attributes.LibraryElements.CLASSES
+import org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE
+
+plugins {
+ java
+ id("junitbuild.build-parameters")
+ id("junitbuild.jacoco-conventions")
+}
+
+val mavenizedProjects: List by rootProject.extra
+
+tasks.withType().configureEach {
+ configure {
+ isEnabled = buildParameters.testing.enableJaCoCo
+ }
+}
+
+val codeCoverageClassesJar by tasks.registering(Jar::class) {
+ from(tasks.jar.map { zipTree(it.archiveFile) })
+ archiveClassifier = "jacoco"
+ enabled = project in mavenizedProjects
+ duplicatesStrategy = DuplicatesStrategy.INCLUDE
+}
+
+configurations.create("codeCoverageReportClasses") {
+ isCanBeResolved = false
+ isCanBeConsumed = true
+ isTransitive = false
+ attributes.attribute(LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements::class, CLASSES))
+ outgoing.artifact(codeCoverageClassesJar)
+}
diff --git a/buildSrc/src/main/kotlin/java-library-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts
similarity index 57%
rename from buildSrc/src/main/kotlin/java-library-conventions.gradle.kts
rename to gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts
index 6ebed2f22a2e..6eb7eb308ec8 100644
--- a/buildSrc/src/main/kotlin/java-library-conventions.gradle.kts
+++ b/gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts
@@ -1,9 +1,15 @@
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
+import junitbuild.java.ModuleCompileOptions
+import junitbuild.java.ModulePathArgumentProvider
+import junitbuild.java.PatchModuleArgumentProvider
+
plugins {
`java-library`
eclipse
idea
checkstyle
- id("custom-java-home")
+ id("junitbuild.base-conventions")
+ id("junitbuild.jacoco-java-conventions")
}
val mavenizedProjects: List by rootProject.extra
@@ -13,25 +19,13 @@ val buildTime: String by rootProject.extra
val buildRevision: Any by rootProject.extra
val builtByValue: String by rootProject.extra
-val internal by configurations.creating {
- isVisible = false
- isCanBeConsumed = false
- isCanBeResolved = false
-}
-
val extension = extensions.create("javaLibrary")
-val moduleSourceDir = file("src/module/$javaModuleName")
-val moduleOutputDir = file("$buildDir/classes/java/module")
+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()
-configurations {
- compileClasspath.get().extendsFrom(internal)
- runtimeClasspath.get().extendsFrom(internal)
- testCompileClasspath.get().extendsFrom(internal)
- testRuntimeClasspath.get().extendsFrom(internal)
-}
-
eclipse {
jdt {
file {
@@ -44,10 +38,14 @@ eclipse {
}
}
+java {
+ modularity.inferModulePath = false
+}
+
if (project in mavenizedProjects) {
- apply(plugin = "publishing-conventions")
- apply(plugin = "osgi-conventions")
+ apply(plugin = "junitbuild.publishing-conventions")
+ apply(plugin = "junitbuild.osgi-conventions")
java {
withJavadocJar()
@@ -61,13 +59,9 @@ if (project in mavenizedProjects) {
encoding = "UTF-8"
locale = "en"
(this as StandardJavadocDocletOptions).apply {
- addBooleanOption("Xdoclint:html,syntax", true)
+ addBooleanOption("Xdoclint:all,-missing,-reference", true)
+ addBooleanOption("XD-Xlint:none", true)
addBooleanOption("html5", true)
- // Javadoc 13 removed support for `--no-module-directories`
- // https://bugs.openjdk.java.net/browse/JDK-8215580
- if (javaVersion.isJava12 && executable == null) {
- addBooleanOption("-no-module-directories", true)
- }
addMultilineStringsOption("tag").value = listOf(
"apiNote:a:API Note:",
"implNote:a:Implementation Note:"
@@ -96,8 +90,6 @@ if (project in mavenizedProjects) {
val javaComponent = components["java"] as AdhocComponentWithVariants
javaComponent.withVariantsFromConfiguration(configurations["testFixturesApiElements"]) { skip() }
javaComponent.withVariantsFromConfiguration(configurations["testFixturesRuntimeElements"]) { skip() }
- configurations["testFixturesCompileClasspath"].extendsFrom(internal)
- configurations["testFixturesRuntimeClasspath"].extendsFrom(internal)
}
configure {
@@ -110,7 +102,7 @@ if (project in mavenizedProjects) {
}
}
pom {
- description.set(provider { "Module \"${project.name}\" of JUnit 5." })
+ description = provider { "Module \"${project.name}\" of JUnit 5." }
}
}
}
@@ -127,12 +119,25 @@ if (project in mavenizedProjects) {
}
}
+tasks.withType().configureEach {
+ isPreserveFileTimestamps = false
+ isReproducibleFileOrder = true
+ dirMode = Integer.parseInt("0755", 8)
+ fileMode = Integer.parseInt("0644", 8)
+}
+
normalization {
runtimeClasspath {
- // Ignore the JAR manifest when checking whether runtime classpath have changed
- // because it contains timestamps and the commit checksum. This is used when
- // checking whether a test task is up-to-date or can be loaded from the build cache.
- ignore("/META-INF/MANIFEST.MF")
+ metaInf {
+ // Ignore inconsequential JAR manifest attributes such as timestamps and the commit checksum.
+ // This is used when checking whether runtime classpaths, e.g. of test tasks, have changed and
+ // improves cacheability of such tasks.
+ ignoreAttribute("Built-By")
+ ignoreAttribute("Build-Date")
+ ignoreAttribute("Build-Time")
+ ignoreAttribute("Build-Revision")
+ ignoreAttribute("Created-By")
+ }
}
}
@@ -140,23 +145,45 @@ val allMainClasses by tasks.registering {
dependsOn(tasks.classes)
}
+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
+}
+
val compileModule by tasks.registering(JavaCompile::class) {
dependsOn(allMainClasses)
- source = fileTree(moduleSourceDir)
- destinationDir = moduleOutputDir
+ enabled = project in modularProjects
+ source = fileTree(combinedModuleSourceDir).builtBy(prepareModuleSourceDir)
+ destinationDirectory = moduleOutputDir
sourceCompatibility = "9"
targetCompatibility = "9"
classpath = files()
- options.release.set(9)
+ options.release = 9
options.compilerArgs.addAll(listOf(
- // "-verbose",
// Suppress warnings for automatic modules: org.apiguardian.api, org.opentest4j
"-Xlint:all,-requires-automatic,-requires-transitive-automatic",
+ "-Werror", // Terminates compilation when warnings occur.
"--module-version", "${project.version}",
- "--module-source-path", files(modularProjects.map { "${it.projectDir}/src/module" }).asPath
))
- options.compilerArgumentProviders.add(ModulePathArgumentProvider())
- options.compilerArgumentProviders.addAll(modularProjects.map { PatchModuleArgumentProvider(it) })
+
+ 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)
+ })
+ options.compilerArgumentProviders.addAll(modularProjects.map { objects.newInstance(PatchModuleArgumentProvider::class, project, it) })
+
+ modularity.inferModulePath = false
+
+ doFirst {
+ options.allCompilerArgs.forEach {
+ logger.info(it)
+ }
+ }
}
tasks.withType().configureEach {
@@ -165,9 +192,9 @@ tasks.withType().configureEach {
into("META-INF")
}
val suffix = archiveClassifier.getOrElse("")
- if (suffix.isBlank() || suffix == "all") { // "all" is used by shadow plugin
+ if (suffix.isBlank() || this is ShadowJar) {
dependsOn(allMainClasses, compileModule)
- from("$moduleOutputDir/$javaModuleName") {
+ from(moduleOutputDir.map { it.dir(javaModuleName) }) {
include("module-info.class")
}
}
@@ -191,6 +218,10 @@ tasks.jar {
}
}
+tasks.withType().configureEach {
+ outputs.doNotCacheIf("Shadow jar contains a Manifest with Build-Time") { true }
+}
+
tasks.withType().configureEach {
options.encoding = "UTF-8"
}
@@ -213,31 +244,6 @@ tasks.compileTestJava {
))
}
-inner class ModulePathArgumentProvider : CommandLineArgumentProvider {
- @get:Input val modulePath: Provider = configurations.compileClasspath
- override fun asArguments(): List = listOf("--module-path", modulePath.get().asPath)
-}
-
-inner class PatchModuleArgumentProvider(it: Project) : CommandLineArgumentProvider {
-
- @get:Input val module: String = it.javaModuleName
-
- @get:Input val patch: Provider = provider {
- if (it == project)
- files(sourceSets.matching { it.name.startsWith("main") }.map { it.output }) + configurations.compileClasspath.get()
- else
- files(it.sourceSets["main"].java.srcDirs)
- }
-
- override fun asArguments(): List