diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 000000000000..2ef85c0b65a5
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,69 @@
+# Configuration for probot-stale - https://github.com/probot/stale
+# Configuration options apply to both Issues and Pull Requests.
+# We configure those individually to match our workflow (see `pulls:` and `issues:`)
+
+pulls:
+ # Number of days of inactivity before a Pull Request becomes stale
+ daysUntilStale: 60
+
+ # Number of days of inactivity before a Pull Request with the stale label is closed.
+ # Set to false to disable. If disabled, Pull Request still need to be closed manually, but will remain marked as stale.
+ daysUntilClose: 21
+
+ # Comment to post when marking as stale. Set to `false` to disable
+ markComment: >
+ This pull request has been automatically marked as stale because it has not had recent activity.
+ Given the limited bandwidth of the team, it will be closed if no further activity occurs.
+ If you intend to work on this pull request, please reopen the PR.
+ Thank you for your contributions.
+ # Comment to post when closing a stale Pull Request.
+ closeComment: >
+ This pull request has been automatically closed due to inactivity.
+ If you are still interested in contributing this, please ensure that
+ it is rebased against the latest branch (usually `main`), all review
+ comments have been addressed and the build is passing.
+issues:
+ daysUntilStale: 365
+
+ # Number of days of inactivity before an Issue with the stale label is closed.
+ # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
+ daysUntilClose: 21
+
+ # Comment to post when marking as stale. Set to `false` to disable
+ markComment: >
+ This issue has been automatically marked as stale because it has not had recent activity.
+ Given the limited bandwidth of the team, it will be automatically closed if no further
+ activity occurs.
+ Thank you for your contribution.
+ # Comment to post when closing a stale Issue.
+ closeComment: >
+ This issue has been automatically closed due to inactivity. If you have a good use case for this
+ feature, please feel free to reopen the issue.
+
+# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
+#onlyLabels: []
+
+# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
+exemptLabels: []
+
+# Set to true to ignore issues in a project (defaults to false)
+exemptProjects: false
+
+# Set to true to ignore issues in a milestone (defaults to false)
+exemptMilestones: false
+
+# Set to true to ignore issues with an assignee (defaults to false)
+exemptAssignees: false
+
+# Label to use when marking as stale
+staleLabel: "status: stale"
+
+# Comment to post when removing the stale label.
+# unmarkComment: >
+# Your comment here.
+
+# Limit the number of actions per hour, from 1-30. Default is 30
+limitPerRun: 30
+
+# Limit to only `issues` or `pulls`
+# only: issues
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 000000000000..90ee694cea43
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,42 @@
+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
+ strategy:
+ fail-fast: false
+ matrix:
+ language: ['java', 'javascript']
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ - name: Set up JDK 11
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ cache: 'gradle'
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: ${{ matrix.language }}
+ - name: Build
+ run: |
+ ./gradlew --version
+ ./gradlew javaToolchains -Porg.gradle.java.installations.auto-download=false
+ ./gradlew --no-build-cache allMainClasses -PjavaToolchainVersion=11 -Porg.gradle.java.installations.auto-download=false
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/cross-version.yml b/.github/workflows/cross-version.yml
index 05e5a3d5c08e..e170649a9aca 100644
--- a/.github/workflows/cross-version.yml
+++ b/.github/workflows/cross-version.yml
@@ -12,38 +12,56 @@ on:
env:
ORG_GRADLE_PROJECT_junitBuildCacheUsername: ${{ secrets.BUILD_CACHE_USERNAME }}
ORG_GRADLE_PROJECT_junitBuildCachePassword: ${{ secrets.BUILD_CACHE_PASSWORD }}
+ GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
jobs:
openjdk:
strategy:
matrix:
- jdk: [14, 15, 16]
+ jdk: [17, 18]
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
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 JDK 8
+ uses: actions/setup-java@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
+ distribution: 'temurin'
+ java-version: '8'
+ - name: Prepare JDK8 env var
+ shell: bash
+ run: echo "JDK8=$JAVA_HOME" >> $GITHUB_ENV
+ - name: 'Download JDK ${{ matrix.jdk }}'
+ id: download-jdk
+ uses: sormuras/download-jdk@v1
+ with:
+ feature: '${{ matrix.jdk }}'
+ - name: 'Set up JDK ${{ matrix.jdk }}'
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'jdkfile'
+ java-version: '${{ steps.download-jdk.outputs.version }}'
+ jdkFile: '${{ steps.download-jdk.outputs.file }}'
+ - name: 'Prepare JDK${{ matrix.jdk }} env var'
+ shell: bash
+ run: echo "JDK${{ matrix.jdk }}=$JAVA_HOME" >> $GITHUB_ENV
+ - name: Set up JDK 11
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ cache: 'gradle'
- name: Test
+ env:
+ ORG_GRADLE_PROJECT_enableTestDistribution: true
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 }}"
+ ./gradlew javaToolchains -Porg.gradle.java.installations.auto-download=false
+ ./gradlew --no-parallel -Dplatform.tooling.support.tests.enabled=true -PjavaToolchainVersion=${{ matrix.jdk }} -Porg.gradle.java.installations.auto-download=false build "-Dscan.tag.JDK_${{ matrix.jdk }}"
+ - name: Upload Test Distribution trace files
+ uses: actions/upload-artifact@v2
+ with:
+ name: Test Distribution trace files
+ path: '**/build/test-results/*/trace.json'
diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml
index 405a2b306592..ba7ceef98a0a 100644
--- a/.github/workflows/gradle-wrapper-validation.yml
+++ b/.github/workflows/gradle-wrapper-validation.yml
@@ -7,4 +7,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
+ with:
+ fetch-depth: 1
- 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..3dadaf6b4ad6
--- /dev/null
+++ b/.github/workflows/issue-labels.yml
@@ -0,0 +1,14 @@
+name: Label new issues
+on:
+ issues:
+ types: ['opened']
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: Renato66/auto-label@69b3cbe79438b2079aed0a49474275d3e572cae5 # or v2
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ labels-synonyms: '{"3rd-party: Eclipse":["Eclipse"],"3rd-party: Gradle":["Gradle"],"3rd-party: IntelliJ IDEA":["IDEA","IntelliJ"],"3rd-party: Maven Surefire":["Failsafe","Maven","Surefire"],"3rd-party: Pioneer":["pioneer"],"component: Groovy":["Groovy"],"component: Test Kit":["Test Kit","TestKit"]}'
+ labels-not-allowed: '["dependencies","status: blocked","status: declined","status: duplicate","status: in progress","status: invalid","status: stale","status: superseded","status: team discussion","status: waiting-for-feedback","status: waiting-for-interest","status: works-as-designed","up-for-grabs"]'
+ default-labels: '["status: new"]'
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 2a4b5da0d7e0..966a2ca7ae30 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -12,151 +12,125 @@ on:
env:
ORG_GRADLE_PROJECT_junitBuildCacheUsername: ${{ secrets.BUILD_CACHE_USERNAME }}
ORG_GRADLE_PROJECT_junitBuildCachePassword: ${{ secrets.BUILD_CACHE_PASSWORD }}
+ GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
jobs:
linux:
- name: 'Linux'
+ name: Linux
runs-on: ubuntu-latest
- container: junitteam/build: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: 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'
+ with:
+ # Codecov needs fetch-depth > 1
+ fetch-depth: 2
+ - name: Set up JDK 8
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '8'
+ - name: Prepare JDK8 env var
+ shell: bash
+ run: echo "JDK8=$JAVA_HOME" >> $GITHUB_ENV
+ - name: Set up JDK 16
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '16'
+ - name: Prepare JDK16 env var
+ shell: bash
+ run: echo "JDK16=$JAVA_HOME" >> $GITHUB_ENV
+ - name: Set up JDK 11
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ cache: 'gradle'
+ - name: Test and coverage
+ env:
+ ORG_GRADLE_PROJECT_enableTestDistribution: true
run: |
./gradlew --version
- ./gradlew --scan --no-parallel --warning-mode=all -Dplatform.tooling.support.tests.enabled=true build
+ ./gradlew javaToolchains -Porg.gradle.java.installations.auto-download=false
+ ./gradlew --no-parallel --stacktrace -Dplatform.tooling.support.tests.enabled=true -PenableJaCoCo -Porg.gradle.java.installations.auto-download=false build jacocoRootReport
+ - name: Upload Test Distribution trace files
+ uses: actions/upload-artifact@v2
+ with:
+ name: Test Distribution trace files
+ path: '**/build/test-results/*/trace.json'
+ - name: Upload to Codecov.io
+ uses: codecov/codecov-action@v1
windows:
- name: '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
- 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
+ with:
+ fetch-depth: 1
+ - name: Set up JDK 8
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '8'
+ - name: Prepare JDK8 env var
shell: bash
- run: |
- mkdir -p $HOME/.gradle/enterprise/
- echo "${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}" > $HOME/.gradle/enterprise/keys.properties
- - name: 'Test'
+ run: echo "JDK8=$JAVA_HOME" >> $GITHUB_ENV
+ - name: Set up JDK 16
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '16'
+ - name: Prepare JDK16 env var
+ shell: bash
+ run: echo "JDK16=$JAVA_HOME" >> $GITHUB_ENV
+ - name: Set up JDK 11
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ cache: 'gradle'
+ - name: Test
shell: bash
run: |
./gradlew --version
- ./gradlew --scan --no-parallel --warning-mode=all -Dplatform.tooling.support.tests.enabled=true build
+ ./gradlew javaToolchains -Porg.gradle.java.installations.auto-download=false
+ ./gradlew --no-parallel --stacktrace -Dplatform.tooling.support.tests.enabled=true -Porg.gradle.java.installations.auto-download=false build
./gradlew --stop
+
mac:
- name: 'Mac OS'
+ name: macOS
runs-on: macos-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
- 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
-
- coverage:
- name: 'Coverage'
- needs: linux
- runs-on: ubuntu-latest
- container: junitteam/build: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
- 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'
+ with:
+ fetch-depth: 1
+ - name: Set up JDK 8
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '8'
+ - name: Prepare JDK8 env var
shell: bash
- run: |
- ./gradlew --version
- ./gradlew --scan --no-parallel --stacktrace --warning-mode=all -PenableJaCoCo build jacocoRootReport
- - name: Upload to Codecov.io
+ run: echo "JDK8=$JAVA_HOME" >> $GITHUB_ENV
+ - name: Set up JDK 16
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '16'
+ - name: Prepare JDK16 env var
shell: bash
- env:
- CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+ run: echo "JDK16=$JAVA_HOME" >> $GITHUB_ENV
+ - name: Set up JDK 11
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ cache: 'gradle'
+ - name: 'Test'
run: |
- bash <(curl -s https://codecov.io/bash)
+ ./gradlew --version
+ ./gradlew javaToolchains -Porg.gradle.java.installations.auto-download=false
+ ./gradlew --no-parallel --stacktrace -Dplatform.tooling.support.tests.enabled=true -Porg.gradle.java.installations.auto-download=false build
publish_artifacts:
name: Publish Snapshot Artifacts
@@ -165,25 +139,29 @@ jobs:
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
- with:
- java-version: 11
- - name: 'Publish'
+ with:
+ fetch-depth: 1
+ - name: Set up JDK 16
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '16'
+ - name: Prepare JDK16 env var
+ shell: bash
+ run: echo "JDK16=$JAVA_HOME" >> $GITHUB_ENV
+ - name: Set up JDK 11
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ cache: 'gradle'
+ - name: Publish
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
+ run: |
+ ./gradlew javaToolchains -Porg.gradle.java.installations.auto-download=false
+ ./gradlew publish -x check -Porg.gradle.java.installations.auto-download=false
update_documentation:
name: Update Snapshot Documentation
@@ -192,23 +170,28 @@ jobs:
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
- 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
- with:
- java-version: 11
- - name: 'Upload Documentation'
+ with:
+ fetch-depth: 1
+ - name: Set up JDK 16
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '16'
+ - name: Prepare JDK16 env var
+ shell: bash
+ run: echo "JDK16=$JAVA_HOME" >> $GITHUB_ENV
+ - name: Set up JDK 11
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ cache: 'gradle'
+ - name: Install Graphviz
+ run: |
+ sudo apt-get update
+ sudo apt-get install graphviz
+ - 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: ./src/publishDocumentationSnapshotOnlyIfNecessary.sh
diff --git a/.github/workflows/reproducible-build.yml b/.github/workflows/reproducible-build.yml
index e48b0c5ff1b2..ead579aa6094 100644
--- a/.github/workflows/reproducible-build.yml
+++ b/.github/workflows/reproducible-build.yml
@@ -9,31 +9,33 @@ on:
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
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: Set up JDK 16
+ uses: actions/setup-java@v2
with:
- java-version: 11
- - name: Prepare Gradle Enterprise credentials
+ distribution: 'temurin'
+ java-version: '16'
+ - name: Prepare JDK16 env var
shell: bash
- run: |
- mkdir -p $HOME/.gradle/enterprise/
- echo "${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}" > $HOME/.gradle/enterprise/keys.properties
+ run: echo "JDK16=$JAVA_HOME" >> $GITHUB_ENV
+ - name: Set up JDK 11
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ cache: 'gradle'
- name: Build and compare checksums
shell: bash
run: |
+ ./gradlew javaToolchains -Porg.gradle.java.installations.auto-download=false
./src/checkBuildReproducibility.sh
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index f77f7d2313e4..fb8322da259f 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,5 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/.jitpack.yml b/.jitpack.yml
index 72eb09e6aa7f..39c04bae497c 100644
--- a/.jitpack.yml
+++ b/.jitpack.yml
@@ -1,5 +1,5 @@
install:
- - wget https://github.com/sormuras/bach/raw/HEAD/install-jdk.sh
- - source ./install-jdk.sh --feature 11
+ - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh
+ - source ./install-jdk.sh --feature 15
- ./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..e2a44b38b935 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -87,6 +87,11 @@ 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.
@@ -127,8 +132,8 @@ See [`ExtensionContext`](junit-jupiter-api/src/main/java/org/junit/jupiter/api/e
### 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
diff --git a/README.md b/README.md
index 8b6a8761c39d..3a54814570e8 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ This repository is the home of the next generation of JUnit, _JUnit 5_.
## Latest Releases
-- General Availability (GA): [JUnit 5.7.0](https://github.com/junit-team/junit5/releases/tag/r5.7.0) (September 13, 2020)
+- General Availability (GA): [JUnit 5.8.2](https://github.com/junit-team/junit5/releases/tag/r5.8.2) (November 28, 2021)
- 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
@@ -48,6 +48,8 @@ in `build/reports/jacoco/jacocoRootReport/html/index.html`.
## Gradle Build Scans and Build Caching
+[](https://ge.junit.org/scans)
+
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
@@ -56,7 +58,8 @@ so that local builds can reuse task outputs from previous CI builds.
## Building from Source
-You need [JDK 11] to build JUnit 5.
+You need [JDK 11] to build JUnit 5. [Gradle toolchains] are used to detect and
+potentially download additional JDKs for compilation and test execution.
All modules can be _built_ with the [Gradle Wrapper] using the following command.
@@ -78,60 +81,23 @@ consumption in other projects via the following command.
## 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
-
-- **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 11]: https://adoptopenjdk.net/archive.html?variant=openjdk11&jvmVariant=hotspot
[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..0e51f7ff7479
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,12 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported |
+| ------- | ------------------ |
+| 5.7.x | :white_check_mark: |
+| < 5.7 | :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..b20a290e8a05 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,17 +8,10 @@ plugins {
id("com.github.ben-manes.versions") // gradle dependencyUpdates
id("com.diffplug.spotless")
id("io.spring.nohttp")
+ id("io.github.gradle-nexus.publish-plugin")
}
-apply(from = "gradle/build-scan-user-data.gradle")
-
-buildScan {
- if (project.hasProperty("javaHome")) {
- value("Custom Java home", project.property("javaHome") as String)
- }
-}
-
-val buildTimeAndDate by extra {
+val buildTimeAndDate: OffsetDateTime by extra {
// SOURCE_DATE_EPOCH is a UNIX timestamp for pinning build metadata against
// in order to achieve reproducible builds
@@ -36,38 +29,41 @@ val buildTimeAndDate by extra {
}
}
-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 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 { versioning.info.commit }
val builtByValue by extra { project.findProperty("builtBy") ?: project.property("defaultBuiltBy") }
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 modularProjects by extra(mavenizedProjects - listOf(projects.junitPlatformConsoleStandalone.dependencyProject))
val license by extra(License(
name = "Eclipse Public License v2.0",
@@ -80,16 +76,23 @@ 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")
-)
+ projects.junitJupiterEngine,
+ projects.junitJupiterMigrationsupport,
+ projects.junitJupiterParams,
+ projects.junitPlatformRunner,
+ projects.junitVintageEngine,
+ projects.platformTests
+).map { it.dependencyProject }
val jacocoCoveredProjects = modularProjects
val jacocoClassesDir = file("$buildDir/jacoco/classes")
+nexusPublishing {
+ packageGroup.set("org.junit")
+ repositories {
+ sonatype()
+ }
+}
+
allprojects {
apply(plugin = "eclipse")
@@ -99,12 +102,11 @@ allprojects {
if (enableJaCoCo) {
apply(plugin = "jacoco")
configure {
- toolVersion = versions["jacoco"]
+ toolVersion = rootProject.libs.versions.jacoco.get()
}
}
repositories {
- // mavenLocal()
mavenCentral()
maven(url = "https://oss.sonatype.org/content/repositories/snapshots") {
mavenContent {
@@ -114,18 +116,26 @@ allprojects {
}
}
+val clearTempRepoDir by tasks.registering {
+ doFirst {
+ tempRepoDir.deleteRecursively()
+ }
+}
+
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")!!
+ when (project) {
+ in jupiterProjects -> {
+ group = property("jupiterGroup")!!
+ }
+ in platformProjects -> {
+ group = property("platformGroup")!!
+ version = property("platformVersion")!!
+ }
+ in vintageProjects -> {
+ group = property("vintageGroup")!!
+ version = property("vintageVersion")!!
+ }
}
tasks.withType().configureEach {
@@ -146,13 +156,17 @@ subprojects {
licenseHeaderFile(headerFile, "(package|import|open|module) ")
importOrderFile(importOrderConfigFile)
eclipse().configFile(javaFormatterConfigFile)
- removeUnusedImports()
+ if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_15)) {
+ // Doesn't work with Java 15 text blocks, see https://github.com/diffplug/spotless/issues/713
+ removeUnusedImports()
+ }
trimTrailingWhitespace()
endWithNewline()
}
kotlin {
- ktlint(versions["ktlint"])
+ targetExclude("**/src/test/resources/**")
+ ktlint(libs.versions.ktlint.get())
licenseHeaderFile(headerFile)
trimTrailingWhitespace()
endWithNewline()
@@ -160,20 +174,42 @@ subprojects {
}
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 }
+ if (enableJaCoCo) {
+ if (project in jacocoCoveredProjects) {
+ val jarTask = (tasks.findByName("shadowJar") ?: tasks["jar"]) as Jar
+ val extractJar by tasks.registering(Copy::class) {
+ from(zipTree(jarTask.archiveFile))
+ 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)
+ }
+ if (project in jacocoTestProjects) {
+ tasks.named("test") {
+ finalizedBy("jacocoTestReport")
+ }
+ tasks.named("jacocoTestReport").configure {
+ dependsOn("test")
+ reports {
+ html.isEnabled = true
+ xml.isEnabled = true
+ csv.isEnabled = false
+ }
+ jacocoCoveredProjects.forEach {
+ dependsOn(it.tasks.named("extractJar"))
+ it.pluginManager.withPlugin("java") {
+ sourceDirectories.setFrom(it.the()["main"].allSource.srcDirs)
+ }
+ }
+ classDirectories.setFrom(files(jacocoClassesDir))
+ }
}
- jarTask.finalizedBy(extractJar)
}
}
}
@@ -189,6 +225,11 @@ subprojects {
}
}
}
+ tasks.withType().configureEach {
+ if (name.endsWith("To${tempRepoName.capitalize()}Repository")) {
+ dependsOn(clearTempRepoDir)
+ }
+ }
}
}
@@ -245,6 +286,7 @@ rootProject.apply {
register("jacocoRootReport") {
dependsOn(jacocoMerge)
jacocoCoveredProjects.forEach {
+ dependsOn(it.tasks.named("extractJar"))
it.pluginManager.withPlugin("java") {
sourceDirectories.from(it.the()["main"].allSource.srcDirs)
}
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index fd74ea278a13..863aec0863ab 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -1,3 +1,5 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
plugins {
`kotlin-dsl`
}
@@ -9,8 +11,14 @@ repositories {
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")
+ implementation("biz.aQute.bnd:biz.aQute.bnd.gradle:5.3.0")
+ implementation("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0")
+ implementation("org.gradle:test-retry-gradle-plugin:1.2.1")
+ compileOnly("com.gradle.enterprise:test-distribution-gradle-plugin:2.1.1")
+}
+
+tasks.withType().configureEach {
+ kotlinOptions {
+ allWarningsAsErrors = true
+ }
}
diff --git a/buildSrc/src/main/kotlin/JavaLibraryExtension.kt b/buildSrc/src/main/kotlin/JavaLibraryExtension.kt
index 7c020478d6c3..ef33ff62d670 100644
--- a/buildSrc/src/main/kotlin/JavaLibraryExtension.kt
+++ b/buildSrc/src/main/kotlin/JavaLibraryExtension.kt
@@ -2,6 +2,7 @@ import org.gradle.api.JavaVersion
@Suppress("UnstableApiUsage")
open class JavaLibraryExtension {
- var mainJavaVersion: JavaVersion = Versions.jvmTarget
- var testJavaVersion: JavaVersion = JavaVersion.VERSION_11
+ var mainJavaVersion: JavaVersion = JavaVersion.VERSION_1_8
+ var testJavaVersion: JavaVersion = JavaVersion.VERSION_16
+ var configureRelease: Boolean = true
}
diff --git a/buildSrc/src/main/kotlin/ProjectExtensions.kt b/buildSrc/src/main/kotlin/ProjectExtensions.kt
index d6d2f4215049..315126c5bae3 100644
--- a/buildSrc/src/main/kotlin/ProjectExtensions.kt
+++ b/buildSrc/src/main/kotlin/ProjectExtensions.kt
@@ -4,9 +4,3 @@ 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/TaskExtensions.kt b/buildSrc/src/main/kotlin/TaskExtensions.kt
new file mode 100644
index 000000000000..7ad4e7ab46cb
--- /dev/null
+++ b/buildSrc/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/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-library-conventions.gradle.kts b/buildSrc/src/main/kotlin/java-library-conventions.gradle.kts
index 6ebed2f22a2e..e26140159719 100644
--- a/buildSrc/src/main/kotlin/java-library-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/java-library-conventions.gradle.kts
@@ -1,9 +1,12 @@
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
+import org.gradle.api.tasks.PathSensitivity.RELATIVE
+
plugins {
`java-library`
eclipse
idea
checkstyle
- id("custom-java-home")
+ id("java-toolchain-conventions")
}
val mavenizedProjects: List by rootProject.extra
@@ -13,25 +16,12 @@ 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 javaVersion = JavaVersion.current()
-configurations {
- compileClasspath.get().extendsFrom(internal)
- runtimeClasspath.get().extendsFrom(internal)
- testCompileClasspath.get().extendsFrom(internal)
- testRuntimeClasspath.get().extendsFrom(internal)
-}
-
eclipse {
jdt {
file {
@@ -44,6 +34,10 @@ eclipse {
}
}
+java {
+ modularity.inferModulePath.set(false)
+}
+
if (project in mavenizedProjects) {
apply(plugin = "publishing-conventions")
@@ -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 {
@@ -129,10 +121,16 @@ if (project in mavenizedProjects) {
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")
+ }
}
}
@@ -143,20 +141,21 @@ val allMainClasses by tasks.registering {
val compileModule by tasks.registering(JavaCompile::class) {
dependsOn(allMainClasses)
source = fileTree(moduleSourceDir)
- destinationDir = moduleOutputDir
+ destinationDirectory.set(moduleOutputDir)
sourceCompatibility = "9"
targetCompatibility = "9"
classpath = files()
options.release.set(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) })
+ modularity.inferModulePath.set(false)
}
tasks.withType().configureEach {
@@ -165,7 +164,7 @@ 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") {
include("module-info.class")
@@ -213,18 +212,24 @@ 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 ModulePathArgumentProvider : CommandLineArgumentProvider, Named {
+ @get:CompileClasspath
+ val modulePath: Provider = configurations.compileClasspath
+ override fun asArguments() = listOf("--module-path", modulePath.get().asPath)
+ @Internal
+ override fun getName() = "module-path"
}
-inner class PatchModuleArgumentProvider(it: Project) : CommandLineArgumentProvider {
+inner class PatchModuleArgumentProvider(it: Project) : CommandLineArgumentProvider, Named {
- @get:Input val module: String = it.javaModuleName
+ @get:Input
+ val module: String = it.javaModuleName
- @get:Input val patch: Provider = provider {
+ @get:InputFiles
+ @get:PathSensitive(RELATIVE)
+ val patch: Provider = provider {
if (it == project)
- files(sourceSets.matching { it.name.startsWith("main") }.map { it.output }) + configurations.compileClasspath.get()
+ files(sourceSets.matching { it.name.startsWith("main") }.map { it.output })
else
files(it.sourceSets["main"].java.srcDirs)
}
@@ -236,6 +241,9 @@ inner class PatchModuleArgumentProvider(it: Project) : CommandLineArgumentProvid
}
return listOf("--patch-module", "$module=$path")
}
+
+ @Internal
+ override fun getName() = "patch-module($module)"
}
afterEvaluate {
@@ -253,26 +261,45 @@ afterEvaluate {
}
tasks {
compileJava {
- options.release.set(extension.mainJavaVersion.majorVersion.toInt())
+ if (extension.configureRelease) {
+ options.release.set(extension.mainJavaVersion.majorVersion.toInt())
+ } else {
+ sourceCompatibility = extension.mainJavaVersion.majorVersion
+ targetCompatibility = extension.mainJavaVersion.majorVersion
+ }
}
compileTestJava {
- options.release.set(extension.testJavaVersion.majorVersion.toInt())
+ if (extension.configureRelease) {
+ options.release.set(extension.testJavaVersion.majorVersion.toInt())
+ } else {
+ sourceCompatibility = extension.testJavaVersion.majorVersion
+ targetCompatibility = extension.testJavaVersion.majorVersion
+ }
}
}
pluginManager.withPlugin("groovy") {
tasks.named("compileGroovy").configure {
- sourceCompatibility = extension.mainJavaVersion.majorVersion
- targetCompatibility = extension.mainJavaVersion.majorVersion
+ if (extension.configureRelease) {
+ options.release.set(extension.mainJavaVersion.majorVersion.toInt())
+ } else {
+ sourceCompatibility = extension.mainJavaVersion.majorVersion
+ targetCompatibility = extension.mainJavaVersion.majorVersion
+ }
}
tasks.named("compileTestGroovy").configure {
- sourceCompatibility = extension.testJavaVersion.majorVersion
- targetCompatibility = extension.testJavaVersion.majorVersion
+ if (extension.configureRelease) {
+ options.release.set(extension.testJavaVersion.majorVersion.toInt())
+ } else {
+ sourceCompatibility = extension.testJavaVersion.majorVersion
+ targetCompatibility = extension.testJavaVersion.majorVersion
+ }
}
}
}
checkstyle {
- toolVersion = versions["checkstyle"]
+ val libs = project.extensions["libs"] as VersionCatalog
+ toolVersion = libs.findVersion("checkstyle").get().requiredVersion
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -286,7 +313,10 @@ tasks {
}
pluginManager.withPlugin("java-test-fixtures") {
- tasks.named("checkstyleTestFixtures").configure {
+ tasks.named("checkstyleTestFixtures") {
configFile = rootProject.file("src/checkstyle/checkstyleTest.xml")
}
+ tasks.named("compileTestFixturesJava") {
+ options.release.set(extension.testJavaVersion.majorVersion.toInt())
+ }
}
diff --git a/buildSrc/src/main/kotlin/java-repackage-jars.gradle.kts b/buildSrc/src/main/kotlin/java-repackage-jars.gradle.kts
index e36bea40b0fa..a000989b0327 100644
--- a/buildSrc/src/main/kotlin/java-repackage-jars.gradle.kts
+++ b/buildSrc/src/main/kotlin/java-repackage-jars.gradle.kts
@@ -1,9 +1,8 @@
-import java.util.Calendar
-import java.util.GregorianCalendar
import java.util.jar.JarEntry
import java.util.jar.JarFile
import java.util.jar.JarOutputStream
import org.gradle.api.internal.file.archive.ZipCopyAction
+import java.nio.file.Files
// This registers a `doLast` action to rewrite the timestamps of the project's output JAR
afterEvaluate {
@@ -11,18 +10,29 @@ afterEvaluate {
jarTask.doLast {
- val newFile = createTempFile("rewrite-timestamp")
- val originalOutput = jarTask.archiveFile.get().getAsFile()
+ val newFile = Files.createTempFile("rewrite-timestamp", null).toFile()
+ val originalOutput = jarTask.archiveFile.get().asFile
newFile.outputStream().use { os ->
val newJarStream = JarOutputStream(os)
val oldJar = JarFile(originalOutput)
+ fun sortAlwaysFirst(name: String): Comparator =
+ Comparator { a, b ->
+ when {
+ a.name == name -> -1
+ b.name == name -> 1
+ else -> 0
+ }
+ }
+
oldJar.entries()
.toList()
.distinctBy { it.name }
- .sortedBy { it.name }
+ .sortedWith(sortAlwaysFirst("META-INF/")
+ .then(sortAlwaysFirst("META-INF/MANIFEST.MF"))
+ .thenBy { it.name })
.forEach { entry ->
val jarEntry = JarEntry(entry.name)
diff --git a/buildSrc/src/main/kotlin/java-toolchain-conventions.gradle.kts b/buildSrc/src/main/kotlin/java-toolchain-conventions.gradle.kts
new file mode 100644
index 000000000000..399e2e23c9e0
--- /dev/null
+++ b/buildSrc/src/main/kotlin/java-toolchain-conventions.gradle.kts
@@ -0,0 +1,23 @@
+import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile
+
+val javaToolchainVersion: String? by project
+val defaultLanguageVersion = JavaLanguageVersion.of(16)
+val javaLanguageVersion = javaToolchainVersion?.let { JavaLanguageVersion.of(it) } ?: defaultLanguageVersion
+
+project.pluginManager.withPlugin("java") {
+ val extension = the()
+ val javaToolchainService = the()
+ extension.toolchain.languageVersion.set(javaLanguageVersion)
+ val compiler = javaToolchainService.compilerFor(extension.toolchain)
+ tasks.withType().configureEach {
+ doFirst {
+ kotlinOptions.jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
+ }
+ }
+ tasks.withType().configureEach {
+ javaLauncher.set(javaToolchainService.launcherFor(extension.toolchain))
+ }
+ tasks.withType().configureEach {
+ outputs.cacheIf { javaLanguageVersion == defaultLanguageVersion }
+ }
+}
diff --git a/buildSrc/src/main/kotlin/junit4-compatibility.gradle.kts b/buildSrc/src/main/kotlin/junit4-compatibility.gradle.kts
index deb9fdced557..70a5072cb015 100644
--- a/buildSrc/src/main/kotlin/junit4-compatibility.gradle.kts
+++ b/buildSrc/src/main/kotlin/junit4-compatibility.gradle.kts
@@ -13,7 +13,9 @@ dependencies {
}
}
pluginManager.withPlugin("osgi-conventions") {
- "osgiVerification"("org.apache.servicemix.bundles:org.apache.servicemix.bundles.junit:4.13_1")
+ val libs = project.extensions["libs"] as VersionCatalog
+ val junit4Osgi = libs.findVersion("junit4Osgi").get().requiredVersion
+ "osgiVerification"("org.apache.servicemix.bundles:org.apache.servicemix.bundles.junit:${junit4Osgi}")
}
}
diff --git a/buildSrc/src/main/kotlin/kotlin-library-conventions.gradle.kts b/buildSrc/src/main/kotlin/kotlin-library-conventions.gradle.kts
index 3342cd189501..1b05c6e0fd4f 100644
--- a/buildSrc/src/main/kotlin/kotlin-library-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/kotlin-library-conventions.gradle.kts
@@ -7,8 +7,9 @@ plugins {
tasks.withType().configureEach {
kotlinOptions {
- jvmTarget = Versions.jvmTarget.toString()
+ jvmTarget = "1.8"
apiVersion = "1.3"
languageVersion = "1.3"
+ allWarningsAsErrors = false
}
}
diff --git a/buildSrc/src/main/kotlin/osgi-conventions.gradle.kts b/buildSrc/src/main/kotlin/osgi-conventions.gradle.kts
index 8b8f1705dc7f..bc4197a5e6bd 100644
--- a/buildSrc/src/main/kotlin/osgi-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/osgi-conventions.gradle.kts
@@ -6,6 +6,8 @@ plugins {
`java-library`
}
+val importAPIGuardian = "org.apiguardian.*;resolution:=\"optional\""
+
// This task enhances `jar` and `shadowJar` tasks with the bnd
// `BundleTaskConvention` convention which allows for generating OSGi
// metadata into the jar
@@ -14,20 +16,31 @@ tasks.withType().matching {
}.configureEach {
val btc = BundleTaskConvention(this)
+ extra["importAPIGuardian"] = importAPIGuardian
+
// 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("""
+ # Set the Bundle-SymbolicName to the archiveBaseName.
+ # We don't use the archiveClassifier which Bnd will use
+ # in the default Bundle-SymbolicName value.
+ Bundle-SymbolicName: ${'$'}{task.archiveBaseName}
+
+ # Set the Bundle-Name from the project description
+ Bundle-Name: ${'$'}{project.description}
+
# These are the general rules for package imports.
Import-Package: \
- !org.apiguardian.api,\
+ ${importAPIGuardian},\
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.
+ # the kotlin and apiguardian packages, but enough modules do to make it a default.
-fixupmessages.kotlin.import: "Unused Import-Package instructions: \\[kotlin.*\\]";is:=ignore
+ -fixupmessages.apiguardian.import: "Unused Import-Package instructions: \\[org.apiguardian.*\\]";is:=ignore
# This tells bnd to ignore classes it finds in `META-INF/versions/`
# because bnd doesn't yet support multi-release jars.
@@ -47,10 +60,11 @@ tasks.withType().matching {
# Instruct the APIGuardianAnnotations how to operate.
# See https://bnd.bndtools.org/instructions/export-apiguardian.html
- -export-apiguardian: *;version=${project.version}
+ -export-apiguardian: *;version=${'$'}{versionmask;===;${'$'}{version_cleanup;${'$'}{task.archiveVersion}}}
""")
// Add the convention to the jar task
+ @Suppress("deprecation") // https://github.com/bndtools/bnd/issues/4699
convention.plugins["bundle"] = btc
doLast {
@@ -71,7 +85,10 @@ val osgiProperties by tasks.registering(WriteProperties::class) {
property("-runee", "JavaSE-${javaLibrary.mainJavaVersion}")
}
property("-runrequires", "osgi.identity;filter:='(osgi.identity=${project.name})'")
- property("-runsystempackages", "jdk.internal.misc,sun.misc")
+ property("-runsystempackages", "jdk.internal.misc,jdk.jfr,sun.misc")
+ // API Guardian should be optional -> instruct resolver to ignore it
+ // during resolution. Resolve should still pass.
+ property("-runblacklist", "org.apiguardian.api")
}
val osgiVerification by configurations.creating {
@@ -85,6 +102,7 @@ val verifyOSGi by tasks.registering(Resolve::class) {
dependsOn(osgiProperties)
setBndrun(osgiPropertiesFile)
isReportOptional = false
+ @Suppress("deprecation") // https://github.com/bndtools/bnd/issues/4699
withConvention(FileSetRepositoryConvention::class) {
// By default bnd will use jars found in:
@@ -101,12 +119,3 @@ val verifyOSGi by tasks.registering(Resolve::class) {
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
index 6f58ce7453af..1acbaf8e5841 100644
--- a/buildSrc/src/main/kotlin/publishing-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/publishing-conventions.gradle.kts
@@ -1,9 +1,6 @@
-import java.time.Duration
-
plugins {
`maven-publish`
signing
- id("de.marcphilipp.nexus-publish")
}
val isSnapshot = project.version.toString().contains("SNAPSHOT")
@@ -12,7 +9,14 @@ val isJitPackEnvironment = System.getenv("JITPACK")?.toBoolean() ?: false
// ensure project is built successfully before publishing it
tasks.withType().configureEach {
- dependsOn(tasks.build)
+ dependsOn(provider {
+ val tempRepoName: String by rootProject
+ if (repository.name != tempRepoName) {
+ listOf(tasks.build)
+ } else {
+ emptyList()
+ }
+ })
}
tasks.withType().configureEach {
dependsOn(tasks.build)
@@ -29,15 +33,6 @@ tasks.withType().configureEach {
}
}
-nexusPublishing {
- connectTimeout.set(Duration.ofMinutes(2))
- clientTimeout.set(Duration.ofMinutes(2))
- packageGroup.set("org.junit")
- repositories {
- sonatype()
- }
-}
-
publishing {
publications {
create("maven") {
diff --git a/buildSrc/src/main/kotlin/shadow-conventions.gradle.kts b/buildSrc/src/main/kotlin/shadow-conventions.gradle.kts
index 0a5d8db24510..44c5bacec51d 100644
--- a/buildSrc/src/main/kotlin/shadow-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/shadow-conventions.gradle.kts
@@ -3,9 +3,7 @@ plugins {
id("com.github.johnrengelman.shadow")
}
-val shadowed by configurations.creating {
- extendsFrom(configurations["internal"])
-}
+val shadowed by configurations.creating
configurations.forEach { configuration ->
configuration.outgoing.apply {
@@ -49,6 +47,8 @@ tasks {
shadowJar {
configurations = listOf(shadowed)
exclude("META-INF/maven/**")
+ excludes.remove("module-info.class")
+ archiveClassifier.set("")
}
jar {
dependsOn(shadowJar)
diff --git a/buildSrc/src/main/kotlin/testing-conventions.gradle.kts b/buildSrc/src/main/kotlin/testing-conventions.gradle.kts
index 57a97e90247d..466e49904ad3 100644
--- a/buildSrc/src/main/kotlin/testing-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/testing-conventions.gradle.kts
@@ -1,5 +1,6 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED
+import org.gradle.internal.os.OperatingSystem
plugins {
id("org.gradle.test-retry")
@@ -15,29 +16,51 @@ tasks.withType().configureEach {
exceptionFormat = FULL
}
retry {
- maxRetries.set(2)
+ maxRetries.set(providers.gradleProperty("retries").map(String::toInt).orElse(2))
+ }
+ distribution {
+ val isCiServer = System.getenv("CI") != null
+ enabled.convention(providers.gradleProperty("enableTestDistribution")
+ .map(String::toBoolean)
+ .map { enabled -> enabled && (!isCiServer || System.getenv("GRADLE_ENTERPRISE_ACCESS_KEY").isNotBlank()) }
+ .orElse(false))
+ maxLocalExecutors.set(providers.gradleProperty("testDistribution.maxLocalExecutors").map(String::toInt).orElse(1))
+ maxRemoteExecutors.set(providers.gradleProperty("testDistribution.maxRemoteExecutors").map(String::toInt))
+ if (isCiServer) {
+ when {
+ OperatingSystem.current().isLinux -> requirements.add("os=linux")
+ OperatingSystem.current().isWindows -> requirements.add("os=windows")
+ OperatingSystem.current().isMacOsX -> requirements.add("os=macos")
+ }
+ }
}
systemProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager")
// Required until ASM officially supports the JDK 14
systemProperty("net.bytebuddy.experimental", true)
+ if (project.hasProperty("enableJFR")) {
+ jvmArgs(
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+DebugNonSafepoints",
+ "-XX:StartFlightRecording=filename=${reports.junitXml.outputLocation.get()},dumponexit=true,settings=profile.jfc",
+ "-XX:FlightRecorderOptions=stackdepth=1024"
+ )
+ }
+ // Track OS as input so that tests are executed on all configured operating systems on CI
+ trackOperationSystemAsInput()
}
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"))
+ val libs = project.extensions["libs"] as VersionCatalog
+ "testImplementation"(libs.findDependency("assertj").get())
+ "testImplementation"(libs.findDependency("mockito").get())
- "testRuntimeOnly"(project(":junit-jupiter-engine"))
+ if (!project.name.startsWith("junit-jupiter")) {
+ "testImplementation"(project(":junit-jupiter"))
}
"testImplementation"(testFixtures(project(":junit-jupiter-api")))
- "testRuntimeOnly"(project(":junit-platform-launcher"))
+ "testRuntimeOnly"(project(":junit-platform-engine"))
+ "testRuntimeOnly"(project(":junit-platform-jfr"))
- "testRuntimeOnly"("org.apache.logging.log4j:log4j-core")
- "testRuntimeOnly"("org.apache.logging.log4j:log4j-jul")
+ "testRuntimeOnly"(libs.findBundle("log4j").get())
}
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..4c69bf8b9475 100644
--- a/documentation/documentation.gradle.kts
+++ b/documentation/documentation.gradle.kts
@@ -1,4 +1,6 @@
import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask
+import org.gradle.api.tasks.PathSensitivity.RELATIVE
+import org.gradle.internal.os.OperatingSystem
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.junit.gradle.exec.ClasspathSystemPropertyProvider
import org.junit.gradle.javadoc.ModuleSpecificJavadocFileOption
@@ -22,14 +24,10 @@ javaLibrary {
testJavaVersion = JavaVersion.VERSION_1_8
}
-val apiReport by configurations.creating {
- extendsFrom(configurations.internal.get())
-}
+val apiReport by configurations.creating
dependencies {
- internal(platform(project(":dependencies")))
-
- implementation(project(":junit-jupiter-api")) {
+ implementation(projects.junitJupiterApi) {
because("Jupiter API is used in src/main/java")
}
@@ -37,18 +35,21 @@ 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(project(":junit-vintage-engine"))
- testRuntimeOnly("org.apache.logging.log4j:log4j-core")
- testRuntimeOnly("org.apache.logging.log4j:log4j-jul")
+ testImplementation(projects.junitJupiter)
+ testImplementation(projects.junitJupiterMigrationsupport)
+ testImplementation(projects.junitPlatformConsole)
+ testImplementation(projects.junitPlatformRunner)
+ testImplementation(projects.junitPlatformSuite)
+ testImplementation(projects.junitPlatformTestkit)
+ testImplementation(kotlin("stdlib"))
+
+ testImplementation(projects.junitVintageEngine)
+ testRuntimeOnly(libs.bundles.log4j)
+ testRuntimeOnly(libs.apiguardian) {
+ because("it's required to generate API tables")
+ }
- testImplementation("io.github.classgraph:classgraph") {
+ testImplementation(libs.classgraph) {
because("ApiReportGenerator needs it")
}
}
@@ -56,7 +57,7 @@ dependencies {
asciidoctorj {
modules {
diagram.use()
- pdf.version(versions["asciidoctor-pdf"])
+ pdf.version(libs.versions.asciidoctor.pdf)
}
}
@@ -67,12 +68,13 @@ val docsDir = file("$buildDir/ghpages-docs")
val replaceCurrentDocs = project.hasProperty("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")
+ sign.set(false)
contents {
from(docsDir)
@@ -93,10 +95,11 @@ val consoleLauncherOptionsFile = File(generatedAsciiDocPath, "console-launcher-o
val experimentalApisTableFile = File(generatedAsciiDocPath, "experimental-apis-table.txt")
val deprecatedApisTableFile = File(generatedAsciiDocPath, "deprecated-apis-table.txt")
+val jdkJavadocBaseUrl = "https://docs.oracle.com/en/java/javase/11/docs/api"
val elementListsDir = file("$buildDir/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,19 +108,42 @@ require(externalModulesWithoutModularJavadoc.values.all { it.endsWith("/") }) {
tasks {
- val consoleLauncherTest by registering(JavaExec::class) {
- dependsOn(testClasses)
+ val consoleLauncherTest by registering {
+ val runtimeClasspath = sourceSets["test"].runtimeClasspath
+ inputs.files(runtimeClasspath).withNormalizer(ClasspathNormalizer::class)
val reportsDir = file("$buildDir/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")
+
+ // Track OS as input so that tests are executed on all configured operating systems on CI
+ trackOperationSystemAsInput()
+
+ doFirst {
+ val debugging = findProperty("consoleLauncherTestDebug")?.toString()?.toBoolean() ?: false
+ val output = ByteArrayOutputStream()
+ val result = javaexec {
+ classpath = runtimeClasspath
+ main = "org.junit.platform.console.ConsoleLauncher"
+ args("--scan-classpath")
+ args("--config", "enableHttpServer=true")
+ 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")
+ debug = debugging
+ if (!debugging) {
+ standardOutput = output
+ errorOutput = output
+ }
+ isIgnoreExitValue = true
+ }
+ if (result.exitValue != 0 && !debugging) {
+ System.out.write(output.toByteArray())
+ System.out.flush()
+ }
+ result.rethrowFailure().assertNormalExitValue()
+ }
}
test {
@@ -127,14 +153,14 @@ tasks {
val generateConsoleLauncherOptions by registering(JavaExec::class) {
classpath = sourceSets["test"].runtimeClasspath
- main = "org.junit.platform.console.ConsoleLauncher"
+ mainClass.set("org.junit.platform.console.ConsoleLauncher")
args("--help", "--disable-banner")
redirectOutput(consoleLauncherOptionsFile)
}
val generateExperimentalApisTable by registering(JavaExec::class) {
classpath = sourceSets["test"].runtimeClasspath
- main = "org.junit.api.tools.ApiReportGenerator"
+ mainClass.set("org.junit.api.tools.ApiReportGenerator")
jvmArgumentProviders += ClasspathSystemPropertyProvider("api.classpath", apiReport)
args("EXPERIMENTAL")
redirectOutput(experimentalApisTableFile)
@@ -142,7 +168,7 @@ tasks {
val generateDeprecatedApisTable by registering(JavaExec::class) {
classpath = sourceSets["test"].runtimeClasspath
- main = "org.junit.api.tools.ApiReportGenerator"
+ mainClass.set("org.junit.api.tools.ApiReportGenerator")
jvmArgumentProviders += ClasspathSystemPropertyProvider("api.classpath", apiReport)
args("DEPRECATED")
redirectOutput(deprecatedApisTableFile)
@@ -159,15 +185,18 @@ tasks {
}
}
+ // Temporary workaround for https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/599
+ inputs.dir(sourceDir).withPropertyName("sourceDir").withPathSensitivity(RELATIVE)
+
attributes(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,
@@ -181,7 +210,8 @@ tasks {
"icons" to "font",
"sectanchors" to true,
"idprefix" to "",
- "idseparator" to "-"
+ "idseparator" to "-",
+ "jdk-javadoc-base-url" to jdkJavadocBaseUrl
))
sourceSets["test"].apply {
@@ -261,15 +291,15 @@ tasks {
this as StandardJavadocDocletOptions
splitIndex(true)
- addBooleanOption("Xdoclint:none", true)
+ addBooleanOption("Xdoclint:all,-accessibility,-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")
}
@@ -368,9 +398,24 @@ 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)
}
+
+ gitPublishCommit {
+ dependsOn(configureGitAuthor)
+ }
}
fun JavaExec.redirectOutput(outputFile: File) {
@@ -385,14 +430,14 @@ fun JavaExec.redirectOutput(outputFile: File) {
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..3cc23413cee2 100644
--- a/documentation/src/docs/asciidoc/link-attributes.adoc
+++ b/documentation/src/docs/asciidoc/link-attributes.adoc
@@ -27,20 +27,26 @@ 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]
+: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]
// 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 +62,24 @@ 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]
: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]
@@ -163,6 +175,7 @@ endif::[]
: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]
diff --git a/documentation/src/docs/asciidoc/release-notes/index.adoc b/documentation/src/docs/asciidoc/release-notes/index.adoc
index 82817f7d5100..5c13474a0f9d 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.8 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,8 @@ 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.8.2.adoc[]
-include::{basedir}/release-notes-5.6.2.adoc[]
+include::{basedir}/release-notes-5.8.1.adoc[]
-include::{basedir}/release-notes-5.6.1.adoc[]
-
-include::{basedir}/release-notes-5.6.0.adoc[]
+include::{basedir}/release-notes-5.8.0.adoc[]
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.8.0.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.0.adoc
new file mode 100644
index 000000000000..fc0763c6e355
--- /dev/null
+++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.0.adoc
@@ -0,0 +1,21 @@
+[[release-notes-5.8.0]]
+== 5.8.0
+
+*Date of Release:* September 12, 2021
+
+*Scope:*
+
+* Declarative test suites via `@Suite` classes
+* `LauncherSession` and accompanying listener
+* New `UniqueIdTrackingListener`
+* More fine-grained Java Flight Recorder events
+* Java Flight Recorder support on Java 8 Update 262 or higher
+* Test class ordering
+* `@TempDir` can be used to create multiple temporary directories
+* Extension registration via `@ExtendWith` on fields and parameters
+* Auto-close support for arguments in `@ParameterizedTest` methods
+* Memory and performance optimizations
+* Numerous bug fixes and minor improvements
+
+For complete details consult the
+https://junit.org/junit5/docs/5.8.0/release-notes/index.html[5.8.0 Release Notes] online.
diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.1.adoc
new file mode 100644
index 000000000000..8a54b4f541d9
--- /dev/null
+++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.1.adoc
@@ -0,0 +1,62 @@
+[[release-notes-5.8.1]]
+== 5.8.1
+
+*Date of Release:* September 22, 2021
+
+*Scope:*
+
+* Support for _text blocks_ in `@CsvSource`
+* Java 18 support in the `JRE` enum
+* Access to the `ExecutionMode` in the `ExtensionContext`
+* Minor bug fixes and enhancements since 5.8.0
+
+For a complete list of all _closed_ issues and pull requests for this release, consult the
+link:{junit5-repo}+/milestone/59?closed=1+[5.8.1] milestone page in the JUnit repository on
+GitHub.
+
+
+[[release-notes-5.8.1-junit-platform]]
+=== JUnit Platform
+
+==== Deprecations and Breaking Changes
+
+* `@UseTechnicalNames` has been deprecated in favor of the new `@Suite` support which does
+ not require the use of technical names. See the warning in
+ <<../user-guide/index.adoc#running-tests-junit-platform-runner, Using JUnit 4 to run the
+ JUnit Platform>> for details.
+
+==== New Features and Improvements
+
+* `ReflectionSupport.findNestedClasses(..)` is now thread-safe with regard to cycle
+ detection.
+
+
+[[release-notes-5.8.1-junit-jupiter]]
+=== JUnit Jupiter
+
+==== Bug Fixes
+
+* `assertLinesMatch()` in `Assertions` no longer fails with a `NoSuchElementException` if
+ a limited fast-forward followed by at least one more expected line exceeds the remaining
+ actual lines.
+* `assertLinesMatch()` in `Assertions` now handles fast-forwards with leading and trailing
+ spaces correctly and no longer throws an `IndexOutOfBoundsException`.
+
+==== New Features and Improvements
+
+* `JAVA_18` has been added to the `JRE` enum for use with JRE-based execution conditions.
+* CSV content in `@CsvSource` can now be supplied as a _text block_ instead of an array of
+ strings. See the
+ <<../user-guide/index.adoc#writing-tests-parameterized-tests-sources-CsvSource, User
+ Guide>> for details and an example.
+* The `ExecutionMode` for the current test or container is now accessible via the
+ `ExtensionContext`.
+
+
+[[release-notes-5.8.1-junit-vintage]]
+=== JUnit Vintage
+
+==== Bug Fixes
+
+* Relaxed version constraint in published Gradle Module Metadata to allow downgrading the
+ `junit:junit` dependency from 4.13.2.
diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.2.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.2.adoc
new file mode 100644
index 000000000000..831cc74ae286
--- /dev/null
+++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.2.adoc
@@ -0,0 +1,45 @@
+[[release-notes-5.8.2]]
+== 5.8.2
+
+*Date of Release:* November 28, 2021
+
+*Scope:*
+
+* Text blocks in `@CsvSource` are treated like CSV files
+* CSV headers in display names for `@CsvSource` and `@CsvFileSource`
+* Custom quote character support in `@CsvSource` and `@CsvFileSource`
+
+For a complete list of all _closed_ issues and pull requests for this release, consult the
+link:{junit5-repo}+/milestone/60?closed=1+[5.8.2] milestone page in the JUnit repository on
+GitHub.
+
+
+[[release-notes-5.8.2-junit-platform]]
+=== JUnit Platform
+
+No changes.
+
+
+[[release-notes-5.8.2-junit-jupiter]]
+=== JUnit Jupiter
+
+==== New Features and Improvements
+
+* Text blocks in `@CsvSource` are now treated like complete CSV files, including support
+ for comments beginning with a `+++#+++` symbol as well as support for new lines within
+ quoted strings. See the
+ <<../user-guide/index.adoc#writing-tests-parameterized-tests-sources-CsvSource, User
+ Guide>> for details and examples.
+* CSV headers can now be used in display names in parameterized tests. See
+ <<../user-guide/index.adoc#writing-tests-parameterized-tests-sources-CsvSource,
+ `@CsvSource`>> and
+ <<../user-guide/index.adoc#writing-tests-parameterized-tests-sources-CsvFileSource,
+ `@CsvFileSource`>> in the User Guide for details and examples.
+* The quote character for _quoted strings_ in `@CsvSource` and `@CsvFileSource` is now
+ configurable via a new `quoteCharacter` attribute in each annotation.
+
+
+[[release-notes-5.8.2-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..17790483e124 100644
--- a/documentation/src/docs/asciidoc/release-notes/release-notes-TEMPLATE.adoc
+++ b/documentation/src/docs/asciidoc/release-notes/release-notes-TEMPLATE.adoc
@@ -7,9 +7,9 @@
*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/⚠️?closed=1+[⚠️] milestone page in the JUnit repository on
+GitHub.
[[release-notes-⚠️-junit-platform]]
diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics.adoc b/documentation/src/docs/asciidoc/user-guide/advanced-topics.adoc
index 53164790e5b2..bc1d41be4c93 100644
--- a/documentation/src/docs/asciidoc/user-guide/advanced-topics.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics.adoc
@@ -3,6 +3,10 @@
include::launcher-api.adoc[]
+include::junit-platform-reporting.adoc[]
+
+include::junit-platform-suite-engine.adoc[]
+
include::testkit.adoc[]
////
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..52777870bedc 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,40 @@ 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]
+----
+
+[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 +146,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 +189,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 +245,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 +266,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.).
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/junit-platform-reporting.adoc b/documentation/src/docs/asciidoc/user-guide/junit-platform-reporting.adoc
new file mode 100644
index 000000000000..586d32b0cab6
--- /dev/null
+++ b/documentation/src/docs/asciidoc/user-guide/junit-platform-reporting.adoc
@@ -0,0 +1,17 @@
+[[junit-platform-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 <>
+for details.
diff --git a/documentation/src/docs/asciidoc/user-guide/junit-platform-suite-engine.adoc b/documentation/src/docs/asciidoc/user-guide/junit-platform-suite-engine.adoc
new file mode 100644
index 000000000000..18be01cd9091
--- /dev/null
+++ b/documentation/src/docs/asciidoc/user-guide/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 declard 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/launcher-api.adoc b/documentation/src/docs/asciidoc/user-guide/launcher-api.adoc
index a1964e34e59c..abcd326e7256 100644
--- a/documentation/src/docs/asciidoc/user-guide/launcher-api.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/launcher-api.adoc
@@ -19,9 +19,9 @@ An example consumer of the launcher API is the `{ConsoleLauncher}` in the
[[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.
+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:
@@ -35,9 +35,9 @@ include::{testDir}/example/UsingTheLauncherDemo.java[tags=imports]
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.
+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
@@ -45,12 +45,13 @@ traverse the tree, retrieve details about a node, and get a link to the original
(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.
+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
@@ -65,25 +66,27 @@ following example.
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}`.
+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}`.
[[launcher-api-engines-custom]]
-==== Plugging in your own Test Engine
+==== Registering a TestEngine
-JUnit currently provides two `{TestEngine}` implementations.
+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 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-platform-suite-engine}`: To execute declarative suites of 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. Engine 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.
NOTE: `{HierarchicalTestEngine}` is a convenient abstract base implementation (used by
@@ -106,33 +109,118 @@ by the JUnit Team may use the `junit-` prefix for their `TestEngine` IDs.
====
[[launcher-api-post-discovery-filters-custom]]
-==== Plugging in your own Post-Discovery Filters
+==== Registering a PostDiscoveryFilter
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
+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.)
+
+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-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]]
-==== 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
+==== 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 Test Execution Listeners
+==== 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
+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
@@ -140,17 +228,17 @@ execution listeners should be deactivated (i.e. not registered) for the current
[NOTE]
====
-Only listeners registered via the `ServiceLoader` mechanism within the
+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
+`{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`.
+supplied in the `LauncherDiscoveryRequest` that is passed to the `{Launcher}`.
====
[[launcher-api-listeners-custom-deactivation-pattern]]
@@ -158,32 +246,13 @@ supplied in the `LauncherDiscoveryRequest` that is passed to the `Launcher`.
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.
+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]
----
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..1daf9302be87 100644
--- a/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc
@@ -30,8 +30,8 @@ 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
+engine_ exposes the category's fully qualified class name as a <>
+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).
@@ -98,9 +98,6 @@ all rule migration support extensions: `VerifierSupport`, `ExternalResourceSuppo
However, if you intend to develop a new extension for JUnit 5 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
@@ -121,6 +118,3 @@ 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.
diff --git a/documentation/src/docs/asciidoc/user-guide/overview.adoc b/documentation/src/docs/asciidoc/user-guide/overview.adoc
index afad42176c2f..708b6c97afbc 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
-<>).
+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 new <> 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..56a57486f27b 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]]
@@ -121,25 +134,26 @@ https://docs.gradle.org/current/userguide/java_testing.html#using_junit5[native
for executing tests on the JUnit Platform. To enable it, you just 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")
}
}
----
@@ -157,15 +171,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 +188,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}")
}
----
@@ -192,7 +202,7 @@ 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 {
@@ -215,11 +225,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")
}
----
@@ -259,46 +269,17 @@ following.
[source,xml,indent=0]
[subs=attributes+]
----
-
-
-
- maven-surefire-plugin
- {surefire-version}
-
-
- maven-failsafe-plugin
- {surefire-version}
-
-
- org.junit.jupiter
- junit-jupiter-api
- {jupiter-version}
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
+ junit-jupiter{jupiter-version}test
-
-----
-
-Maven Surefire and Maven Failsafe can run JUnit 4 based tests alongside Jupiter tests as
-long as you configure `test` scoped dependencies on JUnit 4 and the JUnit Vintage
-`TestEngine` implementation similar to the following.
-
-[source,xml,indent=0]
-[subs=attributes+]
-----
-
@@ -312,6 +293,16 @@ long as you configure `test` scoped dependencies on JUnit 4 and the JUnit Vintag
+----
+
+Maven Surefire and Maven Failsafe can run JUnit 4 based tests alongside Jupiter tests as
+long as you configure `test` scoped dependencies on JUnit 4 and the JUnit Vintage
+`TestEngine` implementation similar to the following.
+
+[source,xml,indent=0]
+[subs=attributes+]
+----
+
@@ -329,6 +320,19 @@ long as you configure `test` scoped dependencies on JUnit 4 and the JUnit Vintag
+
+
+
+ maven-surefire-plugin
+ {surefire-version}
+
+
+ maven-failsafe-plugin
+ {surefire-version}
+
+
+
+
----
[[running-tests-build-maven-filter-test-class-names]]
@@ -376,8 +380,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 +399,7 @@ the following configuration properties.
acceptance | !feature-aintegration, regression
-
+
@@ -600,6 +605,25 @@ subject to expansion.
[[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 +633,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 +641,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 +651,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 +665,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 +722,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.
- <>
@@ -760,8 +786,39 @@ Examples:
- `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.
@@ -808,7 +865,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 +882,63 @@ 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-listeners]]
+=== Using Listeners
+
+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.
+* `{LauncherDiscoveryListener}`: receives events that occur during test discovery.
+* `{TestExecutionListener}`: receives events that occur during test execution.
-[[running-tests-flight-recorder]]
-=== Flight Recorder Support
+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.
+
+<> ::
+ `FlightRecordingExecutionListener` and `FlightRecordingDiscoveryListener` that generate
+ Java Flight Recorder events during test discovery and execution.
+
+`{LegacyXmlReportGeneratingListener}` ::
+ `TestExecutionListener` that generates XML reports compatible with the de facto
+ standard for JUnit 4 based test reports. See <> for
+ details.
+
+`{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 +951,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 +966,6 @@ 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.
diff --git a/documentation/src/docs/asciidoc/user-guide/testkit.adoc b/documentation/src/docs/asciidoc/user-guide/testkit.adoc
index 32658381eaff..665c4daea788 100644
--- a/documentation/src/docs/asciidoc/user-guide/testkit.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/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/writing-tests.adoc b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc
index 3c6a686f3038..5699ce07b095 100644
--- a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc
+++ b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc
@@ -29,7 +29,8 @@ 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_.
@@ -113,10 +114,21 @@ Test classes must not be `abstract` and must have a single constructor.
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
@@ -462,8 +474,7 @@ indirectly present, or meta-present on a given element.
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.
+the annotation via its name.
If needed, the condition method can take a single parameter of type `ExtensionContext`.
[source,java,indent=0]
@@ -471,6 +482,17 @@ If needed, the condition method can take a single parameter of type `ExtensionCo
include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_custom]
----
+Alternatively, the condition method can be located outside the test class. In this case,
+it has to 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: 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.
@@ -480,25 +502,9 @@ must always be `static`. Condition methods located in external classes must also
=== Tagging and Filtering
Test classes and methods can be tagged via the `@Tag` annotation. Those tags can later be
-used to filter <>.
-
-TIP: See also: <>
-
-==== Syntax Rules for Tags
-
-* 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.
+used to filter <>. Please refer to the
+<> section for more information about tag support in the JUnit
+Platform.
[source,java,indent=0]
----
@@ -511,11 +517,16 @@ 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_ and
+_test class_.
-NOTE: See <> for a definition of _test method_.
+[[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 +539,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 +562,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 +572,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,6 +585,73 @@ 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, run 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
@@ -642,7 +721,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 +731,26 @@ 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.
+However, this restriction can be circumvented by annotating a `@Nested` test class with
+`@TestInstance(Lifecycle.PER_CLASS)` (see <>).
[[writing-tests-dependency-injection]]
=== Dependency Injection for Constructors and Methods
@@ -995,9 +1089,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 +1118,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
@@ -1224,8 +1330,11 @@ include::{testDir}/example/ExternalMethodSourceDemo.java[tags=external_MethodSou
[[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 +1346,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 +1462,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 +1476,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
@@ -1568,6 +1784,34 @@ 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 `@ArgumentSource`, you can give names to arguments. This
+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 default name pattern for all parameterized tests in your project, you can add the following
+configuration to `junit-platform.properties`
+
+[source,properties,indent=0]
+----
+junit.jupiter.params.displayname.default = {index}
+----
+
+the display name for a parameterized method is determined according to the following precedence rules:
+
+1. `name` of `@ParameterizedTest`, if present
+2. the value of `junit.jupiter.params.displayname.default` (from junit-platform.properties), if present
+3. `DEFAULT_DISPLAY_NAME` constant defined in `@ParameterizedTest`
[[writing-tests-parameterized-tests-lifecycle-interop]]
==== Lifecycle and Interoperability
@@ -1662,8 +1906,8 @@ 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
+The next six methods are very simple examples that 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 easy it is to generate dynamic tests for a
@@ -1717,6 +1961,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 +1972,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.
@@ -1891,11 +2135,11 @@ junit.jupiter.execution.parallel.mode.default = concurrent
The default execution mode is applied to all nodes of the test tree with a few notable
exceptions, namely test classes that use the `Lifecycle.PER_CLASS` mode or a
-`{MethodOrderer}` (except for `{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.
All nodes of the test tree that are configured with the `CONCURRENT` execution mode will
be executed fully in parallel according to the provided
@@ -2064,9 +2308,9 @@ 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
-`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.
+default. To use it, annotate a 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.
For example, the following test declares a parameter annotated with `@TempDir` for a
single test method, creates and writes to a file in the temporary directory, and checks
@@ -2078,13 +2322,27 @@ 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
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..5a75f7127483 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-2021 the original author or authors.
*
* All rights 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..c6c3fac7516a 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-2021 the original author or authors.
*
* All rights 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..5d396e05bd22 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-2021 the original author or authors.
*
* All rights 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..6a0e5021323a 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-2021 the original author or authors.
*
* All rights 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..55791891d80b 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-2021 the original author or authors.
*
* All rights 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..fe17e5bf55cc 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-2021 the original author or authors.
*
* All rights 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..4233e00a2fed 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-2021 the original author or authors.
*
* All rights 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..670695e3e3fd 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-2021 the original author or authors.
*
* All rights 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/AssumptionsDemo.java b/documentation/src/test/java/example/AssumptionsDemo.java
index 75914749a7bf..6340feca4eab 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-2021 the original author or authors.
*
* All rights 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..7e0a7d060c29 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-2021 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
diff --git a/documentation/src/test/java/example/CustomTestEngine.java b/documentation/src/test/java/example/CustomTestEngine.java
index b12a8e862c49..cd7974280389 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-2021 the original author or authors.
*
* All rights 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..7874a0048771 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-2021 the original author or authors.
*
* All rights 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..ad60c5afe539 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-2021 the original author or authors.
*
* All rights 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..fb98cc889244 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-2021 the original author or authors.
*
* All rights 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..04b77952e0ed 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-2021 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
diff --git a/documentation/src/test/java/example/DocumentationTestSuite.java b/documentation/src/test/java/example/DocumentationTestSuite.java
index 1b2f9d575883..d430a6618158 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-2021 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
@@ -10,11 +10,10 @@
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;
/**
*
Unless otherwise noted, a failed assertion will throw an
* {@link org.opentest4j.AssertionFailedError} or a subclass thereof.
*
+ *
Object Equality
+ *
+ *
Assertion methods comparing two objects for equality, such as the
+ * {@code assertEquals(expected, actual)} and {@code assertNotEquals(unexpected, actual)}
+ * variants, are only intended to test equality for an (un-)expected value
+ * and an actual value. They are not designed for testing whether a class correctly
+ * implements {@link Object#equals(Object)}. For example, {@code assertEquals()}
+ * might immediately return {@code true} when provided the same object for the
+ * expected and actual values, without calling {@code equals(Object)} at all.
+ * Tests that aim to verify the {@code equals(Object)} implementation should instead
+ * be written to explicitly verify the {@link Object#equals(Object)} contract by
+ * using {@link #assertTrue(boolean) assertTrue()} or {@link #assertFalse(boolean)
+ * assertFalse()} — for example, {@code assertTrue(expected.equals(actual))},
+ * {@code assertTrue(actual.equals(expected))}, {@code assertFalse(expected.equals(null))},
+ * etc.
+ *
*
Kotlin Support
*
*
Additional Kotlin assertions can be
@@ -2993,6 +3010,64 @@ public static void assertAll(String heading, Stream executables) thr
// --- executable ---
+ /**
+ * Assert that execution of the supplied {@code executable} throws
+ * an exception of exactly the {@code expectedType} and return the exception.
+ *
+ *
If no exception is thrown, or if an exception of a different type is
+ * thrown, this method will fail.
+ *
+ *
If you do not want to perform additional checks on the exception instance,
+ * ignore the return value.
+ *
+ * @since 5.8
+ */
+ @API(status = EXPERIMENTAL, since = "5.8")
+ public static T assertThrowsExactly(Class expectedType, Executable executable) {
+ return AssertThrowsExactly.assertThrowsExactly(expectedType, executable);
+ }
+
+ /**
+ * Assert that execution of the supplied {@code executable} throws
+ * an exception of exactly the {@code expectedType} and return the exception.
+ *
+ *
If no exception is thrown, or if an exception of a different type is
+ * thrown, this method will fail.
+ *
+ *
If you do not want to perform additional checks on the exception instance,
+ * ignore the return value.
+ *
+ *
Fails with the supplied failure {@code message}.
+ *
+ * @since 5.8
+ */
+ @API(status = EXPERIMENTAL, since = "5.8")
+ public static T assertThrowsExactly(Class expectedType, Executable executable,
+ String message) {
+ return AssertThrowsExactly.assertThrowsExactly(expectedType, executable, message);
+ }
+
+ /**
+ * Assert that execution of the supplied {@code executable} throws
+ * an exception of exactly the {@code expectedType} and return the exception.
+ *
+ *
If no exception is thrown, or if an exception of a different type is
+ * thrown, this method will fail.
+ *
+ *
If necessary, the failure message will be retrieved lazily from the
+ * supplied {@code messageSupplier}.
+ *
+ *
If you do not want to perform additional checks on the exception instance,
+ * ignore the return value.
+ *
+ * @since 5.8
+ */
+ @API(status = EXPERIMENTAL, since = "5.8")
+ public static T assertThrowsExactly(Class expectedType, Executable executable,
+ Supplier messageSupplier) {
+ return AssertThrowsExactly.assertThrowsExactly(expectedType, executable, messageSupplier);
+ }
+
/**
* Assert that execution of the supplied {@code executable} throws
* an exception of the {@code expectedType} and return the exception.
@@ -3001,7 +3076,7 @@ public static void assertAll(String heading, Stream executables) thr
* thrown, this method will fail.
*
*
If you do not want to perform additional checks on the exception instance,
- * simply ignore the return value.
+ * ignore the return value.
*/
public static T assertThrows(Class expectedType, Executable executable) {
return AssertThrows.assertThrows(expectedType, executable);
@@ -3015,7 +3090,7 @@ public static T assertThrows(Class expectedType, Execut
* thrown, this method will fail.
*
*
If you do not want to perform additional checks on the exception instance,
- * simply ignore the return value.
+ * ignore the return value.
*
*
Fails with the supplied failure {@code message}.
*/
@@ -3034,7 +3109,7 @@ public static T assertThrows(Class expectedType, Execut
* supplied {@code messageSupplier}.
*
*
If you do not want to perform additional checks on the exception instance,
- * simply ignore the return value.
+ * ignore the return value.
*/
public static T assertThrows(Class expectedType, Executable executable,
Supplier messageSupplier) {
@@ -3448,4 +3523,53 @@ public static T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier
return AssertTimeout.assertTimeoutPreemptively(timeout, supplier, messageSupplier);
}
+ // --- assertInstanceOf ----------------------------------------------------
+
+ /**
+ * Assert that the supplied {@code actualValue} is an instance of the
+ * {@code expectedType}.
+ *
+ *
Like the {@code instanceof} operator a {@code null} value is not
+ * considered to be of the {@code expectedType} and does not pass the assertion.
+ *
+ * @since 5.8
+ */
+ @API(status = EXPERIMENTAL, since = "5.8")
+ public static T assertInstanceOf(Class expectedType, Object actualValue) {
+ return AssertInstanceOf.assertInstanceOf(expectedType, actualValue);
+ }
+
+ /**
+ * Assert that the supplied {@code actualValue} is an instance of the
+ * {@code expectedType}.
+ *
+ *
Like the {@code instanceof} operator a {@code null} value is not
+ * considered to be of the {@code expectedType} and does not pass the assertion.
+ *
+ *
Fails with the supplied failure {@code message}.
+ *
+ * @since 5.8
+ */
+ @API(status = EXPERIMENTAL, since = "5.8")
+ public static T assertInstanceOf(Class expectedType, Object actualValue, String message) {
+ return AssertInstanceOf.assertInstanceOf(expectedType, actualValue, message);
+ }
+
+ /**
+ * Assert that the supplied {@code actualValue} is an instance of the
+ * {@code expectedType}.
+ *
+ *
Like the {@code instanceof} operator a {@code null} value is not
+ * considered to be of the {@code expectedType} and does not pass the assertion.
+ *
+ *
If necessary, the failure message will be retrieved lazily from the
+ * supplied {@code messageSupplier}.
+ *
+ * @since 5.8
+ */
+ @API(status = EXPERIMENTAL, since = "5.8")
+ public static T assertInstanceOf(Class expectedType, Object actualValue, Supplier messageSupplier) {
+ return AssertInstanceOf.assertInstanceOf(expectedType, actualValue, messageSupplier);
+ }
+
}
diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assumptions.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assumptions.java
index b3ec15998f82..51e308cced3a 100644
--- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assumptions.java
+++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assumptions.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * Copyright 2015-2021 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
@@ -212,11 +212,12 @@ public static void assumeFalse(BooleanSupplier assumptionSupplier, SupplierIf the assumption is invalid, this method does nothing.
- *
- *
If the {@code executable} throws an exception, it will be rethrown
- * as is but {@link ExceptionUtils#throwAsUncheckedException masked}
- * as an unchecked exception.
+ *
Unlike the other assumption methods, this method will not abort the test.
+ * If the assumption is invalid, this method does nothing. If the assumption is
+ * valid and the {@code executable} throws an exception, it will be treated like
+ * a regular test failure. That exception will be rethrown as is
+ * but {@link ExceptionUtils#throwAsUncheckedException masked} as an unchecked
+ * exception.
*
* @param assumptionSupplier the supplier of the assumption to validate
* @param executable the block of code to execute if the assumption is valid
@@ -230,11 +231,12 @@ public static void assumingThat(BooleanSupplier assumptionSupplier, Executable e
* Execute the supplied {@link Executable}, but only if the supplied
* assumption is valid.
*
- *
If the assumption is invalid, this method does nothing.
- *
- *
If the {@code executable} throws an exception, it will be rethrown
- * as is but {@link ExceptionUtils#throwAsUncheckedException masked}
- * as an unchecked exception.
+ *