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 +[![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.junit.org/scans) + 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-a integration, 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; /** *

Logging Configuration

@@ -29,9 +28,9 @@ * * @since 5.0 */ -@RunWith(JUnitPlatform.class) +@Suite @SelectPackages("example") @IncludeClassNamePatterns(".+(Tests|Demo)$") @ExcludeTags("exclude") -public class DocumentationTestSuite { +class DocumentationTestSuite { } diff --git a/documentation/src/test/java/example/DynamicTestsDemo.java b/documentation/src/test/java/example/DynamicTestsDemo.java index 8aa063a57dbe..df23a28a57ea 100644 --- a/documentation/src/test/java/example/DynamicTestsDemo.java +++ b/documentation/src/test/java/example/DynamicTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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 @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; import static org.junit.jupiter.api.DynamicTest.dynamicTest; +import static org.junit.jupiter.api.Named.named; import java.util.Arrays; import java.util.Collection; @@ -33,6 +34,7 @@ import org.junit.jupiter.api.DynamicNode; import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.function.ThrowingConsumer; @@ -151,6 +153,21 @@ Stream dynamicTestsFromStreamFactoryMethod() { return DynamicTest.stream(inputStream, displayNameGenerator, testExecutor); } + @TestFactory + Stream dynamicTestsFromStreamFactoryMethodWithNames() { + // Stream of palindromes to check + Stream> inputStream = Stream.of( + named("racecar is a palindrome", "racecar"), + named("radar is also a palindrome", "radar"), + named("mom also seems to be a palindrome", "mom"), + named("dad is yet another palindrome", "dad") + ); + + // Returns a stream of dynamic tests. + return DynamicTest.stream(inputStream, + text -> assertTrue(isPalindrome(text))); + } + @TestFactory Stream dynamicTestsWithContainers() { return Stream.of("A", "B", "C") diff --git a/documentation/src/test/java/example/ExampleTestCase.java b/documentation/src/test/java/example/ExampleTestCase.java index e08950b1df9b..8581b92851bf 100644 --- a/documentation/src/test/java/example/ExampleTestCase.java +++ b/documentation/src/test/java/example/ExampleTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/ExternalCustomConditionDemo.java b/documentation/src/test/java/example/ExternalCustomConditionDemo.java new file mode 100644 index 000000000000..c577c61f0741 --- /dev/null +++ b/documentation/src/test/java/example/ExternalCustomConditionDemo.java @@ -0,0 +1,34 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example; + +// tag::user_guide_external_custom_condition[] +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIf; + +class ExternalCustomConditionDemo { + + @Test + @EnabledIf("example.ExternalCondition#customCondition") + void enabled() { + // ... + } + +} + +class ExternalCondition { + + static boolean customCondition() { + return true; + } + +} +// end::user_guide_external_custom_condition[] diff --git a/documentation/src/test/java/example/ExternalMethodSourceDemo.java b/documentation/src/test/java/example/ExternalMethodSourceDemo.java index cda486d775de..d12398508a73 100644 --- a/documentation/src/test/java/example/ExternalMethodSourceDemo.java +++ b/documentation/src/test/java/example/ExternalMethodSourceDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/Fast.java b/documentation/src/test/java/example/Fast.java index 911fdb2df0d7..a1045122dbd0 100644 --- a/documentation/src/test/java/example/Fast.java +++ b/documentation/src/test/java/example/Fast.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/FastTest.java b/documentation/src/test/java/example/FastTest.java index f85066f7db29..e596c7519b27 100644 --- a/documentation/src/test/java/example/FastTest.java +++ b/documentation/src/test/java/example/FastTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/HamcrestAssertionsDemo.java b/documentation/src/test/java/example/HamcrestAssertionsDemo.java index 5f9cd857cb81..2bef36f34cc2 100644 --- a/documentation/src/test/java/example/HamcrestAssertionsDemo.java +++ b/documentation/src/test/java/example/HamcrestAssertionsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/IgnoredTestsDemo.java b/documentation/src/test/java/example/IgnoredTestsDemo.java index 6bddc9134c9f..edf05bb2b5c9 100644 --- a/documentation/src/test/java/example/IgnoredTestsDemo.java +++ b/documentation/src/test/java/example/IgnoredTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/JUnit4Tests.java b/documentation/src/test/java/example/JUnit4Tests.java index 2caac8b99351..800ce8a46958 100644 --- a/documentation/src/test/java/example/JUnit4Tests.java +++ b/documentation/src/test/java/example/JUnit4Tests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/JUnitPlatformClassDemo.java b/documentation/src/test/java/example/JUnitPlatformClassDemo.java index 09acf1b0e24d..a4b01f67be04 100644 --- a/documentation/src/test/java/example/JUnitPlatformClassDemo.java +++ b/documentation/src/test/java/example/JUnitPlatformClassDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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 @@ -14,10 +14,12 @@ import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; -import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; -@RunWith(JUnitPlatform.class) +//end::user_guide[] +@SuppressWarnings("deprecation") +//tag::user_guide[] +@RunWith(org.junit.platform.runner.JUnitPlatform.class) public class JUnitPlatformClassDemo { @Test diff --git a/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java b/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java index a5c698558967..9a572b28b9cc 100644 --- a/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java +++ b/documentation/src/test/java/example/JUnitPlatformSuiteDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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 @@ -11,15 +11,15 @@ package example; //tag::user_guide[] -import org.junit.platform.runner.JUnitPlatform; import org.junit.platform.suite.api.SelectPackages; import org.junit.platform.suite.api.SuiteDisplayName; import org.junit.runner.RunWith; -@RunWith(JUnitPlatform.class) +@RunWith(org.junit.platform.runner.JUnitPlatform.class) @SuiteDisplayName("JUnit Platform Suite Demo") @SelectPackages("example") //end::user_guide[] +@SuppressWarnings("deprecation") @org.junit.platform.suite.api.ExcludeTags("exclude") //tag::user_guide[] public class JUnitPlatformSuiteDemo { diff --git a/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java b/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java index 6eea9d7210d6..1166849171a6 100644 --- a/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java +++ b/documentation/src/test/java/example/MyFirstJUnitJupiterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/OrderedNestedTestClassesDemo.java b/documentation/src/test/java/example/OrderedNestedTestClassesDemo.java new file mode 100644 index 000000000000..3565feb52873 --- /dev/null +++ b/documentation/src/test/java/example/OrderedNestedTestClassesDemo.java @@ -0,0 +1,41 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example; + +// tag::user_guide[] +import org.junit.jupiter.api.ClassOrderer; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; + +@TestClassOrder(ClassOrderer.OrderAnnotation.class) +class OrderedNestedTestClassesDemo { + + @Nested + @Order(1) + class PrimaryTests { + + @Test + void test1() { + } + } + + @Nested + @Order(2) + class SecondaryTests { + + @Test + void test2() { + } + } +} +//end::user_guide[] diff --git a/documentation/src/test/java/example/OrderedTestsDemo.java b/documentation/src/test/java/example/OrderedTestsDemo.java index d54ff5b3fc82..2dbbeca1d66e 100644 --- a/documentation/src/test/java/example/OrderedTestsDemo.java +++ b/documentation/src/test/java/example/OrderedTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/ParameterizedTestDemo.java b/documentation/src/test/java/example/ParameterizedTestDemo.java index b0f2b76fde99..4717f6133b15 100644 --- a/documentation/src/test/java/example/ParameterizedTestDemo.java +++ b/documentation/src/test/java/example/ParameterizedTestDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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 @@ -19,6 +19,7 @@ import static org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE; import static org.junit.jupiter.params.provider.EnumSource.Mode.MATCH_ALL; +import java.io.File; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -39,6 +40,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.TestReporter; @@ -210,7 +212,8 @@ static Stream stringIntAndListProvider() { @CsvSource({ "apple, 1", "banana, 2", - "'lemon, lime', 0xF1" + "'lemon, lime', 0xF1", + "strawberry, 700_000" }) void testWithCsvSource(String fruit, int rank) { assertNotNull(fruit); @@ -233,6 +236,13 @@ void testWithCsvFileSourceFromFile(String country, int reference) { assertNotNull(country); assertNotEquals(0, reference); } + + @ParameterizedTest(name = "[{index}] {arguments}") + @CsvFileSource(resources = "/two-column.csv", useHeadersInDisplayName = true) + void testWithCsvFileSourceAndHeaders(String country, int reference) { + assertNotNull(country); + assertNotEquals(0, reference); + } // end::CsvFileSource_example[] // tag::ArgumentsSource_example[] @@ -344,7 +354,7 @@ protected ToLengthArgumentConverter() { @Override protected Integer convert(String source) { - return source.length(); + return (source != null ? source.length() : 0); } } @@ -440,4 +450,17 @@ void testWithCustomAggregatorAnnotation(@CsvToPerson Person person) { void testWithCustomDisplayNames(String fruit, int rank) { } // end::custom_display_names[] + + // tag::named_arguments[] + @DisplayName("A parameterized test with named arguments") + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("namedArguments") + void testWithNamedArguments(File file) { + } + + static Stream namedArguments() { + return Stream.of(arguments(Named.of("An important file", new File("path1"))), + arguments(Named.of("Another file", new File("path2")))); + } + // end::named_arguments[] } diff --git a/documentation/src/test/java/example/PollingTimeoutDemo.java b/documentation/src/test/java/example/PollingTimeoutDemo.java index 2242b6b86c84..cfc2d2d28791 100644 --- a/documentation/src/test/java/example/PollingTimeoutDemo.java +++ b/documentation/src/test/java/example/PollingTimeoutDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/RepeatedTestsDemo.java b/documentation/src/test/java/example/RepeatedTestsDemo.java index 98d552952f4d..07d2e77249f5 100644 --- a/documentation/src/test/java/example/RepeatedTestsDemo.java +++ b/documentation/src/test/java/example/RepeatedTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/SharedResourcesDemo.java b/documentation/src/test/java/example/SharedResourcesDemo.java index 99d3727437cc..95c5c3010cb0 100644 --- a/documentation/src/test/java/example/SharedResourcesDemo.java +++ b/documentation/src/test/java/example/SharedResourcesDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/SlowTests.java b/documentation/src/test/java/example/SlowTests.java index a1fba4358f35..b4aee752547b 100644 --- a/documentation/src/test/java/example/SlowTests.java +++ b/documentation/src/test/java/example/SlowTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/StandardTests.java b/documentation/src/test/java/example/StandardTests.java index 91bd871f9ffa..9395b88ea1b7 100644 --- a/documentation/src/test/java/example/StandardTests.java +++ b/documentation/src/test/java/example/StandardTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/SuiteDemo.java b/documentation/src/test/java/example/SuiteDemo.java new file mode 100644 index 000000000000..57201559ac96 --- /dev/null +++ b/documentation/src/test/java/example/SuiteDemo.java @@ -0,0 +1,28 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example; + +//tag::user_guide[] +import org.junit.platform.suite.api.IncludeClassNamePatterns; +import org.junit.platform.suite.api.SelectPackages; +import org.junit.platform.suite.api.Suite; +import org.junit.platform.suite.api.SuiteDisplayName; + +@Suite +@SuiteDisplayName("JUnit Platform Suite Demo") +@SelectPackages("example") +@IncludeClassNamePatterns(".*Tests") +//end::user_guide[] +@org.junit.platform.suite.api.ExcludeTags("exclude") +//tag::user_guide[] +class SuiteDemo { +} +//end::user_guide[] diff --git a/documentation/src/test/java/example/TaggingDemo.java b/documentation/src/test/java/example/TaggingDemo.java index 6ab3e162badf..d70205927a3d 100644 --- a/documentation/src/test/java/example/TaggingDemo.java +++ b/documentation/src/test/java/example/TaggingDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/TempDirectoryDemo.java b/documentation/src/test/java/example/TempDirectoryDemo.java index e656b44142dc..1daba23a28e0 100644 --- a/documentation/src/test/java/example/TempDirectoryDemo.java +++ b/documentation/src/test/java/example/TempDirectoryDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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 @@ -12,6 +12,7 @@ import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import java.io.IOException; import java.nio.file.Files; @@ -35,6 +36,19 @@ void writeItemsToFile(@TempDir Path tempDir) throws IOException { } // end::user_guide_parameter_injection[] + // tag::user_guide_multiple_directories[] + @Test + void copyFileFromSourceToTarget(@TempDir Path source, @TempDir Path target) throws IOException { + Path sourceFile = source.resolve("test.txt"); + new ListWriter(sourceFile).write("a", "b", "c"); + + Path targetFile = Files.copy(sourceFile, target.resolve("test.txt")); + + assertNotEquals(sourceFile, targetFile); + assertEquals(singletonList("a,b,c"), Files.readAllLines(targetFile)); + } + // end::user_guide_multiple_directories[] + static // tag::user_guide_field_injection[] class SharedTempDirectoryDemo { diff --git a/documentation/src/test/java/example/TestInfoDemo.java b/documentation/src/test/java/example/TestInfoDemo.java index 806397b9408d..941105942db6 100644 --- a/documentation/src/test/java/example/TestInfoDemo.java +++ b/documentation/src/test/java/example/TestInfoDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/TestReporterDemo.java b/documentation/src/test/java/example/TestReporterDemo.java index 5638d24f19d8..0edb0471fc2a 100644 --- a/documentation/src/test/java/example/TestReporterDemo.java +++ b/documentation/src/test/java/example/TestReporterDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/TestTemplateDemo.java b/documentation/src/test/java/example/TestTemplateDemo.java index e017af9d7db0..da330056d3b1 100644 --- a/documentation/src/test/java/example/TestTemplateDemo.java +++ b/documentation/src/test/java/example/TestTemplateDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/TestingAStackDemo.java b/documentation/src/test/java/example/TestingAStackDemo.java index c9cc1e0adc06..d3a6c7e2c5be 100644 --- a/documentation/src/test/java/example/TestingAStackDemo.java +++ b/documentation/src/test/java/example/TestingAStackDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/TimeoutDemo.java b/documentation/src/test/java/example/TimeoutDemo.java index 5959befda41f..93e5cb810d72 100644 --- a/documentation/src/test/java/example/TimeoutDemo.java +++ b/documentation/src/test/java/example/TimeoutDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/UsingTheLauncherDemo.java b/documentation/src/test/java/example/UsingTheLauncherDemo.java index e7e2dbe001b3..544cf960ccdc 100644 --- a/documentation/src/test/java/example/UsingTheLauncherDemo.java +++ b/documentation/src/test/java/example/UsingTheLauncherDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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 @@ -19,8 +19,14 @@ import java.nio.file.Path; import java.nio.file.Paths; +import org.junit.platform.engine.FilterResult; +import org.junit.platform.engine.TestDescriptor; import org.junit.platform.launcher.Launcher; +import org.junit.platform.launcher.LauncherDiscoveryListener; import org.junit.platform.launcher.LauncherDiscoveryRequest; +import org.junit.platform.launcher.LauncherSession; +import org.junit.platform.launcher.LauncherSessionListener; +import org.junit.platform.launcher.PostDiscoveryFilter; import org.junit.platform.launcher.TestExecutionListener; import org.junit.platform.launcher.TestPlan; import org.junit.platform.launcher.core.LauncherConfig; @@ -51,9 +57,11 @@ void discovery() { ) .build(); - Launcher launcher = LauncherFactory.create(); + try (LauncherSession session = LauncherFactory.openSession()) { + TestPlan testPlan = session.getLauncher().discover(request); - TestPlan testPlan = launcher.discover(request); + // ... discover additional test plans or execute tests + } // end::discovery[] // @formatter:on } @@ -73,16 +81,22 @@ void execution() { ) .build(); - Launcher launcher = LauncherFactory.create(); - - // Register a listener of your choice SummaryGeneratingListener listener = new SummaryGeneratingListener(); - launcher.registerTestExecutionListeners(listener); - launcher.execute(request); + try (LauncherSession session = LauncherFactory.openSession()) { + Launcher launcher = session.getLauncher(); + // Register a listener of your choice + launcher.registerTestExecutionListeners(listener); + // Discover tests and build a test plan + TestPlan testPlan = launcher.discover(request); + // Execute test plan + launcher.execute(testPlan); + // Alternatively, execute the request directly + launcher.execute(request); + } TestExecutionSummary summary = listener.getSummary(); - // Do something with the TestExecutionSummary. + // Do something with the summary... // end::execution[] // @formatter:on @@ -96,19 +110,25 @@ void launcherConfig() { // tag::launcherConfig[] LauncherConfig launcherConfig = LauncherConfig.builder() .enableTestEngineAutoRegistration(false) + .enableLauncherSessionListenerAutoRegistration(false) + .enableLauncherDiscoveryListenerAutoRegistration(false) + .enablePostDiscoveryFilterAutoRegistration(false) .enableTestExecutionListenerAutoRegistration(false) .addTestEngines(new CustomTestEngine()) + .addLauncherSessionListeners(new CustomLauncherSessionListener()) + .addLauncherDiscoveryListeners(new CustomLauncherDiscoveryListener()) + .addPostDiscoveryFilters(new CustomPostDiscoveryFilter()) .addTestExecutionListeners(new LegacyXmlReportGeneratingListener(reportsDir, out)) .addTestExecutionListeners(new CustomTestExecutionListener()) .build(); - Launcher launcher = LauncherFactory.create(launcherConfig); - LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request() .selectors(selectPackage("com.example.mytests")) .build(); - launcher.execute(request); + try (LauncherSession session = LauncherFactory.openSession(launcherConfig)) { + session.getLauncher().execute(request); + } // end::launcherConfig[] // @formatter:on } @@ -120,3 +140,16 @@ class MyTestClass { class CustomTestExecutionListener implements TestExecutionListener { } + +class CustomLauncherSessionListener implements LauncherSessionListener { +} + +class CustomLauncherDiscoveryListener implements LauncherDiscoveryListener { +} + +class CustomPostDiscoveryFilter implements PostDiscoveryFilter { + @Override + public FilterResult apply(TestDescriptor object) { + return FilterResult.included("includes everything"); + } +} diff --git a/documentation/src/test/java/example/callbacks/AbstractDatabaseTests.java b/documentation/src/test/java/example/callbacks/AbstractDatabaseTests.java index aec3d20ec30a..58344c1c7c7c 100644 --- a/documentation/src/test/java/example/callbacks/AbstractDatabaseTests.java +++ b/documentation/src/test/java/example/callbacks/AbstractDatabaseTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/callbacks/BrokenLifecycleMethodConfigDemo.java b/documentation/src/test/java/example/callbacks/BrokenLifecycleMethodConfigDemo.java index 7d3ed08f75eb..cedeb020be28 100644 --- a/documentation/src/test/java/example/callbacks/BrokenLifecycleMethodConfigDemo.java +++ b/documentation/src/test/java/example/callbacks/BrokenLifecycleMethodConfigDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/callbacks/DatabaseTestsDemo.java b/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java index aadda9995f2c..e6dc21582ff2 100644 --- a/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java +++ b/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/callbacks/Extension1.java b/documentation/src/test/java/example/callbacks/Extension1.java index a02451794e86..6bf297f9550f 100644 --- a/documentation/src/test/java/example/callbacks/Extension1.java +++ b/documentation/src/test/java/example/callbacks/Extension1.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/callbacks/Extension2.java b/documentation/src/test/java/example/callbacks/Extension2.java index ea0cf0467eb7..d4e595167bbf 100644 --- a/documentation/src/test/java/example/callbacks/Extension2.java +++ b/documentation/src/test/java/example/callbacks/Extension2.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/callbacks/Logger.java b/documentation/src/test/java/example/callbacks/Logger.java index e3e3a76dcb65..aa7250cab307 100644 --- a/documentation/src/test/java/example/callbacks/Logger.java +++ b/documentation/src/test/java/example/callbacks/Logger.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/defaultmethods/ComparableContract.java b/documentation/src/test/java/example/defaultmethods/ComparableContract.java index 0002836ebada..ac55e4b7ac69 100644 --- a/documentation/src/test/java/example/defaultmethods/ComparableContract.java +++ b/documentation/src/test/java/example/defaultmethods/ComparableContract.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/defaultmethods/EqualsContract.java b/documentation/src/test/java/example/defaultmethods/EqualsContract.java index 47e1252d6236..357c74146466 100644 --- a/documentation/src/test/java/example/defaultmethods/EqualsContract.java +++ b/documentation/src/test/java/example/defaultmethods/EqualsContract.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/defaultmethods/StringTests.java b/documentation/src/test/java/example/defaultmethods/StringTests.java index 965100debfbd..95973a1b2ccd 100644 --- a/documentation/src/test/java/example/defaultmethods/StringTests.java +++ b/documentation/src/test/java/example/defaultmethods/StringTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/defaultmethods/Testable.java b/documentation/src/test/java/example/defaultmethods/Testable.java index 273a225c20ff..90028b28659b 100644 --- a/documentation/src/test/java/example/defaultmethods/Testable.java +++ b/documentation/src/test/java/example/defaultmethods/Testable.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/exception/IgnoreIOExceptionExtension.java b/documentation/src/test/java/example/exception/IgnoreIOExceptionExtension.java index 28505bf3e7a9..1e2e15dfbcb8 100644 --- a/documentation/src/test/java/example/exception/IgnoreIOExceptionExtension.java +++ b/documentation/src/test/java/example/exception/IgnoreIOExceptionExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/exception/IgnoreIOExceptionTests.java b/documentation/src/test/java/example/exception/IgnoreIOExceptionTests.java index f4de1484fad7..690b22dfa328 100644 --- a/documentation/src/test/java/example/exception/IgnoreIOExceptionTests.java +++ b/documentation/src/test/java/example/exception/IgnoreIOExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/exception/MultipleHandlersTestCase.java b/documentation/src/test/java/example/exception/MultipleHandlersTestCase.java index 48ee9ec88d7d..8455247839cd 100644 --- a/documentation/src/test/java/example/exception/MultipleHandlersTestCase.java +++ b/documentation/src/test/java/example/exception/MultipleHandlersTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/exception/RecordStateOnErrorExtension.java b/documentation/src/test/java/example/exception/RecordStateOnErrorExtension.java index 3f0422107036..ddd0a9ad6456 100644 --- a/documentation/src/test/java/example/exception/RecordStateOnErrorExtension.java +++ b/documentation/src/test/java/example/exception/RecordStateOnErrorExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/extensions/Random.java b/documentation/src/test/java/example/extensions/Random.java new file mode 100644 index 000000000000..81a2dc7ee0d8 --- /dev/null +++ b/documentation/src/test/java/example/extensions/Random.java @@ -0,0 +1,26 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.extensions; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +//tag::user_guide[] +@Target({ ElementType.FIELD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(RandomNumberExtension.class) +public @interface Random { +} +//end::user_guide[] diff --git a/documentation/src/test/java/example/extensions/RandomNumberDemo.java b/documentation/src/test/java/example/extensions/RandomNumberDemo.java new file mode 100644 index 000000000000..0b8656a99c3e --- /dev/null +++ b/documentation/src/test/java/example/extensions/RandomNumberDemo.java @@ -0,0 +1,41 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.extensions; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +@Disabled("RandomNumberExtension has not been implemented") +//tag::user_guide[] +class RandomNumberDemo { + + // use random number field in test methods and @BeforeEach + // or @AfterEach lifecycle methods + @Random + private int randomNumber1; + + RandomNumberDemo(@Random int randomNumber2) { + // use random number in constructor + } + + @BeforeEach + void beforeEach(@Random int randomNumber3) { + // use random number in @BeforeEach method + } + + @Test + void test(@Random int randomNumber4) { + // use random number in test method + } + +} +//end::user_guide[] diff --git a/documentation/src/test/java/example/extensions/RandomNumberExtension.java b/documentation/src/test/java/example/extensions/RandomNumberExtension.java new file mode 100644 index 000000000000..598582725319 --- /dev/null +++ b/documentation/src/test/java/example/extensions/RandomNumberExtension.java @@ -0,0 +1,39 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.extensions; + +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; + +class RandomNumberExtension implements BeforeAllCallback, BeforeEachCallback, ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return false; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return null; + } + + @Override + public void beforeAll(ExtensionContext context) { + } + + @Override + public void beforeEach(ExtensionContext context) { + } + +} diff --git a/documentation/src/test/java/example/interceptor/SwingEdtInterceptor.java b/documentation/src/test/java/example/interceptor/SwingEdtInterceptor.java index 78f21e8e3b66..09a805c9dcc1 100644 --- a/documentation/src/test/java/example/interceptor/SwingEdtInterceptor.java +++ b/documentation/src/test/java/example/interceptor/SwingEdtInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/registration/DocumentationDemo.java b/documentation/src/test/java/example/registration/DocumentationDemo.java index fe542cf37c28..008c734b5b94 100644 --- a/documentation/src/test/java/example/registration/DocumentationDemo.java +++ b/documentation/src/test/java/example/registration/DocumentationDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/registration/WebServerDemo.java b/documentation/src/test/java/example/registration/WebServerDemo.java index c99d9c0e70f6..4b839e53a8b4 100644 --- a/documentation/src/test/java/example/registration/WebServerDemo.java +++ b/documentation/src/test/java/example/registration/WebServerDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/session/GlobalSetupTeardownListener.java b/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java new file mode 100644 index 000000000000..eb11410018a3 --- /dev/null +++ b/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java @@ -0,0 +1,92 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.session; + +//tag::user_guide[] +import static java.net.InetAddress.getLoopbackAddress; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.InetSocketAddress; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import com.sun.net.httpserver.HttpServer; + +import org.junit.platform.launcher.LauncherSession; +import org.junit.platform.launcher.LauncherSessionListener; +import org.junit.platform.launcher.TestExecutionListener; +import org.junit.platform.launcher.TestPlan; + +public class GlobalSetupTeardownListener implements LauncherSessionListener { + + private Fixture fixture; + + @Override + public void launcherSessionOpened(LauncherSession session) { + // Avoid setup for test discovery by delaying it until tests are about to be executed + session.getLauncher().registerTestExecutionListeners(new TestExecutionListener() { + @Override + public void testPlanExecutionStarted(TestPlan testPlan) { + //end::user_guide[] + if (!testPlan.getConfigurationParameters().getBoolean("enableHttpServer").orElse(false)) { + // avoid starting multiple HTTP servers unnecessarily from UsingTheLauncherDemo + return; + } + //tag::user_guide[] + if (fixture == null) { + fixture = new Fixture(); + fixture.setUp(); + } + } + }); + } + + @Override + public void launcherSessionClosed(LauncherSession session) { + if (fixture != null) { + fixture.tearDown(); + fixture = null; + } + } + + static class Fixture { + + private HttpServer server; + private ExecutorService executorService; + + void setUp() { + try { + server = HttpServer.create(new InetSocketAddress(getLoopbackAddress(), 0), 0); + } + catch (IOException e) { + throw new UncheckedIOException("Failed to start HTTP server", e); + } + server.createContext("/test", exchange -> { + exchange.sendResponseHeaders(204, -1); + exchange.close(); + }); + executorService = Executors.newCachedThreadPool(); + server.setExecutor(executorService); + server.start(); // <1> + int port = server.getAddress().getPort(); + System.setProperty("http.server.host", getLoopbackAddress().getHostAddress()); // <2> + System.setProperty("http.server.port", String.valueOf(port)); // <3> + } + + void tearDown() { + server.stop(0); // <4> + executorService.shutdownNow(); + } + } + +} +//end::user_guide[] diff --git a/documentation/src/test/java/example/session/HttpTests.java b/documentation/src/test/java/example/session/HttpTests.java new file mode 100644 index 000000000000..124d54fc48e0 --- /dev/null +++ b/documentation/src/test/java/example/session/HttpTests.java @@ -0,0 +1,36 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.session; + +//tag::user_guide[] +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.HttpURLConnection; +import java.net.URL; + +import org.junit.jupiter.api.Test; + +class HttpTests { + + @Test + void respondsWith204() throws Exception { + String host = System.getProperty("http.server.host"); // <1> + String port = System.getProperty("http.server.port"); // <2> + URL url = new URL("https://" + host + ":" + port + "/test"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + int responseCode = connection.getResponseCode(); // <3> + + assertEquals(204, responseCode); // <4> + } +} +//end::user_guide[] diff --git a/documentation/src/test/java/example/testinterface/TestInterfaceDemo.java b/documentation/src/test/java/example/testinterface/TestInterfaceDemo.java index 86b64961afb3..51ba1f8bbec5 100644 --- a/documentation/src/test/java/example/testinterface/TestInterfaceDemo.java +++ b/documentation/src/test/java/example/testinterface/TestInterfaceDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/testinterface/TestInterfaceDynamicTestsDemo.java b/documentation/src/test/java/example/testinterface/TestInterfaceDynamicTestsDemo.java index ee611239e61d..2e751d622652 100644 --- a/documentation/src/test/java/example/testinterface/TestInterfaceDynamicTestsDemo.java +++ b/documentation/src/test/java/example/testinterface/TestInterfaceDynamicTestsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/testinterface/TestLifecycleLogger.java b/documentation/src/test/java/example/testinterface/TestLifecycleLogger.java index d6dae294e16a..935f56133edf 100644 --- a/documentation/src/test/java/example/testinterface/TestLifecycleLogger.java +++ b/documentation/src/test/java/example/testinterface/TestLifecycleLogger.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/testinterface/TimeExecutionLogger.java b/documentation/src/test/java/example/testinterface/TimeExecutionLogger.java index 944e7c72ada7..32f88d3c4391 100644 --- a/documentation/src/test/java/example/testinterface/TimeExecutionLogger.java +++ b/documentation/src/test/java/example/testinterface/TimeExecutionLogger.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/testkit/EngineTestKitAllEventsDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitAllEventsDemo.java index 21f05d439caf..713fb68a86e2 100644 --- a/documentation/src/test/java/example/testkit/EngineTestKitAllEventsDemo.java +++ b/documentation/src/test/java/example/testkit/EngineTestKitAllEventsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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 @@ -13,6 +13,7 @@ // @formatter:off // tag::user_guide[] +import static org.junit.jupiter.api.condition.JRE.JAVA_18; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; import static org.junit.platform.testkit.engine.EventConditions.abortedWithReason; import static org.junit.platform.testkit.engine.EventConditions.container; @@ -32,16 +33,18 @@ import example.ExampleTestCase; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnJre; import org.junit.platform.testkit.engine.EngineTestKit; import org.opentest4j.TestAbortedException; class EngineTestKitAllEventsDemo { @Test + @DisabledOnJre(JAVA_18) // https://github.com/assertj/assertj-core/issues/2340 void verifyAllJupiterEvents() { Writer writer = // create a java.io.Writer for debug output // end::user_guide[] - // For the demo, we are simply swallowing the debug output. + // For the demo, we are swallowing the debug output. new StringWriter(); // tag::user_guide[] diff --git a/documentation/src/test/java/example/testkit/EngineTestKitFailedMethodDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitFailedMethodDemo.java index 78d2173f2ebd..a2ab5c59baf7 100644 --- a/documentation/src/test/java/example/testkit/EngineTestKitFailedMethodDemo.java +++ b/documentation/src/test/java/example/testkit/EngineTestKitFailedMethodDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/testkit/EngineTestKitSkippedMethodDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitSkippedMethodDemo.java index 708dfe598c38..dc4b40ff2bdc 100644 --- a/documentation/src/test/java/example/testkit/EngineTestKitSkippedMethodDemo.java +++ b/documentation/src/test/java/example/testkit/EngineTestKitSkippedMethodDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/testkit/EngineTestKitStatisticsDemo.java b/documentation/src/test/java/example/testkit/EngineTestKitStatisticsDemo.java index ef3ca601494b..399189e67790 100644 --- a/documentation/src/test/java/example/testkit/EngineTestKitStatisticsDemo.java +++ b/documentation/src/test/java/example/testkit/EngineTestKitStatisticsDemo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/timing/TimingExtension.java b/documentation/src/test/java/example/timing/TimingExtension.java index 6c4fcc73027e..f219e8175214 100644 --- a/documentation/src/test/java/example/timing/TimingExtension.java +++ b/documentation/src/test/java/example/timing/TimingExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/timing/TimingExtensionTests.java b/documentation/src/test/java/example/timing/TimingExtensionTests.java index b9b11a1582d6..caa01fc7026b 100644 --- a/documentation/src/test/java/example/timing/TimingExtensionTests.java +++ b/documentation/src/test/java/example/timing/TimingExtensionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/extensions/ExpectToFail.java b/documentation/src/test/java/extensions/ExpectToFail.java index c325a81de5db..4787142e7143 100644 --- a/documentation/src/test/java/extensions/ExpectToFail.java +++ b/documentation/src/test/java/extensions/ExpectToFail.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/org/junit/api/tools/AbstractApiReportWriter.java b/documentation/src/test/java/org/junit/api/tools/AbstractApiReportWriter.java index ed59e28e792b..1e2e30f9e4ed 100644 --- a/documentation/src/test/java/org/junit/api/tools/AbstractApiReportWriter.java +++ b/documentation/src/test/java/org/junit/api/tools/AbstractApiReportWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/org/junit/api/tools/ApiReport.java b/documentation/src/test/java/org/junit/api/tools/ApiReport.java index 067d5318f8eb..c51de7a6b17c 100644 --- a/documentation/src/test/java/org/junit/api/tools/ApiReport.java +++ b/documentation/src/test/java/org/junit/api/tools/ApiReport.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/org/junit/api/tools/ApiReportGenerator.java b/documentation/src/test/java/org/junit/api/tools/ApiReportGenerator.java index 963d0c4553cb..b6f88801359e 100644 --- a/documentation/src/test/java/org/junit/api/tools/ApiReportGenerator.java +++ b/documentation/src/test/java/org/junit/api/tools/ApiReportGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/org/junit/api/tools/ApiReportWriter.java b/documentation/src/test/java/org/junit/api/tools/ApiReportWriter.java index 8fcadb342744..12976610581c 100644 --- a/documentation/src/test/java/org/junit/api/tools/ApiReportWriter.java +++ b/documentation/src/test/java/org/junit/api/tools/ApiReportWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/org/junit/api/tools/AsciidocApiReportWriter.java b/documentation/src/test/java/org/junit/api/tools/AsciidocApiReportWriter.java index 98f5f8fb2a51..f29f979bd102 100644 --- a/documentation/src/test/java/org/junit/api/tools/AsciidocApiReportWriter.java +++ b/documentation/src/test/java/org/junit/api/tools/AsciidocApiReportWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/org/junit/api/tools/HtmlApiReportWriter.java b/documentation/src/test/java/org/junit/api/tools/HtmlApiReportWriter.java index b260f8ff1ce3..0498deefff02 100644 --- a/documentation/src/test/java/org/junit/api/tools/HtmlApiReportWriter.java +++ b/documentation/src/test/java/org/junit/api/tools/HtmlApiReportWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/org/junit/api/tools/MarkdownApiReportWriter.java b/documentation/src/test/java/org/junit/api/tools/MarkdownApiReportWriter.java index 65f0f918b2f8..653ddc39b5b1 100644 --- a/documentation/src/test/java/org/junit/api/tools/MarkdownApiReportWriter.java +++ b/documentation/src/test/java/org/junit/api/tools/MarkdownApiReportWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/kotlin/example/FibonacciCalculator.kt b/documentation/src/test/kotlin/example/FibonacciCalculator.kt index ae16322fc335..c7c111039bec 100644 --- a/documentation/src/test/kotlin/example/FibonacciCalculator.kt +++ b/documentation/src/test/kotlin/example/FibonacciCalculator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/kotlin/example/KotlinAssertionsDemo.kt b/documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt index 23c901fa1672..723ea2ac43ae 100644 --- a/documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt +++ b/documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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/kotlin/example/registration/KotlinWebServerDemo.kt b/documentation/src/test/kotlin/example/registration/KotlinWebServerDemo.kt index 73b36a59365e..6448a2baedfd 100644 --- a/documentation/src/test/kotlin/example/registration/KotlinWebServerDemo.kt +++ b/documentation/src/test/kotlin/example/registration/KotlinWebServerDemo.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-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 @@ -17,7 +17,7 @@ import org.junit.jupiter.api.extension.RegisterExtension class KotlinWebServerDemo { companion object { - @JvmField + @JvmStatic @RegisterExtension val server = WebServerExtension.builder() .enableSecurity(false) diff --git a/documentation/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener b/documentation/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener new file mode 100644 index 000000000000..f6d297627eeb --- /dev/null +++ b/documentation/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener @@ -0,0 +1 @@ +example.session.GlobalSetupTeardownListener diff --git a/documentation/src/test/resources/two-column.csv b/documentation/src/test/resources/two-column.csv index 2ef0d215adac..7ebb4c545f1b 100644 --- a/documentation/src/test/resources/two-column.csv +++ b/documentation/src/test/resources/two-column.csv @@ -1,4 +1,5 @@ -Country, reference +COUNTRY, REFERENCE Sweden, 1 Poland, 2 "United States of America", 3 +France, 700_000 diff --git a/gradle.properties b/gradle.properties index 30e3b4488249..1d9dfcfed69b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,58 +1,31 @@ group = org.junit -version = 5.7.0 +version = 5.8.2 jupiterGroup = org.junit.jupiter platformGroup = org.junit.platform -platformVersion = 1.7.0 +platformVersion = 1.8.2 vintageGroup = org.junit.vintage -vintageVersion = 5.7.0 +vintageVersion = 5.8.2 defaultBuiltBy = JUnit Team # We need more metaspace due to apparent memory leak in Asciidoctor/JRuby -org.gradle.jvmargs=-Xmx1g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError +# The exports are needed due to https://github.com/diffplug/spotless/issues/834 +org.gradle.jvmargs=-Xmx1g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError \ + --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED org.gradle.caching=true org.gradle.parallel=true +org.gradle.java.installations.fromEnv=JDK8,JDK16,JDK17,JDK18 -# Dependencies -apiguardian.version=1.1.0 -assertj.version=3.16.1 -junit4.version=4.13 -junit4Min.version=4.12 -opentest4j.version=1.2.0 -picocli.version=4.5.0 -univocity-parsers.version=2.9.0 +# Test Distribution +gradle.internal.testdistribution.writeTraceFile=true -# Test Dependencies -archunit.version=0.14.1 -bartholdy.version=0.2.3 -classgraph.version=4.8.87 -commons-io.version=2.7 -kotlinx-coroutines-core.version=1.3.9 -groovy.version=3.0.5 -log4j.version=2.13.3 -mockito.version=3.5.0 -slf4j.version=1.7.30 -spock.version=1.3-groovy-2.5 - -# Tools -checkstyle.version=8.31 -jacoco.version=0.8.5 -jmh.version=1.25 -ktlint.version=0.35.0 -surefire.version=2.22.2 -bnd.version=5.1.2 - -# Plugins -gradle.enterprise.plugin.version=3.3.1 -versioning.plugin.version=2.14.0 -versions.plugin.version=0.29.0 -spotless.plugin.version=5.1.1 -git-publish.plugin.version=2.1.3 -kotlin.plugin.version=1.4.0 -asciidoctor-pdf.version=1.5.3 -asciidoctor.plugin.version=3.2.0 -jmh.plugin.version=0.5.0 -nohttp.plugin.version=0.0.5.RELEASE +# Omit automatic compile dependency on kotlin-stdlib +# https://kotlinlang.org/docs/gradle.html#dependency-on-the-standard-library +kotlin.stdlib.default.dependency=false diff --git a/gradle/build-scan-user-data.gradle b/gradle/build-scan-user-data.gradle deleted file mode 100644 index 8436692dc512..000000000000 --- a/gradle/build-scan-user-data.gradle +++ /dev/null @@ -1,215 +0,0 @@ -// Source: https://github.com/gradle/gradle-build-scan-snippets/blob/master/guided-trials-default-custom-user-data/default-custom-user-data.gradle - -tagOs() -tagIde() -tagCiOrLocal() -addCiMetadata() -addGitMetadata() -addTestParallelization() - -// Add here other scripts, if needed -//apply from:"${rootProject.projectDir}/<>" - -void tagOs() { - buildScan.tag System.getProperty('os.name') -} - -void tagIde() { - if (project.hasProperty('android.injected.invoked.from.ide')) { - buildScan.tag 'Android Studio' - } else if (System.getProperty('idea.version')) { - buildScan.tag 'IntelliJ IDEA' - } else if (System.getProperty('eclipse.buildId')) { - buildScan.tag 'Eclipse' - } else if (!isCi()) { - buildScan.tag 'Cmd Line' - } -} - -void tagCiOrLocal() { - buildScan.tag(isCi() ? 'CI' : 'LOCAL') -} - -void addCiMetadata() { - if (isJenkins()) { - if (System.getenv('BUILD_URL')) { - buildScan.link 'Jenkins build', System.getenv('BUILD_URL') - } - if (System.getenv('BUILD_NUMBER')) { - buildScan.value 'CI build number', System.getenv('BUILD_NUMBER') - } - if (System.getenv('NODE_NAME')) { - def agentName = System.getenv('NODE_NAME') == 'master' ? 'master-node' : System.getenv('NODE_NAME') - buildScan.tag agentName - buildScan.value 'CI node name', agentName - } - if (System.getenv('JOB_NAME')) { - def jobNameLabel = 'CI job' - def jobName = System.getenv('JOB_NAME') - buildScan.value jobNameLabel, jobName - addCustomValueSearchLink 'CI job build scans', [(jobNameLabel): jobName] - } - if (System.getenv('STAGE_NAME')) { - def stageNameLabel = 'CI stage' - def stageName = System.getenv('STAGE_NAME') - buildScan.value stageNameLabel, stageName - addCustomValueSearchLink 'CI stage build scans', [(stageNameLabel): stageName] - } - } - - if (isTeamCity()) { - def teamCityConfigurationFileProp = 'teamcity.configuration.properties.file' - if (project.hasProperty(teamCityConfigurationFileProp)) { - def properties = new Properties() - properties.load(new FileInputStream("${project.property(teamCityConfigurationFileProp)}")) - def teamCityServerUrl = properties.getProperty("teamcity.serverUrl") - if (teamCityServerUrl && project.hasProperty('build.number') && project.hasProperty('teamcity.buildType.id')) { - def teamCityBuildNumber = project.property('build.number') - def teamCityBuildTypeId = project.property('teamcity.buildType.id') - buildScan.link 'TeamCity build', "${appendIfMissing(teamCityServerUrl, '/')}viewLog.html?buildNumber=${teamCityBuildNumber}&buildTypeId=${teamCityBuildTypeId}" - } - } - if (project.hasProperty('build.number')) { - buildScan.value 'CI build number', project.property('build.number') - } - if (project.hasProperty('agent.name')) { - def agentName = project.property('agent.name') - buildScan.tag agentName - buildScan.value 'CI agent name', agentName - } - } - - if (isCircleCI()) { - if (System.getenv('CIRCLE_BUILD_URL')) { - buildScan.link 'CircleCI build', System.getenv('CIRCLE_BUILD_URL') - } - if (System.getenv('CIRCLE_BUILD_NUM')) { - buildScan.value 'CI build number', System.getenv('CIRCLE_BUILD_NUM') - } - if (System.getenv('CIRCLE_JOB')) { - def jobLabel = 'CI job' - def job = System.getenv('CIRCLE_JOB') - buildScan.value jobLabel, job - addCustomValueSearchLink 'CI job build scans', [(jobLabel): job] - } - if (System.getenv('CIRCLE_WORKFLOW_ID')) { - def workflowIdLabel = 'CI workflow' - def workflowId = System.getenv('CIRCLE_WORKFLOW_ID') - buildScan.value workflowIdLabel, workflowId - addCustomValueSearchLink 'CI workflow build scans', [(workflowIdLabel): workflowId] - } - } - - if (isBamboo()) { - if (System.getenv('bamboo_resultsUrl')) { - buildScan.link 'Bamboo build', System.getenv('bamboo_resultsUrl') - } - if (System.getenv('bamboo_buildNumber')) { - buildScan.value 'CI build number', System.getenv('bamboo_buildNumber') - } - if (System.getenv('bamboo_planName')) { - def planNameLabel = 'CI plan' - def planName = System.getenv('bamboo_planName') - buildScan.value planNameLabel, planName - addCustomValueSearchLink 'CI plan build scans', [(planNameLabel): planName] - } - if (System.getenv('bamboo_buildPlanName')) { - def jobNameLabel = 'CI job' - def jobName = System.getenv('bamboo_buildPlanName') - buildScan.value jobNameLabel, jobName - addCustomValueSearchLink 'CI job build scans', [(jobNameLabel): jobName] - } - if (System.getenv('bamboo_agentId')) { - def agentId = System.getenv('bamboo_agentId') - buildScan.tag agentId - buildScan.value 'CI agent ID', agentId - } - } - - if (isGitHubActions()) { - def repo = System.getenv('GITHUB_REPOSITORY') - def runId = System.getenv('GITHUB_RUN_ID') - if (repo && runId) { - buildScan.link 'GitHub Actions run', "https://github.com/$repo/actions/runs/$runId" - } - if (System.getenv('GITHUB_WORKFLOW')) { - def ghActionWorkflowLabel = 'GitHub workflow' - def ghActionWorkflowName = System.getenv('GITHUB_WORKFLOW') - buildScan.value ghActionWorkflowLabel, ghActionWorkflowName - addCustomValueSearchLink 'GitHub workflow build scans', [(ghActionWorkflowLabel): ghActionWorkflowName] - } - } -} - -void addGitMetadata() { - buildScan.background { - def gitCommitId = versioning.info.commit - def gitBranchName = versioning.info.branch - def gitDirty = versioning.info.dirty - - if (gitCommitId) { - def commitIdLabel = 'Git commit id' - value commitIdLabel, gitCommitId - addCustomValueSearchLink 'Git commit id build scans', [(commitIdLabel): gitCommitId] - link 'Github Source', "https://github.com/junit-team/junit5/tree/" + gitCommitId - } - if (gitBranchName) { - tag gitBranchName - value 'Git branch', gitBranchName - } - if (gitDirty) { - tag 'Dirty' - } - } -} - -void addTestParallelization() { - allprojects { p -> - p.tasks.withType(Test).configureEach { t -> doFirst { buildScan.value "${t.identityPath}#maxParallelForks", t.maxParallelForks.toString() } } - } -} - -static boolean isCi() { - isJenkins() || isTeamCity() || isCircleCI() || isBamboo() || isGitHubActions() -} - -static boolean isJenkins() { - System.getenv('JENKINS_URL') -} - -static boolean isTeamCity() { - System.getenv('TEAMCITY_VERSION') -} - -static boolean isCircleCI() { - System.getenv('CIRCLECI') -} - -static boolean isBamboo() { - System.getenv('bamboo_resultsUrl') -} - -static boolean isGitHubActions() { - System.getenv('GITHUB_ACTIONS') -} - -void addCustomValueSearchLink(String title, Map search) { - if (buildScan.server) { - buildScan.link title, customValueSearchUrl(search) - } -} - -String customValueSearchUrl(Map search) { - def query = search.collect { name, value -> - "search.names=${encodeURL(name)}&search.values=${encodeURL(value)}" - }.join('&') - "${appendIfMissing(buildScan.server, '/')}scans?$query#selection.buildScanB=%7BSCAN_ID%7D" -} - -static String encodeURL(String url) { - URLEncoder.encode(url, 'UTF-8') -} - -static String appendIfMissing(String str, String suffix) { - str.endsWith(suffix) ? str : str + suffix -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000000..a4c72067e6f9 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,43 @@ +[versions] +apiguardian = "1.1.2" +asciidoctor-pdf = "1.5.3" +assertj = "3.20.2" +checkstyle = "8.36.2" +jacoco = "0.8.7" +jmh = "1.32" +junit4 = "4.13.2" +junit4Osgi = "4.13.2_1" +junit4Min = "4.12" +ktlint = "0.35.0" +log4j = "2.14.1" +opentest4j = "1.2.0" +surefire = "2.22.2" + +[libraries] +apiguardian = { module = "org.apiguardian:apiguardian-api", version.ref = "apiguardian" } +archunit = { module = "com.tngtech.archunit:archunit-junit5", version = "0.20.1" } +assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" } +bartholdy = { module = "de.sormuras:bartholdy", version = "0.2.3" } +bnd = { module = "biz.aQute.bnd:biz.aQute.bndlib", version = "5.3.0" } +classgraph = { module = "io.github.classgraph:classgraph", version = "4.8.106" } +commons-io = { module = "commons-io:commons-io", version = "2.9.0" } +groovy4 = { module = "org.apache.groovy:groovy", version = "4.0.0-beta-1" } +groovy2-bom = { module = "org.codehaus.groovy:groovy-bom", version = "2.5.14" } +hamcrest = { module = "org.hamcrest:hamcrest", version = "2.2" } +jfrunit = { module = "org.moditect.jfrunit:jfrunit", version = "1.0.0.Alpha1" } +jmh-core = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh" } +jmh-generator-annprocess = { module = "org.openjdk.jmh:jmh-generator-annprocess", version.ref = "jmh" } +joox = { module = "org.jooq:joox", version = "1.6.2" } +junit4 = { module = "junit:junit", version = { require = "[4.12,)", prefer = "4.13.2" } } +kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.4.3" } +log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" } +log4j-jul = { module = "org.apache.logging.log4j:log4j-jul", version.ref = "log4j" } +mockito = { module = "org.mockito:mockito-junit-jupiter", version = "3.10.0" } +opentest4j = { module = "org.opentest4j:opentest4j", version.ref = "opentest4j" } +picocli = { module = "info.picocli:picocli", version = "4.6.1" } +slf4j-julBinding = { module = "org.slf4j:slf4j-jdk14", version = "1.7.30" } +spock1 = { module = "org.spockframework:spock-core", version = "1.3-groovy-2.5" } +univocity-parsers = { module = "com.univocity:univocity-parsers", version = "2.9.1" } + +[bundles] +log4j = ["log4j-core", "log4j-jul"] diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec..7454180f2ae8 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4a1a99959e8b..a2e01c0dffec 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=7873ed5287f47ca03549ab8dcb6dc877ac7f0e3d7b1eb12685161d10080910ac -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip +distributionSha256Sum=f581709a9c35e9cb92e16f585d2c4bc99b2b1a5f85d2badbd3dc6bff59e1e6dd +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0c811f..1b6c787337ff 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/junit-bom/junit-bom.gradle.kts b/junit-bom/junit-bom.gradle.kts index 6d50be082030..4a7a327a1789 100644 --- a/junit-bom/junit-bom.gradle.kts +++ b/junit-bom/junit-bom.gradle.kts @@ -1,7 +1,7 @@ plugins { `java-platform` + `java-toolchain-conventions` `publishing-conventions` - `custom-java-home` } description = "${rootProject.description} (Bill of Materials)" diff --git a/junit-jupiter-api/junit-jupiter-api.gradle.kts b/junit-jupiter-api/junit-jupiter-api.gradle.kts index f7a66e3c419b..a356601ca00b 100644 --- a/junit-jupiter-api/junit-jupiter-api.gradle.kts +++ b/junit-jupiter-api/junit-jupiter-api.gradle.kts @@ -6,12 +6,11 @@ plugins { description = "JUnit Jupiter API" dependencies { - internal(platform(project(":dependencies"))) + api(platform(projects.junitBom)) + api(libs.opentest4j) + api(projects.junitPlatformCommons) - api(platform(project(":junit-bom"))) - api("org.apiguardian:apiguardian-api") - api("org.opentest4j:opentest4j") - api(project(":junit-platform-commons")) + compileOnlyApi(libs.apiguardian) - compileOnly("org.jetbrains.kotlin:kotlin-stdlib") + compileOnly(kotlin("stdlib")) } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterAll.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterAll.java index 6ae092d20b64..746452c7a9c7 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterAll.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterAll.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterEach.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterEach.java index 1c693e922b49..f95ed4f53a47 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterEach.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterEach.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertAll.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertAll.java index 0cf00416fde0..b083e3274320 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertAll.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertAll.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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 @@ -62,8 +62,8 @@ static void assertAll(String heading, Stream executables) { Preconditions.notNull(executables, "executables stream must not be null"); List failures = executables // - .peek(executable -> Preconditions.notNull(executable, "individual executables must not be null"))// .map(executable -> { + Preconditions.notNull(executable, "individual executables must not be null"); try { executable.execute(); return null; diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java index 67b75c0bc608..32a0707efd7b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java index 20b9ba749fb5..2e7f00afa57f 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java index 2ed37d219f6f..a66ff5d3bb38 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java index 01c67a0e87ea..fe3239d354a0 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java new file mode 100644 index 000000000000..f6fbff1420c5 --- /dev/null +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java @@ -0,0 +1,56 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.api; + +import static org.junit.jupiter.api.AssertionUtils.buildPrefix; +import static org.junit.jupiter.api.AssertionUtils.format; +import static org.junit.jupiter.api.AssertionUtils.nullSafeGet; + +import java.util.function.Supplier; + +import org.opentest4j.AssertionFailedError; + +/** + * {@code AssertInstanceOf} is a collection of utility methods that support + * asserting that an object is of an expected type — in other words, if it + * can be assigned to the expected type. + * + * @since 5.8 + */ +class AssertInstanceOf { + + private AssertInstanceOf() { + /* no-op */ + } + + static T assertInstanceOf(Class expectedType, Object actualValue) { + return assertInstanceOf(expectedType, actualValue, (Object) null); + } + + static T assertInstanceOf(Class expectedType, Object actualValue, String message) { + return assertInstanceOf(expectedType, actualValue, (Object) message); + } + + static T assertInstanceOf(Class expectedType, Object actualValue, Supplier messageSupplier) { + return assertInstanceOf(expectedType, actualValue, (Object) messageSupplier); + } + + private static T assertInstanceOf(Class expectedType, Object actualValue, Object messageOrSupplier) { + if (!expectedType.isInstance(actualValue)) { + String reason = (actualValue == null ? "Unexpected null value" : "Unexpected type"); + String message = buildPrefix(nullSafeGet(messageOrSupplier)) + + format(expectedType, actualValue == null ? null : actualValue.getClass(), reason); + throw new AssertionFailedError(message); + } + return expectedType.cast(actualValue); + } + +} diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java index 98df2ba54493..8cc9f609bebe 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java index 2e65d69995a3..eb580b75b15f 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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 @@ -127,7 +127,7 @@ void assertLinesMatchWithFastForward() { } String actualLine = actualDeque.peek(); - // trivial case: take the fast path when they simply match + // trivial case: take the fast path when they match if (matches(expectedLine, actualLine)) { actualDeque.pop(); continue; // main @@ -136,10 +136,10 @@ void assertLinesMatchWithFastForward() { // fast-forward marker found in expected line: fast-forward actual line... if (isFastForwardLine(expectedLine)) { int fastForwardLimit = parseFastForwardLimit(expectedLine); + int actualRemaining = actualDeque.size(); // trivial case: fast-forward marker was in last expected line if (expectedDeque.isEmpty()) { - int actualRemaining = actualDeque.size(); // no limit given or perfect match? we're done. if (fastForwardLimit == Integer.MAX_VALUE || fastForwardLimit == actualRemaining) { return; @@ -150,6 +150,10 @@ void assertLinesMatchWithFastForward() { // fast-forward limit was given: use it if (fastForwardLimit != Integer.MAX_VALUE) { + if (actualRemaining < fastForwardLimit) { + fail("fast-forward(%d) error: not enough actual lines remaining (%s)", fastForwardLimit, + actualRemaining); + } // fast-forward now: actualDeque.pop(fastForwardLimit) for (int i = 0; i < fastForwardLimit; i++) { actualDeque.pop(); @@ -202,7 +206,8 @@ static boolean isFastForwardLine(String line) { } static int parseFastForwardLimit(String fastForwardLine) { - String text = fastForwardLine.trim().substring(2, fastForwardLine.length() - 2).trim(); + fastForwardLine = fastForwardLine.trim(); + String text = fastForwardLine.substring(2, fastForwardLine.length() - 2).trim(); try { int limit = Integer.parseInt(text); condition(limit > 0, () -> format("fast-forward(%d) limit must be greater than zero", limit)); diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java index abc3295e1974..cf1655bd9f66 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java index 9d2fb08f5478..6f6b8f978fed 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java index f596080dd42d..b8635cb17f94 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java index d730f2555b27..f843202d4a20 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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 @@ -12,6 +12,7 @@ import static org.junit.jupiter.api.AssertionUtils.buildPrefix; import static org.junit.jupiter.api.AssertionUtils.fail; +import static org.junit.jupiter.api.AssertionUtils.format; import static org.junit.jupiter.api.AssertionUtils.nullSafeGet; import java.util.function.Supplier; @@ -45,6 +46,13 @@ static void assertNull(Object actual, Supplier messageSupplier) { } private static void failNotNull(Object actual, String message) { - fail(buildPrefix(message) + "expected: but was: <" + actual + ">", null, actual); + String stringRepresentation = actual.toString(); + if (stringRepresentation == null || stringRepresentation.equals("null")) { + fail(format(null, actual, message), null, actual); + } + else { + fail(buildPrefix(message) + "expected: but was: <" + actual + ">", null, actual); + } } + } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java index 06056746b5e3..620c52a8ac79 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java index 675e1b717b8e..0a6b0d4864b4 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java new file mode 100644 index 000000000000..a2852ed390d1 --- /dev/null +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java @@ -0,0 +1,74 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.api; + +import static org.junit.jupiter.api.AssertionUtils.buildPrefix; +import static org.junit.jupiter.api.AssertionUtils.format; +import static org.junit.jupiter.api.AssertionUtils.getCanonicalName; +import static org.junit.jupiter.api.AssertionUtils.nullSafeGet; + +import java.util.function.Supplier; + +import org.junit.jupiter.api.function.Executable; +import org.junit.platform.commons.util.UnrecoverableExceptions; +import org.opentest4j.AssertionFailedError; + +/** + * {@code AssertThrowsExactly} is a collection of utility methods that support asserting + * an exception of an exact type is thrown. + * + * @since 5.8 + */ +class AssertThrowsExactly { + + private AssertThrowsExactly() { + /* no-op */ + } + + static T assertThrowsExactly(Class expectedType, Executable executable) { + return assertThrowsExactly(expectedType, executable, (Object) null); + } + + static T assertThrowsExactly(Class expectedType, Executable executable, String message) { + return assertThrowsExactly(expectedType, executable, (Object) message); + } + + static T assertThrowsExactly(Class expectedType, Executable executable, + Supplier messageSupplier) { + + return assertThrowsExactly(expectedType, executable, (Object) messageSupplier); + } + + @SuppressWarnings("unchecked") + private static T assertThrowsExactly(Class expectedType, Executable executable, + Object messageOrSupplier) { + + try { + executable.execute(); + } + catch (Throwable actualException) { + if (expectedType.equals(actualException.getClass())) { + return (T) actualException; + } + else { + UnrecoverableExceptions.rethrowIfUnrecoverable(actualException); + String message = buildPrefix(nullSafeGet(messageOrSupplier)) + + format(expectedType, actualException.getClass(), "Unexpected exception type thrown"); + throw new AssertionFailedError(message, actualException); + } + } + + String message = buildPrefix(nullSafeGet(messageOrSupplier)) + + String.format("Expected %s to be thrown, but nothing was thrown.", getCanonicalName(expectedType)); + throw new AssertionFailedError(message); + } + +} diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java index b8f6a029bea4..e3043ef42804 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java index 853f6051cb48..2aba46841a9c 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java index 0aba77997855..faeeb6ab6808 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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 @@ -123,6 +123,10 @@ static String formatValues(Object expected, Object actual) { } private static String formatClassAndValue(Object value, String valueString) { + // If the value is null, return instead of null. + if (value == null) { + return ""; + } String classAndHash = getClassName(value) + toHash(value); // if it's a class, there's no need to repeat the class name contained in the valueString. return (value instanceof Class ? "<" + classAndHash + ">" : classAndHash + "<" + valueString + ">"); diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assertions.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assertions.java index 59ebdf61f3b0..2e96dcd06c2b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assertions.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assertions.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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,6 +10,7 @@ package org.junit.jupiter.api; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; import java.time.Duration; @@ -33,6 +34,22 @@ *

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. + *

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 assumption the assumption to validate * @param executable the block of code to execute if the assumption is valid diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeAll.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeAll.java index 057cddd0ae30..98665c5427ce 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeAll.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeAll.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeEach.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeEach.java index 4ea0738f2e59..2fc46372e0c3 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeEach.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeEach.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassDescriptor.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassDescriptor.java new file mode 100644 index 000000000000..9a6a660b9497 --- /dev/null +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassDescriptor.java @@ -0,0 +1,87 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.api; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.Optional; + +import org.apiguardian.api.API; + +/** + * {@code ClassDescriptor} encapsulates functionality for a given {@link Class}. + * + * @since 5.8 + * @see ClassOrdererContext + */ +@API(status = EXPERIMENTAL, since = "5.8") +public interface ClassDescriptor { + + /** + * Get the class for this descriptor. + * + * @return the class; never {@code null} + */ + Class getTestClass(); + + /** + * Get the display name for this descriptor's {@link #getTestClass() class}. + * + * @return the display name for this descriptor's class; never {@code null} + * or blank + */ + String getDisplayName(); + + /** + * Determine if an annotation of {@code annotationType} is either + * present or meta-present on the {@link Class} for + * this descriptor. + * + * @param annotationType the annotation type to search for; never {@code null} + * @return {@code true} if the annotation is present or meta-present + * @see #findAnnotation(Class) + * @see #findRepeatableAnnotations(Class) + */ + boolean isAnnotated(Class annotationType); + + /** + * Find the first annotation of {@code annotationType} that is either + * present or meta-present on the {@link Class} for + * this descriptor. + * + * @param the annotation type + * @param annotationType the annotation type to search for; never {@code null} + * @return an {@code Optional} containing the annotation; never {@code null} but + * potentially empty + * @see #isAnnotated(Class) + * @see #findRepeatableAnnotations(Class) + */ + Optional findAnnotation(Class annotationType); + + /** + * Find all repeatable {@linkplain Annotation annotations} of + * {@code annotationType} that are either present or + * meta-present on the {@link Class} for this descriptor. + * + * @param the annotation type + * @param annotationType the repeatable annotation type to search for; never + * {@code null} + * @return the list of all such annotations found; neither {@code null} nor + * mutable, but potentially empty + * @see #isAnnotated(Class) + * @see #findAnnotation(Class) + * @see java.lang.annotation.Repeatable + */ + List findRepeatableAnnotations(Class annotationType); + +} diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrderer.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrderer.java new file mode 100644 index 000000000000..2bc5dfa71d29 --- /dev/null +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrderer.java @@ -0,0 +1,255 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.api; + +import static java.util.Comparator.comparingInt; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Optional; + +import org.apiguardian.api.API; +import org.junit.platform.commons.logging.Logger; +import org.junit.platform.commons.logging.LoggerFactory; + +/** + * {@code ClassOrderer} defines the API for ordering top-level test classes and + * {@link Nested @Nested} test classes. + * + *

In this context, the term "test class" refers to any class containing methods + * annotated with {@code @Test}, {@code @RepeatedTest}, {@code @ParameterizedTest}, + * {@code @TestFactory}, or {@code @TestTemplate}. + * + *

Top-level test classes will be ordered relative to each other; whereas, + * {@code @Nested} test classes will be ordered relative to other {@code @Nested} + * test classes sharing the same {@linkplain Class#getEnclosingClass() enclosing + * class}. + * + *

A {@link ClassOrderer} can be configured globally for the entire + * test suite via the {@code junit.jupiter.testclass.order.default} configuration + * parameter (see the User Guide for details) or locally for + * {@link Nested @Nested} test classes via the {@link TestClassOrder @TestClassOrder} + * annotation. + * + *

Built-in Implementations

+ * + *

JUnit Jupiter provides the following built-in {@code ClassOrderer} + * implementations. + * + *

    + *
  • {@link ClassOrderer.ClassName}
  • + *
  • {@link ClassOrderer.DisplayName}
  • + *
  • {@link ClassOrderer.OrderAnnotation}
  • + *
  • {@link ClassOrderer.Random}
  • + *
+ * + * @since 5.8 + * @see TestClassOrder + * @see ClassOrdererContext + * @see #orderClasses(ClassOrdererContext) + * @see MethodOrderer + */ +@API(status = EXPERIMENTAL, since = "5.8") +public interface ClassOrderer { + + /** + * Order the classes encapsulated in the supplied {@link ClassOrdererContext}. + * + *

The classes to order or sort are made indirectly available via + * {@link ClassOrdererContext#getClassDescriptors()}. Since this method + * has a {@code void} return type, the list of class descriptors must be + * modified directly. + * + *

For example, a simplified implementation of the {@link ClassOrderer.Random} + * {@code ClassOrderer} might look like the following. + * + *

+	 * public void orderClasses(ClassOrdererContext context) {
+	 *     Collections.shuffle(context.getClassDescriptors());
+	 * }
+	 * 
+ * + * @param context the {@code ClassOrdererContext} containing the + * {@linkplain ClassDescriptor class descriptors} to order; never {@code null} + */ + void orderClasses(ClassOrdererContext context); + + /** + * {@code ClassOrderer} that sorts classes alphanumerically based on their + * fully qualified names using {@link String#compareTo(String)}. + */ + class ClassName implements ClassOrderer { + + public ClassName() { + } + + /** + * Sort the classes encapsulated in the supplied + * {@link ClassOrdererContext} alphanumerically based on their fully + * qualified names. + */ + @Override + public void orderClasses(ClassOrdererContext context) { + context.getClassDescriptors().sort(comparator); + } + + private static final Comparator comparator = Comparator.comparing( + descriptor -> descriptor.getTestClass().getName()); + } + + /** + * {@code ClassOrderer} that sorts classes alphanumerically based on their + * display names using {@link String#compareTo(String)} + */ + class DisplayName implements ClassOrderer { + + public DisplayName() { + } + + /** + * Sort the classes encapsulated in the supplied + * {@link ClassOrdererContext} alphanumerically based on their display + * names. + */ + @Override + public void orderClasses(ClassOrdererContext context) { + context.getClassDescriptors().sort(comparator); + } + + private static final Comparator comparator = Comparator.comparing( + ClassDescriptor::getDisplayName); + } + + /** + * {@code ClassOrderer} that sorts classes based on the {@link Order @Order} + * annotation. + * + *

Any classes that are assigned the same order value will be sorted + * arbitrarily adjacent to each other. + * + *

Any classes not annotated with {@code @Order} will be assigned the + * {@link Order#DEFAULT default order} value which will effectively cause them + * to appear at the end of the sorted list, unless certain classes are assigned + * an explicit order value greater than the default order value. Any classes + * assigned an explicit order value greater than the default order value will + * appear after non-annotated classes in the sorted list. + */ + class OrderAnnotation implements ClassOrderer { + + public OrderAnnotation() { + } + + /** + * Sort the classes encapsulated in the supplied + * {@link ClassOrdererContext} based on the {@link Order @Order} + * annotation. + */ + @Override + public void orderClasses(ClassOrdererContext context) { + context.getClassDescriptors().sort(comparingInt(OrderAnnotation::getOrder)); + } + + private static int getOrder(ClassDescriptor descriptor) { + return descriptor.findAnnotation(Order.class).map(Order::value).orElse(Order.DEFAULT); + } + } + + /** + * {@code ClassOrderer} that orders classes pseudo-randomly. + * + *

Custom Seed

+ * + *

By default, the random seed used for ordering classes is the + * value returned by {@link System#nanoTime()} during static initialization + * of this class. In order to support repeatable builds, the value of the + * default random seed is logged at {@code CONFIG} level. In addition, a + * custom seed (potentially the default seed from the previous test plan + * execution) may be specified via the {@link Random#RANDOM_SEED_PROPERTY_NAME + * junit.jupiter.execution.order.random.seed} configuration parameter + * which can be supplied via the {@code Launcher} API, build tools (e.g., + * Gradle and Maven), a JVM system property, or the JUnit Platform configuration + * file (i.e., a file named {@code junit-platform.properties} in the root of + * the class path). Consult the User Guide for further information. + * + * @see Random#RANDOM_SEED_PROPERTY_NAME + * @see java.util.Random + */ + class Random implements ClassOrderer { + + private static final Logger logger = LoggerFactory.getLogger(Random.class); + + /** + * Default seed, which is generated during initialization of this class + * via {@link System#nanoTime()} for reproducibility of tests. + */ + private static final long DEFAULT_SEED; + + static { + DEFAULT_SEED = System.nanoTime(); + logger.config(() -> "ClassOrderer.Random default seed: " + DEFAULT_SEED); + } + + /** + * Property name used to set the random seed used by this + * {@code ClassOrderer}: {@value} + * + *

The same property is used by {@link MethodOrderer.Random} for + * consistency between the two random orderers. + * + *

Supported Values

+ * + *

Supported values include any string that can be converted to a + * {@link Long} via {@link Long#valueOf(String)}. + * + *

If not specified or if the specified value cannot be converted to + * a {@link Long}, the default random seed will be used (see the + * {@linkplain Random class-level Javadoc} for details). + * + * @see MethodOrderer.Random + */ + public static final String RANDOM_SEED_PROPERTY_NAME = MethodOrderer.Random.RANDOM_SEED_PROPERTY_NAME; + + public Random() { + } + + /** + * Order the classes encapsulated in the supplied + * {@link ClassOrdererContext} pseudo-randomly. + */ + @Override + public void orderClasses(ClassOrdererContext context) { + Collections.shuffle(context.getClassDescriptors(), + new java.util.Random(getCustomSeed(context).orElse(DEFAULT_SEED))); + } + + private Optional getCustomSeed(ClassOrdererContext context) { + return context.getConfigurationParameter(RANDOM_SEED_PROPERTY_NAME).map(configurationParameter -> { + Long seed = null; + try { + seed = Long.valueOf(configurationParameter); + logger.config( + () -> String.format("Using custom seed for configuration parameter [%s] with value [%s].", + RANDOM_SEED_PROPERTY_NAME, configurationParameter)); + } + catch (NumberFormatException ex) { + logger.warn(ex, + () -> String.format( + "Failed to convert configuration parameter [%s] with value [%s] to a long. " + + "Using default seed [%s] as fallback.", + RANDOM_SEED_PROPERTY_NAME, configurationParameter, DEFAULT_SEED)); + } + return seed; + }); + } + } + +} diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrdererContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrdererContext.java new file mode 100644 index 000000000000..8942a327e206 --- /dev/null +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrdererContext.java @@ -0,0 +1,56 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.api; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.util.List; +import java.util.Optional; + +import org.apiguardian.api.API; + +/** + * {@code ClassOrdererContext} encapsulates the context in which + * a {@link ClassOrderer} will be invoked. + * + * @since 5.8 + * @see ClassOrderer + * @see ClassDescriptor + */ +@API(status = EXPERIMENTAL, since = "5.8") +public interface ClassOrdererContext { + + /** + * Get the list of {@linkplain ClassDescriptor class descriptors} to + * order. + * + * @return the list of class descriptors; never {@code null} + */ + List getClassDescriptors(); + + /** + * Get the configuration parameter stored under the specified {@code key}. + * + *

If no such key is present in the {@code ConfigurationParameters} for + * the JUnit Platform, an attempt will be made to look up the value as a + * JVM system property. If no such system property exists, an attempt will + * be made to look up the value in the JUnit Platform properties file. + * + * @param key the key to look up; never {@code null} or blank + * @return an {@code Optional} containing the value; never {@code null} + * but potentially empty + * + * @see System#getProperty(String) + * @see org.junit.platform.engine.ConfigurationParameters + */ + Optional getConfigurationParameter(String key); + +} diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Disabled.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Disabled.java index 6b784a958494..fc6db60bf4fd 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Disabled.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Disabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayName.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayName.java index 9f6e96937ba7..bb30c8846867 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayName.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayName.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGeneration.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGeneration.java index 237d9b5e1002..f12a81923bd1 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGeneration.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGeneration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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 @@ -25,9 +25,14 @@ * {@code @DisplayNameGeneration} is used to declare a custom display name * generator for the annotated test class. * + *

This annotation is inherited from superclasses and implemented + * interfaces. It is also inherited from {@linkplain Class#getEnclosingClass() + * enclosing classes} for {@link Nested @Nested} test classes. + * * @since 5.4 * @see DisplayName * @see DisplayNameGenerator + * @see IndicativeSentencesGeneration */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGenerator.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGenerator.java index 42bb9821057c..58a5038b162e 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGenerator.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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 @@ -12,10 +12,12 @@ import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; -import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation; +import static org.junit.platform.commons.support.ModifierSupport.isStatic; +import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation; import java.lang.reflect.Method; import java.util.Optional; +import java.util.function.Predicate; import org.apiguardian.api.API; import org.junit.platform.commons.util.ClassUtils; @@ -31,6 +33,14 @@ * *

Concrete implementations must have a default constructor. * + *

Built-in Implementations

+ *
    + *
  • {@link Standard}
  • + *
  • {@link Simple}
  • + *
  • {@link ReplaceUnderscores}
  • + *
  • {@link IndicativeSentences}
  • + *
+ * * @since 5.4 * @see DisplayName @DisplayName * @see DisplayNameGeneration @DisplayNameGeneration @@ -92,6 +102,9 @@ class Standard implements DisplayNameGenerator { static final DisplayNameGenerator INSTANCE = new Standard(); + public Standard() { + } + @Override public String generateDisplayNameForClass(Class testClass) { String name = testClass.getName(); @@ -122,6 +135,9 @@ class Simple extends Standard { static final DisplayNameGenerator INSTANCE = new Simple(); + public Simple() { + } + @Override public String generateDisplayNameForMethod(Class testClass, Method testMethod) { String displayName = testMethod.getName(); @@ -148,6 +164,9 @@ class ReplaceUnderscores extends Simple { static final DisplayNameGenerator INSTANCE = new ReplaceUnderscores(); + public ReplaceUnderscores() { + } + @Override public String generateDisplayNameForClass(Class testClass) { return replaceUnderscores(super.generateDisplayNameForClass(testClass)); @@ -172,11 +191,12 @@ private static String replaceUnderscores(String name) { /** * {@code DisplayNameGenerator} that generates complete sentences. * - *

This implements the functionality of {@link DisplayNameGenerator} - * by generating complete sentences display names, these - * sentences are divided with a separator, and the generator and separator - * can be customisable by using the {@link IndicativeSentencesGeneration} - * interface as annotation. + *

This generator generates display names that build up complete sentences + * by concatenating the names of the test and the enclosing classes. The + * sentence fragments are concatenated using a separator. The separator and + * the display name generator for individual sentence fragments can be configured + * via the {@link IndicativeSentencesGeneration @IndicativeSentencesGeneration} + * annotation. * * @since 5.7 */ @@ -185,9 +205,12 @@ class IndicativeSentences implements DisplayNameGenerator { static final DisplayNameGenerator INSTANCE = new IndicativeSentences(); + public IndicativeSentences() { + } + @Override public String generateDisplayNameForClass(Class testClass) { - return getGeneratorForIndicativeSentence(testClass).generateDisplayNameForClass(testClass); + return getGeneratorFor(testClass).generateDisplayNameForClass(testClass); } @Override @@ -197,94 +220,109 @@ public String generateDisplayNameForNestedClass(Class nestedClass) { @Override public String generateDisplayNameForMethod(Class testClass, Method testMethod) { - return getSentenceBeginning(testClass) + getSentenceSeparator(testClass) - + getGeneratorForIndicativeSentence(testClass).generateDisplayNameForMethod(testClass, testMethod); + return getSentenceBeginning(testClass) + getFragmentSeparator(testClass) + + getGeneratorFor(testClass).generateDisplayNameForMethod(testClass, testMethod); } private String getSentenceBeginning(Class testClass) { - Class enclosingParent = testClass.getEnclosingClass(); - Optional displayName = findAnnotation(testClass, DisplayName.class); - Optional displayNameGeneration = findAnnotation(testClass, - DisplayNameGeneration.class); - - if (enclosingParent == null || displayNameGeneration.isPresent()) { - return displayName.map(DisplayName::value).orElseGet(() -> generateDisplayNameForClass(testClass)); + Class enclosingClass = testClass.getEnclosingClass(); + boolean topLevelTestClass = (enclosingClass == null || isStatic(testClass)); + Optional displayName = findAnnotation(testClass, DisplayName.class)// + .map(DisplayName::value).map(String::trim); + + if (topLevelTestClass) { + if (displayName.isPresent()) { + return displayName.get(); + } + Class generatorClass = findDisplayNameGeneration(testClass)// + .map(DisplayNameGeneration::value)// + .filter(not(IndicativeSentences.class))// + .orElse(null); + if (generatorClass != null) { + return getDisplayNameGenerator(generatorClass).generateDisplayNameForClass(testClass); + } + return generateDisplayNameForClass(testClass); } - String separator = getSentenceSeparator(testClass); - String sentenceBeginning = getSentenceBeginning(enclosingParent); - return displayName.map(name -> sentenceBeginning + separator + name.value()) // - .orElseGet(() -> sentenceBeginning + separator - + getGeneratorForIndicativeSentence(testClass).generateDisplayNameForNestedClass( - testClass)); + + // Only build prefix based on the enclosing class if the enclosing + // class is also configured to use the IndicativeSentences generator. + boolean buildPrefix = findDisplayNameGeneration(enclosingClass)// + .map(DisplayNameGeneration::value)// + .filter(IndicativeSentences.class::equals)// + .isPresent(); + + String prefix = (buildPrefix ? getSentenceBeginning(enclosingClass) + getFragmentSeparator(testClass) : ""); + + return prefix + displayName.orElseGet( + () -> getGeneratorFor(testClass).generateDisplayNameForNestedClass(testClass)); } /** - * Gets the separator for {@link IndicativeSentencesGeneration} when extracting the - * annotation from {@code IndicativeSentencesGeneration}, if it doesn't find it, - * then search for the parent classes, if no separator is found use @code{", "} by default. + * Get the sentence fragment separator. + * + *

If {@link IndicativeSentencesGeneration @IndicativeSentencesGeneration} + * is present (searching enclosing classes if not found locally), the + * configured {@link IndicativeSentencesGeneration#separator() separator} + * will be used. Otherwise, {@link IndicativeSentencesGeneration#DEFAULT_SEPARATOR} + * will be used. * - * @param testClass Class to get Indicative sentence annotation separator either custom or default - * @return the indicative sentence separator + * @param testClass the test class to search on for {@code @IndicativeSentencesGeneration} + * @return the sentence fragment separator */ - private String getSentenceSeparator(Class testClass) { - Optional indicativeSentencesGeneration = getIndicativeSentencesGeneration( - testClass); - if (indicativeSentencesGeneration.isPresent()) { - if (indicativeSentencesGeneration.get().separator().equals("")) { - return IndicativeSentencesGeneration.DEFAULT_SEPARATOR; - } - return indicativeSentencesGeneration.get().separator(); - } - - return IndicativeSentencesGeneration.DEFAULT_SEPARATOR; + private static String getFragmentSeparator(Class testClass) { + return findIndicativeSentencesGeneration(testClass)// + .map(IndicativeSentencesGeneration::separator)// + .orElse(IndicativeSentencesGeneration.DEFAULT_SEPARATOR); } /** - * Gets the generator for {@link IndicativeSentencesGeneration} when extracting the - * annotation from {@code IndicativeSentencesGeneration}, if it doesn't find it, - * then search for the parent classes, if no generator value is found use - * {@link Standard} by default. + * Get the display name generator to use for the supplied test class. + * + *

If {@link IndicativeSentencesGeneration @IndicativeSentencesGeneration} + * is present (searching enclosing classes if not found locally), the + * configured {@link IndicativeSentencesGeneration#generator() generator} + * will be used. Otherwise, {@link IndicativeSentencesGeneration#DEFAULT_GENERATOR} + * will be used. * - * @param testClass Class to get Indicative sentence generator either custom or default - * @return the {@code DisplayNameGenerator} instance to use in indicative sentences generator + * @param testClass the test class to search on for {@code @IndicativeSentencesGeneration} + * @return the {@code DisplayNameGenerator} instance to use */ - private DisplayNameGenerator getGeneratorForIndicativeSentence(Class testClass) { - Optional indicativeSentencesGeneration = getIndicativeSentencesGeneration( - testClass); - if (indicativeSentencesGeneration.isPresent()) { - DisplayNameGenerator displayNameGenerator = getDisplayNameGenerator( - indicativeSentencesGeneration.get().generator()); - if (displayNameGenerator.getClass() == IndicativeSentences.class) { - return getDisplayNameGenerator(IndicativeSentencesGeneration.DEFAULT_GENERATOR); - } - return displayNameGenerator; - } - - return getDisplayNameGenerator(IndicativeSentencesGeneration.DEFAULT_GENERATOR); + private static DisplayNameGenerator getGeneratorFor(Class testClass) { + return findIndicativeSentencesGeneration(testClass)// + .map(IndicativeSentencesGeneration::generator)// + .filter(not(IndicativeSentences.class))// + .map(DisplayNameGenerator::getDisplayNameGenerator)// + .orElseGet(() -> getDisplayNameGenerator(IndicativeSentencesGeneration.DEFAULT_GENERATOR)); } /** - * Finds the {@code IndicativeSentencesGeneration} annotation that is present, - * meta-present or if it doesn't find it, then search for the enclosing - * parent classes, if no annotation is found returns empty. + * Find the first {@code DisplayNameGeneration} annotation that is either + * directly present, meta-present, or indirectly present + * on the supplied {@code testClass} or on an enclosing class. * - * @param testClass the test class to find the {@code IndicativeSentencesGeneration} - * annotation - * @return the optional annotation retrieved from the test class. + * @param testClass the test class on which to find the annotation; never {@code null} + * @return an {@code Optional} containing the annotation, potentially empty if not found */ - private Optional getIndicativeSentencesGeneration(Class testClass) { - Optional indicativeSentencesGeneration = findAnnotation(testClass, - IndicativeSentencesGeneration.class); + private static Optional findDisplayNameGeneration(Class testClass) { + return findAnnotation(testClass, DisplayNameGeneration.class, true); + } - if (indicativeSentencesGeneration.isPresent()) { - return indicativeSentencesGeneration; - } - if (testClass.getEnclosingClass() != null) { - return getIndicativeSentencesGeneration(testClass.getEnclosingClass()); - } + /** + * Find the first {@code IndicativeSentencesGeneration} annotation that is either + * directly present, meta-present, or indirectly present + * on the supplied {@code testClass} or on an enclosing class. + * + * @param testClass the test class on which to find the annotation; never {@code null} + * @return an {@code Optional} containing the annotation, potentially empty if not found + */ + private static Optional findIndicativeSentencesGeneration(Class testClass) { + return findAnnotation(testClass, IndicativeSentencesGeneration.class, true); + } - return Optional.empty(); + private static Predicate> not(Class clazz) { + return ((Predicate>) clazz::equals).negate(); } + } /** diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicContainer.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicContainer.java index 09ba38d6edeb..88c40789b96f 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicContainer.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicNode.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicNode.java index 3dab77739a5b..f7e2b42b3a6d 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicNode.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java index b4b1ef0c350c..de0e03cfe88e 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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 @@ -155,6 +155,77 @@ public static Stream stream(Stream inputStream, .map(input -> dynamicTest(displayNameGenerator.apply(input), () -> testExecutor.accept(input))); } + /** + * Generate a stream of dynamic tests based on the given generator and test + * executor. + * + *

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

The given {@code inputGenerator} is responsible for generating + * input values and display names. A {@link DynamicTest} will be added to + * the resulting stream for each dynamically generated input value, + * using the given {@code testExecutor}. + * + * @param inputGenerator an {@code Iterator} with {@code Named} values + * that serves as a dynamic input generator; never {@code null} + * @param testExecutor a consumer that executes a test based on an input + * value; never {@code null} + * @param the type of input generated by the {@code inputGenerator} + * and used by the {@code testExecutor} + * @return a stream of dynamic tests based on the given generator and + * executor; never {@code null} + * @since 5.8 + * + * @see #dynamicTest(String, Executable) + * @see #stream(Stream, ThrowingConsumer) + * @see Named + */ + @API(status = MAINTAINED, since = "5.8") + public static Stream stream(Iterator> inputGenerator, + ThrowingConsumer testExecutor) { + Preconditions.notNull(inputGenerator, "inputGenerator must not be null"); + + return stream(StreamSupport.stream(spliteratorUnknownSize(inputGenerator, ORDERED), false), testExecutor); + } + + /** + * Generate a stream of dynamic tests based on the given input stream and + * test executor. + * + *

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

The given {@code inputStream} is responsible for supplying input values + * and display names. A {@link DynamicTest} will be added to the resulting stream for + * each dynamically supplied input value, using the given {@code testExecutor}. + * + * @param inputStream a {@code Stream} that supplies dynamic {@code Named} + * input values; never {@code null} + * @param testExecutor a consumer that executes a test based on an input + * value; never {@code null} + * @param the type of input supplied by the {@code inputStream} + * and used by the {@code displayNameGenerator} and {@code testExecutor} + * @return a stream of dynamic tests based on the given generator and + * executor; never {@code null} + * @since 5.8 + * + * @see #dynamicTest(String, Executable) + * @see #stream(Iterator, ThrowingConsumer) + * @see Named + */ + @API(status = MAINTAINED, since = "5.8") + public static Stream stream(Stream> inputStream, + ThrowingConsumer testExecutor) { + Preconditions.notNull(inputStream, "inputStream must not be null"); + Preconditions.notNull(testExecutor, "testExecutor must not be null"); + + return inputStream // + .map(input -> dynamicTest(input.getName(), () -> testExecutor.accept(input.getPayload()))); + } + private final Executable executable; private DynamicTest(String displayName, URI testSourceUri, Executable executable) { diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/IndicativeSentencesGeneration.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/IndicativeSentencesGeneration.java index 3a5372eeb482..a3ded3df9de9 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/IndicativeSentencesGeneration.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/IndicativeSentencesGeneration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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 @@ -20,18 +20,29 @@ import java.lang.annotation.Target; import org.apiguardian.api.API; +import org.junit.jupiter.api.DisplayNameGenerator.IndicativeSentences; /** - * {@code @IndicativeSentencesGeneration} is used to declare a custom parameters - * by {@code IndicativeSentences}, if this notation has some not declared - * parameters, it will use the default values instead. + * {@code @IndicativeSentencesGeneration} is used to register the + * {@link IndicativeSentences} display name generator and configure it. + * + *

The {@link #separator} for sentence fragments and the display name + * {@link #generator} for sentence fragments are configurable. If this annotation + * is declared without any attributes — for example, + * {@code @IndicativeSentencesGeneration} or {@code @IndicativeSentencesGeneration()} + * — the default configuration will be used. + * + *

This annotation is inherited from superclasses and implemented + * interfaces. It is also inherited from {@linkplain Class#getEnclosingClass() + * enclosing classes} for {@link Nested @Nested} test classes. * * @since 5.7 * @see DisplayName * @see DisplayNameGenerator * @see DisplayNameGenerator.IndicativeSentences + * @see DisplayNameGeneration */ -@DisplayNameGeneration(DisplayNameGenerator.IndicativeSentences.class) +@DisplayNameGeneration(IndicativeSentences.class) @Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented @@ -40,15 +51,21 @@ public @interface IndicativeSentencesGeneration { String DEFAULT_SEPARATOR = ", "; + Class DEFAULT_GENERATOR = DisplayNameGenerator.Standard.class; /** - * Custom separator for indicative sentences generator. + * Custom separator for sentence fragments. + * + *

Defaults to {@value #DEFAULT_SEPARATOR}. */ - String separator() default ""; + String separator() default DEFAULT_SEPARATOR; /** - * Custom display name generator. + * Custom display name generator to use for sentence fragments. + * + *

Defaults to {@link DisplayNameGenerator.Standard}. */ Class generator() default DisplayNameGenerator.Standard.class; + } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodDescriptor.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodDescriptor.java index ce0db501e3d6..0c9ab15a8670 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodDescriptor.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrderer.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrderer.java index 9e57b0b28dcd..3443eb20d584 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrderer.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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 @@ -49,6 +49,7 @@ * @see TestMethodOrder * @see MethodOrdererContext * @see #orderMethods(MethodOrdererContext) + * @see ClassOrderer */ @API(status = STABLE, since = "5.7") public interface MethodOrderer { @@ -71,7 +72,7 @@ public interface MethodOrderer { * * * @param context the {@code MethodOrdererContext} containing the - * {@link MethodDescriptor method descriptors} to order; never {@code null} + * {@linkplain MethodDescriptor method descriptors} to order; never {@code null} * @see #getDefaultExecutionMode() */ void orderMethods(MethodOrdererContext context); @@ -112,18 +113,20 @@ default Optional getDefaultExecutionMode() { * *

If two methods have the same name, {@code String} representations of * their formal parameter lists will be used as a fallback for comparing the - * methods.

- *

This class has been deprecated in favor of - * {@link MethodOrderer.MethodName} and will be removed in 6.0

+ * methods. * * @since 5.4 - * @deprecated Please use {@link MethodOrderer.MethodName} instead. + * @deprecated as of JUnit Jupiter 5.7 in favor of {@link MethodOrderer.MethodName}; + * to be removed in 6.0 */ @API(status = DEPRECATED, since = "5.7") @Deprecated class Alphanumeric extends MethodName { + public Alphanumeric() { + } } + /** * {@code MethodOrderer} that sorts methods alphanumerically based on their * names using {@link String#compareTo(String)}. @@ -131,11 +134,15 @@ class Alphanumeric extends MethodName { *

If two methods have the same name, {@code String} representations of * their formal parameter lists will be used as a fallback for comparing the * methods. + * * @since 5.7 */ @API(status = EXPERIMENTAL, since = "5.7") class MethodName implements MethodOrderer { + public MethodName() { + } + /** * Sort the methods encapsulated in the supplied * {@link MethodOrdererContext} alphanumerically based on their names @@ -164,6 +171,9 @@ private static String parameterList(Method method) { @API(status = EXPERIMENTAL, since = "5.7") class DisplayName implements MethodOrderer { + public DisplayName() { + } + /** * Sort the methods encapsulated in the supplied * {@link MethodOrdererContext} alphanumerically based on their display @@ -186,15 +196,17 @@ public void orderMethods(MethodOrdererContext context) { * arbitrarily adjacent to each other. * *

Any methods not annotated with {@code @Order} will be assigned the - * {@link org.junit.jupiter.api.Order#DEFAULT default order} value which will - * effectively cause them to appear at the end of the sorted list, unless - * certain methods are assigned an explicit order value greater than the default - * order value. Any methods assigned an explicit order value greater than the - * default order value will appear after non-annotated methods in the sorted - * list. + * {@link Order#DEFAULT default order} value which will effectively cause them + * to appear at the end of the sorted list, unless certain methods are assigned + * an explicit order value greater than the default order value. Any methods + * assigned an explicit order value greater than the default order value will + * appear after non-annotated methods in the sorted list. */ class OrderAnnotation implements MethodOrderer { + public OrderAnnotation() { + } + /** * Sort the methods encapsulated in the supplied * {@link MethodOrdererContext} based on the {@link Order @Order} @@ -218,7 +230,7 @@ private static int getOrder(MethodDescriptor descriptor) { *

By default, the random seed used for ordering methods is the * value returned by {@link System#nanoTime()} during static initialization * of this class. In order to support repeatable builds, the value of the - * default random seed is logged at {@code INFO} level. In addition, a + * default random seed is logged at {@code CONFIG} level. In addition, a * custom seed (potentially the default seed from the previous test plan * execution) may be specified via the {@link Random#RANDOM_SEED_PROPERTY_NAME * junit.jupiter.execution.order.random.seed} configuration parameter @@ -242,13 +254,16 @@ class Random implements MethodOrderer { static { DEFAULT_SEED = System.nanoTime(); - logger.info(() -> "MethodOrderer.Random default seed: " + DEFAULT_SEED); + logger.config(() -> "MethodOrderer.Random default seed: " + DEFAULT_SEED); } /** * Property name used to set the random seed used by this * {@code MethodOrderer}: {@value} * + *

The same property is used by {@link ClassOrderer.Random} for + * consistency between the two random orderers. + * *

Supported Values

* *

Supported values include any string that can be converted to a @@ -257,9 +272,14 @@ class Random implements MethodOrderer { *

If not specified or if the specified value cannot be converted to * a {@link Long}, the default random seed will be used (see the * {@linkplain Random class-level Javadoc} for details). + * + * @see ClassOrderer.Random */ public static final String RANDOM_SEED_PROPERTY_NAME = "junit.jupiter.execution.order.random.seed"; + public Random() { + } + /** * Order the methods encapsulated in the supplied * {@link MethodOrdererContext} pseudo-randomly. diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrdererContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrdererContext.java index db9290c92408..03f414da97d4 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrdererContext.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrdererContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Named.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Named.java new file mode 100644 index 000000000000..d707529a7e50 --- /dev/null +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Named.java @@ -0,0 +1,94 @@ +/* + * 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 + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.api; + +import static org.apiguardian.api.API.Status.STABLE; + +import org.apiguardian.api.API; +import org.junit.platform.commons.util.Preconditions; + +/** + * {@code Named} is a container that associates a name with a given payload. + * + * @param the type of the payload + * + * @since 5.8 + */ +@API(status = STABLE, since = "5.8") +public interface Named { + + /** + * Factory method for creating an instance of {@code Named} based on a + * {@code name} and a {@code payload}. + * + * @param name the name associated with the payload; never {@code null} or + * blank + * @param payload the object that serves as the payload; may be {@code null} + * depending on the use case + * @param the type of the payload + * @return an instance of {@code Named}; never {@code null} + * @see #named(String, java.lang.Object) + */ + static Named of(String name, T payload) { + Preconditions.notBlank(name, "name must not be null or blank"); + + return new Named() { + @Override + public String getName() { + return name; + } + + @Override + public T getPayload() { + return payload; + } + + @Override + public String toString() { + return name; + } + }; + } + + /** + * Factory method for creating an instance of {@code Named} based on a + * {@code name} and a {@code payload}. + * + *

This method is an alias for {@link Named#of} and is + * intended to be used when statically imported — for example, via: + * {@code import static org.junit.jupiter.api.Named.named;} + * + * @param name the name associated with the payload; never {@code null} or + * blank + * @param payload the object that serves as the payload; may be {@code null} + * depending on the use case + * @param the type of the payload + * @return an instance of {@code Named}; never {@code null} + */ + static Named named(String name, T payload) { + return of(name, payload); + } + + /** + * Get the name of the payload. + * + * @return the name of the payload; never {@code null} or blank + */ + String getName(); + + /** + * Get the payload. + * + * @return the payload; may be {@code null} depending on the use case + */ + T getPayload(); + +} diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Nested.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Nested.java index e7f2f4c0dbb0..28c4edaf7294 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Nested.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/Nested.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-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 @@ -28,6 +28,9 @@ * enclosing class}. The enclosing class may be a top-level test class or * another {@code @Nested} test class, and nesting can be arbitrarily deep. * + *

{@code @Nested} test classes may be ordered via + * {@link TestClassOrder @TestClassOrder} or a global {@link ClassOrderer}. + * *

Test Instance Lifecycle

* *