mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 08:11:25 +00:00
Compare commits
208 Commits
rossendrij
...
gdejong/te
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
561adcd0d6 | ||
|
|
05c5ba2677 | ||
|
|
efb9ea91b7 | ||
|
|
36ed176c4c | ||
|
|
aa21c57185 | ||
|
|
6e7f21f827 | ||
|
|
37580fa5ff | ||
|
|
b26ec206a0 | ||
|
|
354d98dbb7 | ||
|
|
6b99639f0e | ||
|
|
a5d4a3e87f | ||
|
|
c19a30cdb8 | ||
|
|
b61dc4e151 | ||
|
|
27a248def0 | ||
|
|
0e72a531e0 | ||
|
|
8842b11ad3 | ||
|
|
39550227a7 | ||
|
|
867985819a | ||
|
|
0a6c880e9c | ||
|
|
ae4a196fd6 | ||
|
|
b5ef39c89c | ||
|
|
21b6fc7bd1 | ||
|
|
cec781b317 | ||
|
|
76778344f6 | ||
|
|
c663c0cb7a | ||
|
|
0f7231a3ad | ||
|
|
4affdc8519 | ||
|
|
97d87cc9c0 | ||
|
|
4c9ab70bb6 | ||
|
|
03232ad9b9 | ||
|
|
4f48e05d8f | ||
|
|
57eb44285a | ||
|
|
4dd39610e7 | ||
|
|
643fd4e8a5 | ||
|
|
67c57c2442 | ||
|
|
2ec3938737 | ||
|
|
486ee7353f | ||
|
|
7cbe859b8c | ||
|
|
3cfcdda0fc | ||
|
|
952759209d | ||
|
|
9ad5f6e37c | ||
|
|
ebb6ff46b4 | ||
|
|
a305bf6da0 | ||
|
|
1d3cc10ff0 | ||
|
|
8d93549935 | ||
|
|
7096ce6075 | ||
|
|
9652c6f990 | ||
|
|
f02d9adcb2 | ||
|
|
9dda67e6cc | ||
|
|
51fdf18577 | ||
|
|
e3253011c3 | ||
|
|
d1836383ab | ||
|
|
882b256c18 | ||
|
|
36e0765734 | ||
|
|
d46a1ab87e | ||
|
|
3be79074d8 | ||
|
|
d8991627a7 | ||
|
|
1344321746 | ||
|
|
559a55b7d0 | ||
|
|
2e28ac9828 | ||
|
|
4d7b88a986 | ||
|
|
c0e177e4cd | ||
|
|
768b05bee6 | ||
|
|
048203434e | ||
|
|
9b89c0863c | ||
|
|
d03d536376 | ||
|
|
c812c95ea9 | ||
|
|
0d23dd1845 | ||
|
|
e29c08604d | ||
|
|
22c1f07017 | ||
|
|
9dd7621c18 | ||
|
|
3c59795c1b | ||
|
|
2688088750 | ||
|
|
a7b3378bda | ||
|
|
d7a10eda13 | ||
|
|
78e833ff85 | ||
|
|
edf2b7ca79 | ||
|
|
9ab79cb69b | ||
|
|
ac78cc709d | ||
|
|
2ef223ec9d | ||
|
|
2f797a322a | ||
|
|
3537e58305 | ||
|
|
f5e816d967 | ||
|
|
c0acb8b202 | ||
|
|
8f2183b6c2 | ||
|
|
278b679049 | ||
|
|
29657997f3 | ||
|
|
3562e4c764 | ||
|
|
d1b9f93cad | ||
|
|
fc177a9355 | ||
|
|
9ef2692885 | ||
|
|
35ada6b449 | ||
|
|
1a98a4d599 | ||
|
|
e5d7482133 | ||
|
|
69e987363a | ||
|
|
dad73a8447 | ||
|
|
f22b344d87 | ||
|
|
8d78b5b316 | ||
|
|
6024caf99c | ||
|
|
ff60c7be25 | ||
|
|
891e3dde5f | ||
|
|
ee4d3a70fa | ||
|
|
81c0e300a1 | ||
|
|
79abb132f5 | ||
|
|
d94518cbed | ||
|
|
6f862401ab | ||
|
|
aeae1dd66e | ||
|
|
8a032bea18 | ||
|
|
2c3ca2f885 | ||
|
|
d4a22682cd | ||
|
|
4c03041a47 | ||
|
|
c96cb95ec3 | ||
|
|
91923ef168 | ||
|
|
b086db14d9 | ||
|
|
369566f6f1 | ||
|
|
555bf36a7a | ||
|
|
81b27dab08 | ||
|
|
ecdd2c0f61 | ||
|
|
ef9dd72364 | ||
|
|
f2031b8308 | ||
|
|
6995f5627a | ||
|
|
2ad8deb6af | ||
|
|
8f0eea6e44 | ||
|
|
8859417f42 | ||
|
|
cc35110ff5 | ||
|
|
eed6f39b10 | ||
|
|
1e229949fc | ||
|
|
28a93e949e | ||
|
|
40ad38bc2a | ||
|
|
e125c6b7e2 | ||
|
|
5596c4530d | ||
|
|
d81fe19836 | ||
|
|
d6838ec947 | ||
|
|
b36a69aa5f | ||
|
|
831b757bb1 | ||
|
|
527fc5785b | ||
|
|
8c8055d381 | ||
|
|
b658c19c03 | ||
|
|
85976e199f | ||
|
|
f6a392e118 | ||
|
|
539fcae745 | ||
|
|
29f1a3d2a6 | ||
|
|
01f139b6a4 | ||
|
|
c61980721e | ||
|
|
471a1ebff1 | ||
|
|
b8d9ff0971 | ||
|
|
e02d836c12 | ||
|
|
abd47eb269 | ||
|
|
5219fd8f6c | ||
|
|
588fc38f81 | ||
|
|
e3aa8a5d12 | ||
|
|
3255c0b6eb | ||
|
|
d2dbd88f25 | ||
|
|
ff824cfa20 | ||
|
|
43303e770a | ||
|
|
cfadbca32a | ||
|
|
e7ca4a5325 | ||
|
|
7bab1eb7fd | ||
|
|
072e39da32 | ||
|
|
ec7e84ac45 | ||
|
|
4228a63ad1 | ||
|
|
6093e6f322 | ||
|
|
ee103a99f6 | ||
|
|
c34065beab | ||
|
|
d9e1f3ad5d | ||
|
|
8a8290587a | ||
|
|
162aa0d458 | ||
|
|
0fb37c45b5 | ||
|
|
0c2ce44742 | ||
|
|
f089157443 | ||
|
|
e192dacdfb | ||
|
|
8418652de0 | ||
|
|
1d8ac35660 | ||
|
|
913cd2ee3a | ||
|
|
6adaa6c4f6 | ||
|
|
08dbb8c298 | ||
|
|
e7bc0e113c | ||
|
|
6d2c926b0e | ||
|
|
60e15cb569 | ||
|
|
22f61d3032 | ||
|
|
5d2a726aec | ||
|
|
192322a982 | ||
|
|
ddf5d803bd | ||
|
|
02fb6d468a | ||
|
|
e7d50c247d | ||
|
|
85cfb4b4b7 | ||
|
|
0684c577ac | ||
|
|
32778edc74 | ||
|
|
1e6780afc1 | ||
|
|
ef4e004141 | ||
|
|
72c5a42feb | ||
|
|
271e01a02c | ||
|
|
d47549d68f | ||
|
|
01687c7f3e | ||
|
|
85cb7ffdb1 | ||
|
|
0367037f0a | ||
|
|
6669a2e1ec | ||
|
|
eb36c1e493 | ||
|
|
8f5faf0f6a | ||
|
|
5fad0ea04f | ||
|
|
4558f8affb | ||
|
|
7be27614da | ||
|
|
7118d6bf03 | ||
|
|
eb84ddf500 | ||
|
|
032109756d | ||
|
|
9e230302e9 | ||
|
|
4708fec201 | ||
|
|
d102d6acbb |
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -26,13 +26,15 @@ jobs:
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.adoptium.net:443
|
||||
github.com:443
|
||||
jitpack.io:443
|
||||
objects.githubusercontent.com:443
|
||||
repo.maven.apache.org:443
|
||||
# We run the build twice for each supported JDK: once against the
|
||||
# original Error Prone release, using only Error Prone checks available
|
||||
@@ -40,7 +42,7 @@ jobs:
|
||||
# additionally enabling all checks defined in this project and any Error
|
||||
# Prone checks available only from other artifact repositories.
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
java-version: ${{ matrix.jdk }}
|
||||
java-distribution: ${{ matrix.distribution }}
|
||||
|
||||
9
.github/workflows/codeql.yml
vendored
9
.github/workflows/codeql.yml
vendored
@@ -22,30 +22,31 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.adoptium.net:443
|
||||
api.github.com:443
|
||||
github.com:443
|
||||
objects.githubusercontent.com:443
|
||||
repo.maven.apache.org:443
|
||||
uploads.github.com:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.6
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
|
||||
uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Perform minimal build
|
||||
if: matrix.language == 'java'
|
||||
run: mvn -T1C clean package -DskipTests -Dverification.skip
|
||||
- name: Perform CodeQL analysis
|
||||
uses: github/codeql-action/analyze@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
|
||||
uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
|
||||
with:
|
||||
category: /language:${{ matrix.language }}
|
||||
|
||||
8
.github/workflows/deploy-website.yml
vendored
8
.github/workflows/deploy-website.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
@@ -39,10 +39,10 @@ jobs:
|
||||
www.youtube.com:443
|
||||
youtrack.jetbrains.com:443
|
||||
- name: Check out code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: ruby/setup-ruby@5f19ec79cedfadb78ab837f95b87734d0003c899 # v1.173.0
|
||||
- uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0
|
||||
with:
|
||||
working-directory: ./website
|
||||
bundler-cache: true
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
|
||||
9
.github/workflows/openssf-scorecard.yml
vendored
9
.github/workflows/openssf-scorecard.yml
vendored
@@ -21,13 +21,14 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.github.com:443
|
||||
api.osv.dev:443
|
||||
api.scorecard.dev:443
|
||||
api.securityscorecards.dev:443
|
||||
fulcio.sigstore.dev:443
|
||||
github.com:443
|
||||
@@ -36,16 +37,16 @@ jobs:
|
||||
tuf-repo-cdn.sigstore.dev:443
|
||||
www.bestpractices.dev:443
|
||||
- name: Check out code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run OpenSSF Scorecard analysis
|
||||
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
|
||||
uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
publish_results: ${{ github.ref == 'refs/heads/master' }}
|
||||
- name: Update GitHub's code scanning dashboard
|
||||
uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
|
||||
uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
8
.github/workflows/pitest-analyze-pr.yml
vendored
8
.github/workflows/pitest-analyze-pr.yml
vendored
@@ -12,15 +12,17 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.adoptium.net:443
|
||||
github.com:443
|
||||
objects.githubusercontent.com:443
|
||||
repo.maven.apache.org:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
checkout-fetch-depth: 2
|
||||
java-version: 17.0.10
|
||||
@@ -36,7 +38,7 @@ jobs:
|
||||
- name: Aggregate Pitest reports
|
||||
run: mvn pitest-git:aggregate -DkilledEmoji=":tada:" -DmutantEmoji=":zombie:" -DtrailingText="Mutation testing report by [Pitest](https://pitest.org/). Review any surviving mutants by inspecting the line comments under [_Files changed_](${{ github.event.number }}/files)."
|
||||
- name: Upload Pitest reports as artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
with:
|
||||
name: pitest-reports
|
||||
path: ./target/pit-reports-ci
|
||||
|
||||
6
.github/workflows/pitest-update-pr.yml
vendored
6
.github/workflows/pitest-update-pr.yml
vendored
@@ -20,16 +20,18 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.adoptium.net:443
|
||||
api.github.com:443
|
||||
github.com:443
|
||||
objects.githubusercontent.com:443
|
||||
repo.maven.apache.org:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
|
||||
8
.github/workflows/run-integration-tests.yml
vendored
8
.github/workflows/run-integration-tests.yml
vendored
@@ -19,19 +19,21 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.adoptium.net:443
|
||||
checkstyle.org:443
|
||||
github.com:443
|
||||
objects.githubusercontent.com:443
|
||||
oss.sonatype.org:443
|
||||
raw.githubusercontent.com:443
|
||||
repo.maven.apache.org:443
|
||||
repository.sonatype.org:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
checkout-ref: "refs/pull/${{ github.event.issue.number }}/head"
|
||||
java-version: 17.0.10
|
||||
@@ -43,7 +45,7 @@ jobs:
|
||||
run: xvfb-run ./integration-tests/checkstyle.sh "${{ runner.temp }}/artifacts"
|
||||
- name: Upload artifacts on failure
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
with:
|
||||
name: integration-test-checkstyle
|
||||
path: "${{ runner.temp }}/artifacts"
|
||||
|
||||
6
.github/workflows/sonarcloud.yml
vendored
6
.github/workflows/sonarcloud.yml
vendored
@@ -19,19 +19,21 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.adoptium.net:443
|
||||
ea6ne4j2sb.execute-api.eu-central-1.amazonaws.com:443
|
||||
github.com:443
|
||||
objects.githubusercontent.com:443
|
||||
repo.maven.apache.org:443
|
||||
sc-cleancode-sensorcache-eu-central-1-prod.s3.amazonaws.com:443
|
||||
scanner.sonarcloud.io:443
|
||||
sonarcloud.io:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
checkout-fetch-depth: 0
|
||||
java-version: 17.0.10
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"separateMinorPatch": true
|
||||
},
|
||||
{
|
||||
"matchDepNames": [
|
||||
"matchPackageNames": [
|
||||
"dawidd6/action-download-artifact",
|
||||
"github/codeql-action",
|
||||
"ruby/setup-ruby"
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
|
||||
@@ -232,7 +232,7 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
|
||||
@Override
|
||||
public @Nullable Void visitReturn(ReturnTree node, @Nullable Void unused) {
|
||||
returnExpressions.add(node.getExpression());
|
||||
return super.visitReturn(node, unused);
|
||||
return super.visitReturn(node, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -192,7 +192,7 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
@Override
|
||||
public @Nullable Void visitIdentifier(IdentifierTree node, @Nullable Void unused) {
|
||||
nodes.add(ImmutableList.of(node.getName().toString()));
|
||||
return super.visitIdentifier(node, unused);
|
||||
return super.visitIdentifier(node, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -203,13 +203,13 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
? STRING_ARGUMENT_SPLITTER.splitToStream(str).collect(toImmutableList())
|
||||
: ImmutableList.of(String.valueOf(value)));
|
||||
|
||||
return super.visitLiteral(node, unused);
|
||||
return super.visitLiteral(node, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Void visitPrimitiveType(PrimitiveTypeTree node, @Nullable Void unused) {
|
||||
nodes.add(ImmutableList.of(node.getPrimitiveTypeKind().toString()));
|
||||
return super.visitPrimitiveType(node, unused);
|
||||
return super.visitPrimitiveType(node, null);
|
||||
}
|
||||
}.scan(array, null);
|
||||
|
||||
|
||||
@@ -210,7 +210,7 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
|
||||
}
|
||||
}
|
||||
|
||||
return super.visitIdentifier(node, unused);
|
||||
return super.visitIdentifier(node, null);
|
||||
}
|
||||
}.scan(tree, null);
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ final class ComparatorRules {
|
||||
static final class MinOfPairCustomOrder<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S1067" /* The conditional operators are independent. */)
|
||||
T before(T value1, T value2, Comparator<T> cmp) {
|
||||
T before(T value1, T value2, Comparator<? super T> cmp) {
|
||||
return Refaster.anyOf(
|
||||
cmp.compare(value1, value2) <= 0 ? value1 : value2,
|
||||
cmp.compare(value1, value2) > 0 ? value2 : value1,
|
||||
@@ -305,7 +305,7 @@ final class ComparatorRules {
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(T value1, T value2, Comparator<T> cmp) {
|
||||
T after(T value1, T value2, Comparator<? super T> cmp) {
|
||||
return Comparators.min(value1, value2, cmp);
|
||||
}
|
||||
}
|
||||
@@ -357,7 +357,7 @@ final class ComparatorRules {
|
||||
static final class MaxOfPairCustomOrder<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S1067" /* The conditional operators are independent. */)
|
||||
T before(T value1, T value2, Comparator<T> cmp) {
|
||||
T before(T value1, T value2, Comparator<? super T> cmp) {
|
||||
return Refaster.anyOf(
|
||||
cmp.compare(value1, value2) >= 0 ? value1 : value2,
|
||||
cmp.compare(value1, value2) < 0 ? value2 : value1,
|
||||
@@ -372,7 +372,7 @@ final class ComparatorRules {
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(T value1, T value2, Comparator<T> cmp) {
|
||||
T after(T value1, T value2, Comparator<? super T> cmp) {
|
||||
return Comparators.max(value1, value2, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,19 @@ import tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation;
|
||||
final class OptionalRules {
|
||||
private OptionalRules() {}
|
||||
|
||||
/** Prefer {@link Optional#empty()} over the more contrived alternative. */
|
||||
static final class OptionalEmpty<T> {
|
||||
@BeforeTemplate
|
||||
Optional<T> before() {
|
||||
return Optional.ofNullable(null);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Optional<T> after() {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
static final class OptionalOfNullable<T> {
|
||||
// XXX: Refaster should be smart enough to also rewrite occurrences in which there are
|
||||
// parentheses around the null check, but that's currently not the case. Try to fix that.
|
||||
|
||||
@@ -3,7 +3,6 @@ package tech.picnic.errorprone.refasterrules;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.MoreCollectors.toOptional;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
@@ -52,7 +51,6 @@ import reactor.util.context.Context;
|
||||
import reactor.util.function.Tuple2;
|
||||
import tech.picnic.errorprone.refaster.annotation.Description;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
import tech.picnic.errorprone.refaster.annotation.Severity;
|
||||
import tech.picnic.errorprone.refaster.matchers.IsEmpty;
|
||||
import tech.picnic.errorprone.refaster.matchers.IsIdentityOperation;
|
||||
import tech.picnic.errorprone.refaster.matchers.ThrowsCheckedException;
|
||||
@@ -380,30 +378,23 @@ final class ReactorRules {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Flux#take(long, boolean)} over {@link Flux#take(long)}.
|
||||
* Prefer {@link Flux#take(long)} over {@link Flux#take(long, boolean)} where relevant.
|
||||
*
|
||||
* <p>In Reactor versions prior to 3.5.0, {@code Flux#take(long)} makes an unbounded request
|
||||
* upstream, and is equivalent to {@code Flux#take(long, false)}. In 3.5.0, the behavior of {@code
|
||||
* Flux#take(long)} will change to that of {@code Flux#take(long, true)}.
|
||||
*
|
||||
* <p>The intent with this Refaster rule is to get the new behavior before upgrading to Reactor
|
||||
* 3.5.0.
|
||||
* upstream, and is equivalent to {@code Flux#take(long, false)}. From version 3.5.0 onwards, the
|
||||
* behavior of {@code Flux#take(long)} instead matches {@code Flux#take(long, true)}.
|
||||
*/
|
||||
// XXX: Drop this rule some time after upgrading to Reactor 3.6.0, or introduce a way to apply
|
||||
// this rule only when an older version of Reactor is on the classpath.
|
||||
// XXX: Once Reactor 3.6.0 is out, introduce a rule that rewrites code in the opposite direction.
|
||||
@Description(
|
||||
"Prior to Reactor 3.5.0, `take(n)` requests and unbounded number of elements upstream.")
|
||||
@Severity(WARNING)
|
||||
"From Reactor 3.5.0 onwards, `take(n)` no longer requests an unbounded number of elements upstream.")
|
||||
static final class FluxTake<T> {
|
||||
@BeforeTemplate
|
||||
Flux<T> before(Flux<T> flux, long n) {
|
||||
return flux.take(n);
|
||||
return flux.take(n, /* limitRequest= */ true);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Flux<T> after(Flux<T> flux, long n) {
|
||||
return flux.take(n, /* limitRequest= */ true);
|
||||
return flux.take(n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,6 +557,7 @@ final class ReactorRules {
|
||||
@Matches(IsIdentityOperation.class)
|
||||
Function<? super P, ? extends Publisher<? extends S>> identityOperation) {
|
||||
return Refaster.anyOf(
|
||||
flux.concatMap(function, 0),
|
||||
flux.flatMap(function, 1),
|
||||
flux.flatMapSequential(function, 1),
|
||||
flux.map(function).concatMap(identityOperation));
|
||||
|
||||
@@ -13,6 +13,10 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(Streams.class);
|
||||
}
|
||||
|
||||
Optional<String> testOptionalEmpty() {
|
||||
return Optional.ofNullable(null);
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testOptionalOfNullable() {
|
||||
return ImmutableSet.of(
|
||||
toString() == null ? Optional.empty() : Optional.of(toString()),
|
||||
|
||||
@@ -15,6 +15,10 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(Streams.class);
|
||||
}
|
||||
|
||||
Optional<String> testOptionalEmpty() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testOptionalOfNullable() {
|
||||
return ImmutableSet.of(Optional.ofNullable(toString()), Optional.ofNullable(toString()));
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
}
|
||||
|
||||
Flux<Integer> testFluxTake() {
|
||||
return Flux.just(1, 2, 3).take(1);
|
||||
return Flux.just(1, 2, 3).take(1, true);
|
||||
}
|
||||
|
||||
Mono<String> testMonoDefaultIfEmpty() {
|
||||
@@ -207,11 +207,12 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
|
||||
ImmutableSet<Flux<Integer>> testFluxConcatMap() {
|
||||
return ImmutableSet.of(
|
||||
Flux.just(1).flatMap(Mono::just, 1),
|
||||
Flux.just(2).flatMapSequential(Mono::just, 1),
|
||||
Flux.just(3).map(Mono::just).concatMap(identity()),
|
||||
Flux.just(4).map(Mono::just).concatMap(v -> v),
|
||||
Flux.just(5).map(Mono::just).concatMap(v -> Mono.empty()));
|
||||
Flux.just(1).concatMap(Mono::just, 0),
|
||||
Flux.just(2).flatMap(Mono::just, 1),
|
||||
Flux.just(3).flatMapSequential(Mono::just, 1),
|
||||
Flux.just(4).map(Mono::just).concatMap(identity()),
|
||||
Flux.just(5).map(Mono::just).concatMap(v -> v),
|
||||
Flux.just(6).map(Mono::just).concatMap(v -> Mono.empty()));
|
||||
}
|
||||
|
||||
ImmutableSet<Flux<Integer>> testFluxConcatMapWithPrefetch() {
|
||||
|
||||
@@ -147,7 +147,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
}
|
||||
|
||||
Flux<Integer> testFluxTake() {
|
||||
return Flux.just(1, 2, 3).take(1, true);
|
||||
return Flux.just(1, 2, 3).take(1);
|
||||
}
|
||||
|
||||
Mono<String> testMonoDefaultIfEmpty() {
|
||||
@@ -214,7 +214,8 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Flux.just(2).concatMap(Mono::just),
|
||||
Flux.just(3).concatMap(Mono::just),
|
||||
Flux.just(4).concatMap(Mono::just),
|
||||
Flux.just(5).map(Mono::just).concatMap(v -> Mono.empty()));
|
||||
Flux.just(5).concatMap(Mono::just),
|
||||
Flux.just(6).map(Mono::just).concatMap(v -> Mono.empty()));
|
||||
}
|
||||
|
||||
ImmutableSet<Flux<Integer>> testFluxConcatMapWithPrefetch() {
|
||||
|
||||
7
jitpack.yml
Normal file
7
jitpack.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
before_install:
|
||||
- source "${HOME}/.sdkman/bin/sdkman-init.sh"
|
||||
- sdk update
|
||||
- sdk install java 17.0.10-tem
|
||||
- sdk use java 17.0.10-tem
|
||||
- sdk install maven 3.9.8
|
||||
- sdk use maven 3.9.8
|
||||
83
pom.xml
83
pom.xml
@@ -48,6 +48,7 @@
|
||||
<module>refaster-runner</module>
|
||||
<module>refaster-support</module>
|
||||
<module>refaster-test-support</module>
|
||||
<module>testng-junit-migrator</module>
|
||||
</modules>
|
||||
|
||||
<scm child.scm.developerConnection.inherit.append.path="false" child.scm.url.inherit.append.path="false">
|
||||
@@ -205,19 +206,19 @@
|
||||
one place. We use these to keep dependencies in sync. Version numbers
|
||||
that need to be referenced only once should *not* be listed here. -->
|
||||
<version.auto-service>1.1.1</version.auto-service>
|
||||
<version.auto-value>1.10.4</version.auto-value>
|
||||
<version.auto-value>1.11.0</version.auto-value>
|
||||
<version.error-prone>${version.error-prone-orig}</version.error-prone>
|
||||
<version.error-prone-fork>v${version.error-prone-orig}-picnic-2</version.error-prone-fork>
|
||||
<version.error-prone-orig>2.26.1</version.error-prone-orig>
|
||||
<version.error-prone-slf4j>0.1.23</version.error-prone-slf4j>
|
||||
<version.error-prone-fork>v${version.error-prone-orig}-picnic-1</version.error-prone-fork>
|
||||
<version.error-prone-orig>2.27.1</version.error-prone-orig>
|
||||
<version.error-prone-slf4j>0.1.25</version.error-prone-slf4j>
|
||||
<version.guava-beta-checker>1.0</version.guava-beta-checker>
|
||||
<version.jdk>17</version.jdk>
|
||||
<version.maven>3.9.5</version.maven>
|
||||
<version.mockito>5.11.0</version.mockito>
|
||||
<version.mockito>5.12.0</version.mockito>
|
||||
<version.nopen-checker>1.0.1</version.nopen-checker>
|
||||
<version.nullaway>0.10.25</version.nullaway>
|
||||
<version.nullaway>0.11.0</version.nullaway>
|
||||
<version.pitest-git>1.1.4</version.pitest-git>
|
||||
<version.rewrite-templating>1.6.3</version.rewrite-templating>
|
||||
<version.rewrite-templating>1.10.0</version.rewrite-templating>
|
||||
<version.surefire>3.2.3</version.surefire>
|
||||
</properties>
|
||||
|
||||
@@ -296,7 +297,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson</groupId>
|
||||
<artifactId>jackson-bom</artifactId>
|
||||
<version>2.17.0</version>
|
||||
<version>2.17.1</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -338,7 +339,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava-bom</artifactId>
|
||||
<version>33.1.0-jre</version>
|
||||
<version>33.2.1-jre</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -360,7 +361,7 @@
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-bom</artifactId>
|
||||
<version>2023.0.4</version>
|
||||
<version>2023.0.6</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -377,12 +378,12 @@
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.2.21</version>
|
||||
<version>2.2.22</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<version>6.0.0</version>
|
||||
<version>6.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
@@ -407,7 +408,7 @@
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
<version>1.14.13</version>
|
||||
<version>1.14.17</version>
|
||||
</dependency>
|
||||
<!-- Specified so that Renovate will file Maven upgrade PRs, which
|
||||
subsequently will cause `maven-enforcer-plugin` to require that
|
||||
@@ -420,7 +421,7 @@
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<version>1.9.22</version>
|
||||
<version>1.9.22.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
@@ -432,7 +433,7 @@
|
||||
<dependency>
|
||||
<groupId>org.checkerframework</groupId>
|
||||
<artifactId>checker-qual</artifactId>
|
||||
<version>3.42.0</version>
|
||||
<version>3.44.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
@@ -466,7 +467,7 @@
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongodb-driver-core</artifactId>
|
||||
<version>5.0.1</version>
|
||||
<version>5.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
@@ -476,40 +477,40 @@
|
||||
<dependency>
|
||||
<groupId>org.openrewrite.recipe</groupId>
|
||||
<artifactId>rewrite-recipe-bom</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<version>2.12.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-bom</artifactId>
|
||||
<version>2.0.12</version>
|
||||
<version>2.0.13</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>6.1.5</version>
|
||||
<version>6.1.8</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<version>3.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-bom</artifactId>
|
||||
<version>6.2.3</version>
|
||||
<version>6.3.1</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<version>7.10.0</version>
|
||||
<version>7.10.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
@@ -520,7 +521,7 @@
|
||||
<plugin>
|
||||
<groupId>com.github.ekryd.sortpom</groupId>
|
||||
<artifactId>sortpom-maven-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>4.0.0</version>
|
||||
<configuration>
|
||||
<createBackupFile>false</createBackupFile>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
@@ -624,7 +625,7 @@
|
||||
<plugin>
|
||||
<groupId>io.github.git-commit-id</groupId>
|
||||
<artifactId>git-commit-id-maven-plugin</artifactId>
|
||||
<version>8.0.2</version>
|
||||
<version>9.0.0</version>
|
||||
<configuration>
|
||||
<injectAllReactorProjects>true</injectAllReactorProjects>
|
||||
<runOnlyOnce>true</runOnlyOnce>
|
||||
@@ -643,7 +644,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<version>3.4.0</version>
|
||||
<configuration>
|
||||
<checkstyleRules>
|
||||
<!-- We only enable rules that are not enforced by
|
||||
@@ -679,6 +680,7 @@
|
||||
<property name="allowNonPrintableEscapes" value="true" />
|
||||
</module>
|
||||
<module name="AvoidNoArgumentSuperConstructorCall" />
|
||||
<module name="ConstructorsDeclarationGrouping" />
|
||||
<module name="DeclarationOrder">
|
||||
<!-- We don't enforce sorting fields by
|
||||
their visibility modifier, for two
|
||||
@@ -891,7 +893,7 @@
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.15.0</version>
|
||||
<version>10.17.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.spring.nohttp</groupId>
|
||||
@@ -963,7 +965,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.6.1</version>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<!-- XXX: Drop `ignoreAllNonTestScoped` once
|
||||
https://issues.apache.org/jira/browse/MNG-6058 is
|
||||
@@ -978,7 +980,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.2</version>
|
||||
<configuration>
|
||||
<retryFailedDeploymentCount>3</retryFailedDeploymentCount>
|
||||
</configuration>
|
||||
@@ -986,7 +988,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
<configuration>
|
||||
<fail>false</fail>
|
||||
<rules>
|
||||
@@ -1041,6 +1043,9 @@
|
||||
<requireJavaVersion>
|
||||
<version>${version.jdk}</version>
|
||||
</requireJavaVersion>
|
||||
<requireMatchingCoordinates>
|
||||
<moduleNameMustMatchArtifactId>true</moduleNameMustMatchArtifactId>
|
||||
</requireMatchingCoordinates>
|
||||
<requireMavenVersion>
|
||||
<version>${version.maven}</version>
|
||||
</requireMavenVersion>
|
||||
@@ -1068,7 +1073,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
@@ -1081,12 +1086,12 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.4.1</version>
|
||||
<configuration>
|
||||
<skipIfEmpty>true</skipIfEmpty>
|
||||
<archive>
|
||||
@@ -1114,7 +1119,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<additionalJOptions>
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</additionalJOption>
|
||||
@@ -1143,7 +1148,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<autoVersionSubmodules>true</autoVersionSubmodules>
|
||||
<preparationProfiles>release</preparationProfiles>
|
||||
@@ -1200,7 +1205,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.6.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
@@ -1330,7 +1335,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>tidy-maven-plugin</artifactId>
|
||||
<version>1.2.0</version>
|
||||
<version>1.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>check-pom</id>
|
||||
@@ -1351,7 +1356,7 @@
|
||||
<plugin>
|
||||
<groupId>org.gaul</groupId>
|
||||
<artifactId>modernizer-maven-plugin</artifactId>
|
||||
<version>2.8.0</version>
|
||||
<version>2.9.0</version>
|
||||
<configuration>
|
||||
<exclusionPatterns>
|
||||
<!-- The plugin suggests replacing usages of
|
||||
@@ -1412,7 +1417,7 @@
|
||||
<plugin>
|
||||
<groupId>org.pitest</groupId>
|
||||
<artifactId>pitest-maven</artifactId>
|
||||
<version>1.15.8</version>
|
||||
<version>1.16.1</version>
|
||||
<configuration>
|
||||
<excludedClasses>
|
||||
<!-- AutoValue generated classes. -->
|
||||
@@ -1466,7 +1471,7 @@
|
||||
<plugin>
|
||||
<groupId>org.sonarsource.scanner.maven</groupId>
|
||||
<artifactId>sonar-maven-plugin</artifactId>
|
||||
<version>3.11.0.3922</version>
|
||||
<version>4.0.0.4121</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
@@ -112,7 +112,7 @@ final class RefasterRuleCompilerTaskListener implements TaskListener {
|
||||
return (sym != null
|
||||
&& sym.getQualifiedName()
|
||||
.contentEquals(BeforeTemplate.class.getCanonicalName()))
|
||||
|| super.visitAnnotation(node, unused);
|
||||
|| super.visitAnnotation(node, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
147
testng-junit-migrator/README.md
Normal file
147
testng-junit-migrator/README.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# TestNG to JUnit Jupiter migrator
|
||||
|
||||
This module contains a tool to automatically migrate TestNG tests to JUnit
|
||||
Jupiter. The tool is built on top of [Error Prone][error-prone-orig-repo]. To
|
||||
use it, read the installation guide below.
|
||||
|
||||
### Installation
|
||||
|
||||
1. First, follow Error Prone's [installation
|
||||
guide][error-prone-installation-guide]. For extra information, see this
|
||||
[README][eps-readme]. (This step can be skipped for Picnic repositories!)
|
||||
2. Clone the Error Prone Support repository and checkout the branch
|
||||
`gdejong/testng-migrator`.
|
||||
3. Next, run `mvn versions:set -DnewVersion=0.17.1-testng-migration -DgenerateBackupPoms=false`.
|
||||
This will update set the version to `0.17.1-testng-migration`.
|
||||
4. Next, run `mvn clean install`. This will create a `0.17.1-testng-migrator` version
|
||||
of the `testng-junit-migrator` module. The version will now be available in your local Maven repository.
|
||||
5. Finally, add the following profile to your `pom.xml`. This should be the `pom.xml` in the root of your module.
|
||||
Usually this is the parent `pom.xml`, but single module projects are also supported.
|
||||
|
||||
```xml
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>testng-migrator</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths combine.children="append">
|
||||
<path>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>testng-junit-migrator</artifactId>
|
||||
<version>0.10.1-testng-migration</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
```
|
||||
|
||||
Having this profile allows the migration script to verify the correctness of
|
||||
the result by making sure the same amount of tests are executed.
|
||||
|
||||
## Run the migration
|
||||
|
||||
> **Note**
|
||||
> For Picnic repositories there is an extra step required _before_ running the
|
||||
> migration, see [here](#picnic-specific).
|
||||
|
||||
Now that the migration is set up, one can start the migration by executing the
|
||||
[run-testng-junit-migrator.sh][migration-script] script in the same directory as the `pom.xml` file we changed earlier.
|
||||
|
||||
This script will:
|
||||
|
||||
1. Add the required `JUnit` dependencies to your `pom.xml`.
|
||||
2. Run the `testng-to-junit` migration.
|
||||
|
||||
> **Note**
|
||||
> Please verify that the migrated code still compiles after each step of the compilation.
|
||||
|
||||
### Counting tests
|
||||
|
||||
The amount of tests executed before the migration can be counted using the `--count` flag:
|
||||
```sh
|
||||
./run-testng-junit-migrator.sh --count
|
||||
```
|
||||
This will count the amount of tests that are executed. This is recommended before running the migration
|
||||
to allow for comparison.
|
||||
|
||||
### Picnic specific
|
||||
|
||||
The `PicnicSupermarket/picnic-scratch` repository contains a helper script
|
||||
`java-platform/testng-junit-migration.sh` that migrates some more
|
||||
Picnic-specific code. This should be executed _before_ starting the actual
|
||||
migration.
|
||||
|
||||
> **Warning**
|
||||
> This is a warning for `macOs` users.
|
||||
> Make sure gnu-grep and gnu-sed are installed!
|
||||
>
|
||||
> ```brew install grep gnu-sed```
|
||||
|
||||
Continue with performing the actual migration [here](#run-the-migration).
|
||||
Afterward, run the `./picnic-shared-tools/patch.sh` script.
|
||||
|
||||
Now you are done! 🤘🚀
|
||||
|
||||
### Migration code example
|
||||
|
||||
Consider the following TestNG test class:
|
||||
|
||||
```java
|
||||
// TestNG code:
|
||||
@Test
|
||||
public class A {
|
||||
public void simpleTest() {}
|
||||
|
||||
@Test(priority = 2)
|
||||
public void priorityTest() {}
|
||||
|
||||
@DataProvider
|
||||
private static Object[][] dataProviderTestCases() {
|
||||
return new Object[]{{1}, {2}, {3}};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "dataProviderTestCases")
|
||||
public void dataProviderTest(int number) {}
|
||||
}
|
||||
```
|
||||
|
||||
This migration tool will turn this into the following:
|
||||
|
||||
```java
|
||||
// JUnit Jupiter code:
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class A {
|
||||
@Test
|
||||
void simpleTest() {}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void priorityTest() {}
|
||||
|
||||
private static Stream<Argument> dataProviderTestCases() {
|
||||
return Stream.of(arguments(1), arguments(2), arguments(3));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("dataProviderTestCases")
|
||||
public void dataProviderTest(int number) {}
|
||||
}
|
||||
```
|
||||
|
||||
### Known limitations
|
||||
- Certain `@DataProvider` methods cannot be automatically migrated (e.g., `return Stream.of(...).toArray(Object[][]::new)`).
|
||||
- Some uncommon `@Test` attributes are not supported, such as `ignoreMissingDependencies` and `dependsOnMethods`.
|
||||
- Test setup and teardown methods `@{Before, After}Test` are migrated to `@{Before, After}Each` to avoid introducing breaking changes. `@{Before, After}All` require a static method, while `@{Before, After}Test` are instance methods.
|
||||
|
||||
[eps-readme]: ../README.md
|
||||
[error-prone-installation-guide]: https://errorprone.info/docs/installation#maven
|
||||
[error-prone-orig-repo]: https://github.com/google/error-prone
|
||||
[migration-script]: run-testng-junit-migration.sh
|
||||
156
testng-junit-migrator/pom.xml
Normal file
156
testng-junit-migrator/pom.xml
Normal file
@@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.16.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>testng-junit-migrator</artifactId>
|
||||
|
||||
<name>Picnic :: Error Prone Support :: TestNG JUnit Migrator</name>
|
||||
<description>A tool to migrate TestNG tests to JUnit</description>
|
||||
<url>https://error-prone.picnic.tech</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_annotation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_check_api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_test_helpers</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-compiler</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-support</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto</groupId>
|
||||
<artifactId>auto-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
<artifactId>auto-service-annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.value</groupId>
|
||||
<artifactId>auto-value-annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jspecify</groupId>
|
||||
<artifactId>jspecify</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-java</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-java-11</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-templating</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths combine.children="append">
|
||||
<path>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-compiler</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-support</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs combine.children="append">
|
||||
<arg>-Xplugin:RefasterRuleCompiler</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
142
testng-junit-migrator/run-testng-junit-migration.sh
Executable file
142
testng-junit-migrator/run-testng-junit-migration.sh
Executable file
@@ -0,0 +1,142 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -u -o pipefail
|
||||
|
||||
# If this is not a Maven build, exit here to skip Maven-specific steps.
|
||||
if [ ! -f pom.xml ]; then
|
||||
echo "Not a Maven build, exiting."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
function insert_dependency() {
|
||||
groupId=${1:?groupId not specified or empty}
|
||||
artifactId=${2:?artifactId not specified or empty}
|
||||
classifier=${3?classifier not specified}
|
||||
scope=${4?scope not specified}
|
||||
pomFile=${5:-pom.xml}
|
||||
|
||||
# If the dependency declaration is already present (irrespective of scope),
|
||||
# then we don't modify the file.
|
||||
xmlstarlet sel -T -N 'x=http://maven.apache.org/POM/4.0.0' \
|
||||
-t -m "/x:project/x:dependencies/x:dependency[
|
||||
x:groupId/text() = '${groupId}' and
|
||||
x:artifactId/text() = '${artifactId}' and
|
||||
(x:classifier/text() = '${classifier}' or '${classifier}' = '')
|
||||
]" -nl "${pomFile}" && return 0
|
||||
|
||||
# Determine the index at which to insert the dependency declaration.
|
||||
insertionIndex="$(
|
||||
(xmlstarlet sel -T -N 'x=http://maven.apache.org/POM/4.0.0' \
|
||||
-t -m '/x:project/x:dependencies/x:dependency' \
|
||||
-v 'concat(x:groupId, " : ", x:artifactId, " : ", x:classifier, " : ", x:scope)' -nl \
|
||||
"${pomFile}" || true) |
|
||||
awk "\$0 < \"${groupId} : ${artifactId} : ${classifier} : ${scope}\"" |
|
||||
wc -l
|
||||
)"
|
||||
|
||||
# Generate a placeholder that will be inserted at the place where the new
|
||||
# dependency declaration should reside. We need to jump through this hoop
|
||||
# because `xmlstarlet` does not support insertion of complex XML
|
||||
# sub-documents.
|
||||
placeholder="$(head -c 30 /dev/urandom | base64 | $sed_command 's,[^a-zA-Z0-9],,g')"
|
||||
|
||||
# Insert the placeholder. (Note that only one case will match.)
|
||||
xmlstarlet ed -L -P -N 'x=http://maven.apache.org/POM/4.0.0' \
|
||||
-s "/x:project[not(x:dependencies)]" \
|
||||
-t elem -n dependencies -v "${placeholder}" \
|
||||
-i "/x:project/x:dependencies/x:dependency[${insertionIndex} = 0 and position() = 1]" \
|
||||
-t text -n placeholder -v "${placeholder}" \
|
||||
-a "/x:project/x:dependencies/x:dependency[${insertionIndex}]" \
|
||||
-t text -n placeholder -v "${placeholder}" \
|
||||
"${pomFile}"
|
||||
|
||||
# Generate the XML subdocument we _actually_ want to insert.
|
||||
decl="$(echo "
|
||||
<dependency>
|
||||
<groupId>${groupId}</groupId>
|
||||
<artifactId>${artifactId}</artifactId>
|
||||
<classifier>${classifier}</classifier>
|
||||
<scope>${scope}</scope>
|
||||
</dependency>" |
|
||||
$sed_command '/></d' |
|
||||
$sed_command ':a;N;$!ba;s/\n/\\n/g')"
|
||||
|
||||
# Replace the placeholder with the actual dependency declaration.
|
||||
$sed_command -i "s,${placeholder},${decl}," "${pomFile}"
|
||||
}
|
||||
|
||||
if [[ -n "${1-}" ]] && [[ "${1}" == "--count" ]]; then
|
||||
echo "Counting number of tests..."
|
||||
test_results=$(mvn test | $grep_command -n "Results:" -A 3 | $grep_command -oP "Tests run: \K\d+(?=,)" | awk '{s+=$1} END {print s}')
|
||||
echo "Number of tests run: $test_results"
|
||||
exit
|
||||
fi
|
||||
|
||||
function handle_file() {
|
||||
module=${1:?module not specified or empty}
|
||||
groupId=${2:?groupId not specified or empty}
|
||||
artifactId=${3:?artifactId not specified or empty}
|
||||
classifier=${4?classifier not specified}
|
||||
scope=${5?scope not specified}
|
||||
pomFile=${6:-pom.xml}
|
||||
|
||||
if [[ -d $module ]] && [[ -f "$module/pom.xml" ]]; then
|
||||
cd "$module"
|
||||
insert_dependency "$groupId" "$artifactId" "$classifier" "$scope"
|
||||
echo "[$module] Added $groupId:$artifactId"
|
||||
cd -
|
||||
fi
|
||||
}
|
||||
|
||||
case "$(uname -s)" in
|
||||
Linux*)
|
||||
grep_command="grep"
|
||||
sed_command="sed"
|
||||
;;
|
||||
Darwin*)
|
||||
grep_command="ggrep"
|
||||
sed_command="gsed"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported distribution $(uname -s) for this script."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Migrating to JUnit 5..."
|
||||
echo "Adding required dependencies..."
|
||||
|
||||
if $grep_command -q "<packaging>pom</packaging>" "pom.xml"; then
|
||||
|
||||
for module in $($grep_command -rl "org.testng.annotations.Test" $(pwd) | awk -F "$(pwd)" '{print $2}' | awk -F '/' '{print $2}' | uniq); do
|
||||
(
|
||||
handle_file "$module" "org.junit.jupiter" "junit-jupiter-api" "" "test"
|
||||
)
|
||||
(
|
||||
handle_file "$module" "org.junit.jupiter" "junit-jupiter-engine" "" "test"
|
||||
)
|
||||
done
|
||||
for module in $($grep_command -rl "org.testng.annotations.DataProvider" $(pwd) | awk -F "$(pwd)" '{print $2}' | awk -F '/' '{print $2}' | uniq); do
|
||||
(
|
||||
handle_file "$module" "org.junit.jupiter" "junit-jupiter-params" "" "test"
|
||||
)
|
||||
done
|
||||
else
|
||||
if $grep_command -rq "org.testng.annotations.Test" "src/"; then
|
||||
handle_file "./" "org.junit.jupiter" "junit-jupiter-api" "" "test"
|
||||
handle_file "./" "org.junit.jupiter" "junit-jupiter-engine" "" "test"
|
||||
fi
|
||||
if $grep_command -rq "org.testng.annotations.DataProvider" "src/"; then
|
||||
handle_file "./" "org.junit.jupiter" "junit-jupiter-params" "" "test"
|
||||
fi
|
||||
fi
|
||||
echo "Running migration..."
|
||||
mvn \
|
||||
-Perror-prone \
|
||||
-Ptestng-migrator \
|
||||
-Ppatch \
|
||||
clean test-compile fmt:format \
|
||||
-Derror-prone.patch-checks="TestNGJUnitMigration" \
|
||||
-Dverification.skip
|
||||
|
||||
echo "Finished executing migration!"
|
||||
@@ -0,0 +1,29 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* Interface implemented by classes that define how to migrate a specific attribute from a TestNG
|
||||
* {@code Test} annotation to JUnit.
|
||||
*/
|
||||
@Immutable
|
||||
interface AttributeMigrator {
|
||||
/**
|
||||
* Attempts to create a {@link SuggestedFix}.
|
||||
*
|
||||
* @param methodTree The method tree the annotation is on.
|
||||
* @param state The visitor state.
|
||||
* @return an {@link Optional} containing the created fix. This returns an {@link
|
||||
* Optional#empty()} if the {@link AttributeMigrator} is not able to migrate the attribute.
|
||||
*/
|
||||
Optional<SuggestedFix> migrate(
|
||||
TestNgMetadata metadata,
|
||||
AnnotationMetadata annotation,
|
||||
MethodTree methodTree,
|
||||
VisitorState state);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* A {@link AttributeMigrator} that migrates the {@code org.testng.annotations.Test#dataProvider}
|
||||
* attributes.
|
||||
*/
|
||||
@Immutable
|
||||
final class DataProviderAttributeMigrator implements AttributeMigrator {
|
||||
@Override
|
||||
public Optional<SuggestedFix> migrate(
|
||||
TestNgMetadata metadata,
|
||||
AnnotationMetadata annotation,
|
||||
MethodTree methodTree,
|
||||
VisitorState state) {
|
||||
ExpressionTree dataProviderNameExpressionTree = annotation.getAttributes().get("dataProvider");
|
||||
if (dataProviderNameExpressionTree == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
String dataProviderName = ASTHelpers.constValue(dataProviderNameExpressionTree, String.class);
|
||||
if (!metadata.getDataProviderMetadata().containsKey(dataProviderName)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(
|
||||
SuggestedFix.builder()
|
||||
.addImport("org.junit.jupiter.params.ParameterizedTest")
|
||||
.addImport("org.junit.jupiter.params.provider.MethodSource")
|
||||
.prefixWith(methodTree, "@ParameterizedTest\n")
|
||||
.prefixWith(methodTree, String.format("@MethodSource(\"%s\")%n", dataProviderName))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.sun.source.tree.Tree.Kind.NEW_ARRAY;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.google.errorprone.util.ErrorProneToken;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.NewArrayTree;
|
||||
import com.sun.source.tree.ReturnTree;
|
||||
import com.sun.tools.javac.parser.Tokens.Comment;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.util.SourceCode;
|
||||
|
||||
// XXX: Can this one also implement a `Migrator`?
|
||||
/** A helper class that migrates a TestNG {@code DataProvider} to a JUnit {@code MethodSource}. */
|
||||
final class DataProviderMigrator {
|
||||
/** This regular expression replaces matches instances of `this.getClass()` and `getClass()`. */
|
||||
private static final Pattern GET_CLASS =
|
||||
Pattern.compile("((?<!\\b\\.)|(\\bthis\\.))(getClass\\(\\))");
|
||||
|
||||
private DataProviderMigrator() {}
|
||||
|
||||
/**
|
||||
* Tells whether the specified {@code DataProvider} can be migrated.
|
||||
*
|
||||
* @param methodTree The dataprovider methode tree.
|
||||
* @return {@code true} if the data provider can be migrated or else {@code false}.
|
||||
*/
|
||||
static boolean canFix(MethodTree methodTree) {
|
||||
return getDataProviderReturnTree(getReturnTree(methodTree)).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@link SuggestedFix} required to migrate a TestNG {@code DataProvider} to a JUnit
|
||||
* {@code MethodSource}.
|
||||
*
|
||||
* @param classTree The class containing the data provider.
|
||||
* @param methodTree The data provider method.
|
||||
* @param state The {@link VisitorState}.
|
||||
* @return An {@link Optional} containing the created fix.
|
||||
*/
|
||||
static Optional<SuggestedFix> createFix(
|
||||
ClassTree classTree, MethodTree methodTree, VisitorState state) {
|
||||
return tryMigrateDataProvider(methodTree, classTree, state);
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix> tryMigrateDataProvider(
|
||||
MethodTree methodTree, ClassTree classTree, VisitorState state) {
|
||||
ReturnTree returnTree = getReturnTree(methodTree);
|
||||
|
||||
return getDataProviderReturnTree(returnTree)
|
||||
.map(
|
||||
dataProviderReturnTree ->
|
||||
SuggestedFix.builder()
|
||||
.addStaticImport("org.junit.jupiter.params.provider.Arguments.arguments")
|
||||
.addImport(Stream.class.getCanonicalName())
|
||||
.addImport("org.junit.jupiter.params.provider.Arguments")
|
||||
.delete(methodTree)
|
||||
.postfixWith(
|
||||
methodTree,
|
||||
buildMethodSource(
|
||||
classTree.getSimpleName().toString(),
|
||||
methodTree.getName().toString(),
|
||||
methodTree,
|
||||
returnTree,
|
||||
dataProviderReturnTree,
|
||||
state))
|
||||
.build());
|
||||
}
|
||||
|
||||
private static ReturnTree getReturnTree(MethodTree methodTree) {
|
||||
return methodTree.getBody().getStatements().stream()
|
||||
.filter(ReturnTree.class::isInstance)
|
||||
.findFirst()
|
||||
.map(ReturnTree.class::cast)
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
private static Optional<NewArrayTree> getDataProviderReturnTree(ReturnTree returnTree) {
|
||||
if (returnTree.getExpression().getKind() != NEW_ARRAY
|
||||
|| ((NewArrayTree) returnTree.getExpression()).getInitializers().isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of((NewArrayTree) returnTree.getExpression());
|
||||
}
|
||||
|
||||
private static String buildMethodSource(
|
||||
String className,
|
||||
String name,
|
||||
MethodTree methodTree,
|
||||
ReturnTree returnTree,
|
||||
NewArrayTree newArrayTree,
|
||||
VisitorState state) {
|
||||
StringBuilder sourceBuilder =
|
||||
new StringBuilder()
|
||||
.append(" private static Stream<Arguments> ")
|
||||
.append(name)
|
||||
.append(" () ");
|
||||
|
||||
if (!methodTree.getThrows().isEmpty()) {
|
||||
sourceBuilder
|
||||
.append(" throws ")
|
||||
.append(
|
||||
methodTree.getThrows().stream()
|
||||
.filter(IdentifierTree.class::isInstance)
|
||||
.map(IdentifierTree.class::cast)
|
||||
.map(identifierTree -> identifierTree.getName().toString())
|
||||
.collect(joining(", ")));
|
||||
}
|
||||
|
||||
return sourceBuilder
|
||||
.append(" {\n")
|
||||
.append(extractMethodBodyWithoutReturnStatement(methodTree, returnTree, state))
|
||||
.append(" return ")
|
||||
.append(buildArgumentStream(className, newArrayTree, state))
|
||||
.append(";\n}")
|
||||
.toString();
|
||||
}
|
||||
|
||||
private static String extractMethodBodyWithoutReturnStatement(
|
||||
MethodTree methodTree, ReturnTree returnTree, VisitorState state) {
|
||||
String body = SourceCode.treeToString(methodTree.getBody(), state);
|
||||
return body.substring(2, body.indexOf(SourceCode.treeToString(returnTree, state)) - 1);
|
||||
}
|
||||
|
||||
private static String buildArgumentStream(
|
||||
String className, NewArrayTree newArrayTree, VisitorState state) {
|
||||
int startPos = ASTHelpers.getStartPosition(newArrayTree);
|
||||
int endPos = state.getEndPosition(newArrayTree);
|
||||
ImmutableMap<Integer, List<Comment>> comments =
|
||||
state.getOffsetTokens(startPos, endPos).stream()
|
||||
.collect(toImmutableMap(ErrorProneToken::pos, ErrorProneToken::comments));
|
||||
|
||||
StringBuilder argumentsBuilder = new StringBuilder();
|
||||
argumentsBuilder.append(
|
||||
newArrayTree.getInitializers().stream()
|
||||
.map(
|
||||
expression ->
|
||||
wrapTestValueWithArguments(
|
||||
expression,
|
||||
comments.getOrDefault(
|
||||
ASTHelpers.getStartPosition(expression), ImmutableList.of()),
|
||||
state))
|
||||
.collect(joining(",")));
|
||||
|
||||
/*
|
||||
* This replaces all instances of `{,this.}getClass()` with the fully qualified class name to
|
||||
* retain functionality in static context.
|
||||
*/
|
||||
return GET_CLASS
|
||||
.matcher(String.format("Stream.of(%s%n)", argumentsBuilder))
|
||||
.replaceAll(className + ".class");
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a value in {@code org.junit.jupiter.params.provider#arguments()}.
|
||||
*
|
||||
* <p>Drops curly braces from array initialisation values.
|
||||
*/
|
||||
private static String wrapTestValueWithArguments(
|
||||
ExpressionTree tree, List<Comment> comments, VisitorState state) {
|
||||
String source = SourceCode.treeToString(tree, state);
|
||||
|
||||
String argumentValue =
|
||||
tree.getKind() == NEW_ARRAY ? source.substring(1, source.length() - 1) : source;
|
||||
|
||||
return String.format(
|
||||
"\t\t%s%n\t\targuments(%s)",
|
||||
comments.stream().map(Comment::getText).collect(joining("\n")), argumentValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.AnnotationMetadata;
|
||||
import tech.picnic.errorprone.util.SourceCode;
|
||||
|
||||
/** A {@link AttributeMigrator} that migrates the {@code description} attribute. */
|
||||
@Immutable
|
||||
final class DescriptionAttributeMigrator implements AttributeMigrator {
|
||||
@Override
|
||||
public Optional<SuggestedFix> migrate(
|
||||
TestNgMetadata metadata,
|
||||
AnnotationMetadata annotation,
|
||||
MethodTree methodTree,
|
||||
VisitorState state) {
|
||||
return Optional.ofNullable(annotation.getAttributes().get("description"))
|
||||
.map(
|
||||
description ->
|
||||
SuggestedFix.builder()
|
||||
.addImport("org.junit.jupiter.api.DisplayName")
|
||||
.prefixWith(
|
||||
methodTree,
|
||||
String.format(
|
||||
"@DisplayName(%s)%n", SourceCode.treeToString(description, state)))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.sun.source.tree.LiteralTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.AnnotationMetadata;
|
||||
|
||||
/** A {@link AttributeMigrator} that migrates the {@code enabled} attribute. */
|
||||
@Immutable
|
||||
final class EnabledAttributeMigrator implements AttributeMigrator {
|
||||
@Override
|
||||
public Optional<SuggestedFix> migrate(
|
||||
TestNgMetadata metadata,
|
||||
AnnotationMetadata annotation,
|
||||
MethodTree methodTree,
|
||||
VisitorState state) {
|
||||
return Optional.ofNullable(annotation.getAttributes().get("enabled"))
|
||||
.map(enabled -> ((LiteralTree) enabled).getValue())
|
||||
.filter(Boolean.FALSE::equals)
|
||||
.map(
|
||||
unused ->
|
||||
SuggestedFix.builder()
|
||||
.addImport("org.junit.jupiter.api.Disabled")
|
||||
.prefixWith(methodTree, "@Disabled\n")
|
||||
.build())
|
||||
.or(() -> Optional.of(SuggestedFix.emptyFix()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import static com.google.auto.common.MoreStreams.toImmutableList;
|
||||
import static com.sun.source.tree.Tree.Kind.MEMBER_SELECT;
|
||||
import static com.sun.source.tree.Tree.Kind.NEW_ARRAY;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.sun.source.tree.BlockTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.NewArrayTree;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.AnnotationMetadata;
|
||||
import tech.picnic.errorprone.util.SourceCode;
|
||||
|
||||
/** A {@link AttributeMigrator} that migrates the {@code expectedExceptions} attribute. */
|
||||
@Immutable
|
||||
final class ExpectedExceptionsAttributeMigrator implements AttributeMigrator {
|
||||
@Override
|
||||
public Optional<SuggestedFix> migrate(
|
||||
TestNgMetadata metadata,
|
||||
AnnotationMetadata annotation,
|
||||
MethodTree methodTree,
|
||||
VisitorState state) {
|
||||
return Optional.ofNullable(annotation.getAttributes().get("expectedExceptions"))
|
||||
.map(
|
||||
expectedExceptions ->
|
||||
getExpectedException(expectedExceptions, state)
|
||||
.map(
|
||||
expectedException -> {
|
||||
SuggestedFix.Builder fix =
|
||||
SuggestedFix.builder()
|
||||
.replace(
|
||||
methodTree.getBody(),
|
||||
buildWrappedBody(
|
||||
methodTree.getBody(), expectedException, state));
|
||||
ImmutableList<String> removedExceptions =
|
||||
getRemovedExceptions(expectedExceptions, state);
|
||||
if (!removedExceptions.isEmpty()) {
|
||||
fix.prefixWith(
|
||||
methodTree,
|
||||
String.format(
|
||||
"// XXX: Removed handling of `%s` because this migration doesn't support%n// XXX: multiple expected exceptions.%n",
|
||||
String.join(", ", removedExceptions)));
|
||||
}
|
||||
|
||||
return fix.build();
|
||||
})
|
||||
.orElseGet(SuggestedFix::emptyFix));
|
||||
}
|
||||
|
||||
private static Optional<String> getExpectedException(
|
||||
ExpressionTree expectedExceptions, VisitorState state) {
|
||||
if (expectedExceptions.getKind() == NEW_ARRAY) {
|
||||
NewArrayTree arrayTree = (NewArrayTree) expectedExceptions;
|
||||
if (arrayTree.getInitializers().isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(SourceCode.treeToString(arrayTree.getInitializers().get(0), state));
|
||||
} else if (expectedExceptions.getKind() == MEMBER_SELECT) {
|
||||
return Optional.of(SourceCode.treeToString(expectedExceptions, state));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static ImmutableList<String> getRemovedExceptions(
|
||||
ExpressionTree expectedExceptions, VisitorState state) {
|
||||
if (expectedExceptions.getKind() != NEW_ARRAY) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
NewArrayTree arrayTree = (NewArrayTree) expectedExceptions;
|
||||
return arrayTree.getInitializers().subList(1, arrayTree.getInitializers().size()).stream()
|
||||
.map(initializer -> SourceCode.treeToString(initializer, state))
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
private static String buildWrappedBody(BlockTree tree, String exception, VisitorState state) {
|
||||
return String.format(
|
||||
"{%norg.junit.jupiter.api.Assertions.assertThrows(%s, () -> %s);%n}",
|
||||
exception, SourceCode.treeToString(tree, state));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.NewArrayTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.AnnotationMetadata;
|
||||
import tech.picnic.errorprone.util.SourceCode;
|
||||
|
||||
/** A {@link AttributeMigrator} that migrates the {@code group} attribute. */
|
||||
@Immutable
|
||||
final class GroupsAttributeMigrator implements AttributeMigrator {
|
||||
@Override
|
||||
public Optional<SuggestedFix> migrate(
|
||||
TestNgMetadata metadata,
|
||||
AnnotationMetadata annotation,
|
||||
MethodTree methodTree,
|
||||
VisitorState state) {
|
||||
ExpressionTree groupsExpression = annotation.getAttributes().get("groups");
|
||||
if (groupsExpression == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
ImmutableList<String> groups = extractGroups(groupsExpression, state);
|
||||
if (!groups.stream().allMatch(GroupsAttributeMigrator::isValidTagName)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder().addImport("org.junit.jupiter.api.Tag");
|
||||
groups.forEach(group -> fix.prefixWith(methodTree, String.format("@Tag(\"%s\")%n", group)));
|
||||
|
||||
return Optional.of(fix.build());
|
||||
}
|
||||
|
||||
private static boolean isValidTagName(String tagName) {
|
||||
return !tagName.isEmpty() && tagName.chars().noneMatch(Character::isISOControl);
|
||||
}
|
||||
|
||||
private static ImmutableList<String> extractGroups(ExpressionTree dataValue, VisitorState state) {
|
||||
if (dataValue.getKind() == Tree.Kind.STRING_LITERAL) {
|
||||
return ImmutableList.of(trimTagName(SourceCode.treeToString(dataValue, state)));
|
||||
}
|
||||
|
||||
NewArrayTree groupsTree = (NewArrayTree) dataValue;
|
||||
return groupsTree.getInitializers().stream()
|
||||
.map(initializer -> trimTagName(SourceCode.treeToString(initializer, state)))
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
private static String trimTagName(String tagName) {
|
||||
return tagName.replaceAll("(^\")|(\"$)", "").trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.AnnotationMetadata;
|
||||
import tech.picnic.errorprone.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link AttributeMigrator} that migrates the {@code org.testng.annotations.Test#priority}
|
||||
* attribute.
|
||||
*/
|
||||
@Immutable
|
||||
final class PriorityAttributeMigrator implements AttributeMigrator {
|
||||
@Override
|
||||
public Optional<SuggestedFix> migrate(
|
||||
TestNgMetadata metadata,
|
||||
AnnotationMetadata annotation,
|
||||
MethodTree methodTree,
|
||||
VisitorState state) {
|
||||
return Optional.ofNullable(annotation.getAttributes().get("priority"))
|
||||
.map(
|
||||
priority ->
|
||||
SuggestedFix.builder()
|
||||
.addImport("org.junit.jupiter.api.Order")
|
||||
.addImport("org.junit.jupiter.api.TestMethodOrder")
|
||||
.addImport("org.junit.jupiter.api.MethodOrderer")
|
||||
.prefixWith(
|
||||
methodTree,
|
||||
String.format("@Order(%s)%n", SourceCode.treeToString(priority, state)))
|
||||
.prefixWith(
|
||||
metadata.getClassTree(),
|
||||
"@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.SetupTeardownType;
|
||||
|
||||
/**
|
||||
* A helper class that migrates TestNG setup and teardown methods to their JUnit Jupiter equivalent.
|
||||
*/
|
||||
final class SetupTeardownMethodMigrator {
|
||||
private SetupTeardownMethodMigrator() {}
|
||||
|
||||
/**
|
||||
* Create the {@link SuggestedFix} required to migrate a TestNG setup/teardown methods to the
|
||||
* JUnit Jupiter variant.
|
||||
*
|
||||
* @param tree The setup/teardown method tree.
|
||||
* @param type The setup/teardown type.
|
||||
* @param state The visitor state.
|
||||
* @return An {@link Optional} containing the created fix.
|
||||
*/
|
||||
static Optional<SuggestedFix> createFix(
|
||||
MethodTree tree, SetupTeardownType type, VisitorState state) {
|
||||
return getSetupTeardownAnnotationTree(tree, type, state)
|
||||
.map(
|
||||
annotation -> {
|
||||
SuggestedFix.Builder fix =
|
||||
SuggestedFix.builder()
|
||||
.replace(annotation, String.format("@%s", type.getJunitAnnotationClass()));
|
||||
if (type.requiresStaticMethod()
|
||||
&& !tree.getModifiers().getFlags().contains(Modifier.STATIC)) {
|
||||
SuggestedFixes.addModifiers(tree, state, Modifier.STATIC).ifPresent(fix::merge);
|
||||
}
|
||||
|
||||
return fix.build();
|
||||
});
|
||||
}
|
||||
|
||||
private static Optional<? extends AnnotationTree> getSetupTeardownAnnotationTree(
|
||||
MethodTree tree, SetupTeardownType type, VisitorState state) {
|
||||
return ASTHelpers.getAnnotations(tree).stream()
|
||||
.filter(annotation -> type.getAnnotationMatcher().matches(annotation, state))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/** The annotation attributes that are supported by the TestNG to JUnit Jupiter migration. */
|
||||
enum TestAnnotationAttribute {
|
||||
DATA_PROVIDER("dataProvider", new DataProviderAttributeMigrator()),
|
||||
DESCRIPTION("description", new DescriptionAttributeMigrator()),
|
||||
ENABLED("enabled", new EnabledAttributeMigrator()),
|
||||
EXPECTED_EXCEPTIONS("expectedExceptions", new ExpectedExceptionsAttributeMigrator()),
|
||||
GROUPS("groups", new GroupsAttributeMigrator()),
|
||||
PRIORITY("priority", new PriorityAttributeMigrator()),
|
||||
TIMEOUT("timeOut", new TimeOutAttributeMigrator());
|
||||
|
||||
private final String name;
|
||||
private final AttributeMigrator attributeMigrator;
|
||||
|
||||
TestAnnotationAttribute(String name, AttributeMigrator attributeMigrator) {
|
||||
this.name = name;
|
||||
this.attributeMigrator = attributeMigrator;
|
||||
}
|
||||
|
||||
AttributeMigrator getAttributeMigrator() {
|
||||
return attributeMigrator;
|
||||
}
|
||||
|
||||
static Optional<TestAnnotationAttribute> fromString(String attribute) {
|
||||
return stream(values()).filter(v -> v.name.equals(attribute)).findFirst();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.REFACTORING;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.ErrorProneFlags;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.CompilationUnitTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.CompilationUnitTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.AnnotationMetadata;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.DataProviderMetadata;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.SetupTeardownType;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that migrates TestNG unit tests to JUnit 5.
|
||||
*
|
||||
* <p>Supported TestNG annotation attributes are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code dataProvider}
|
||||
* <li>{@code description}
|
||||
* <li>{@code isEnabled}
|
||||
* <li>{@code expectedExceptions}
|
||||
* <li>{@code priority}
|
||||
* <li>{@code groups}
|
||||
* </ul>
|
||||
*
|
||||
* This migration will also take care of any setup/teardown methods.
|
||||
*
|
||||
* <p>Note: As the {@code @BeforeAll} and {@code @AfterAll} methods in JUnit are required to be
|
||||
* static, this <em>might</em> introduce breaking changes.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Migrate TestNG tests to their JUnit equivalent",
|
||||
linkType = NONE,
|
||||
tags = REFACTORING,
|
||||
severity = ERROR)
|
||||
public final class TestNGJUnitMigration extends BugChecker implements CompilationUnitTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String CONSERVATIVE_MIGRATION_MODE_FLAG =
|
||||
"TestNGJUnitMigration:ConservativeMode";
|
||||
|
||||
private final boolean conservativeMode;
|
||||
|
||||
/**
|
||||
* Instantiates a new {@link TestNGJUnitMigration} instance. This will default to the aggressive
|
||||
* migration mode.
|
||||
*/
|
||||
public TestNGJUnitMigration() {
|
||||
this(ErrorProneFlags.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new {@link TestNGJUnitMigration} with the specified {@link ErrorProneFlags}.
|
||||
*
|
||||
* @param flags The Error Prone flags used to set the migration mode.
|
||||
*/
|
||||
@Inject
|
||||
TestNGJUnitMigration(ErrorProneFlags flags) {
|
||||
conservativeMode = flags.getBoolean(CONSERVATIVE_MIGRATION_MODE_FLAG).orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) {
|
||||
TestNGScanner scanner = new TestNGScanner(state);
|
||||
ImmutableMap<ClassTree, TestNgMetadata> classMetaData = scanner.collectMetadataForClasses(tree);
|
||||
|
||||
new TreeScanner<@Nullable Void, TestNgMetadata>() {
|
||||
@Override
|
||||
public @Nullable Void visitClass(ClassTree node, TestNgMetadata testNgMetadata) {
|
||||
TestNgMetadata metadata = classMetaData.get(node);
|
||||
if (metadata == null) {
|
||||
return super.visitClass(node, testNgMetadata);
|
||||
}
|
||||
|
||||
for (DataProviderMetadata dataProviderMetadata : metadata.getDataProvidersInUse()) {
|
||||
DataProviderMigrator.createFix(
|
||||
metadata.getClassTree(), dataProviderMetadata.getMethodTree(), state)
|
||||
.ifPresent(
|
||||
fix ->
|
||||
state.reportMatch(
|
||||
describeMatch(
|
||||
dataProviderMetadata.getMethodTree(),
|
||||
fix.toBuilder().removeStaticImport("org.testng.Assert.*").build())));
|
||||
}
|
||||
|
||||
for (Entry<MethodTree, SetupTeardownType> entry : metadata.getSetupTeardown().entrySet()) {
|
||||
SetupTeardownMethodMigrator.createFix(entry.getKey(), entry.getValue(), state)
|
||||
.ifPresent(fix -> state.reportMatch(describeMatch(entry.getKey(), fix)));
|
||||
}
|
||||
|
||||
super.visitClass(node, metadata);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Void visitMethod(MethodTree tree, TestNgMetadata metadata) {
|
||||
/* Make sure ALL Tests in the class can be migrated. */
|
||||
if (conservativeMode && !canMigrateAllTestsInClass(metadata, state)) {
|
||||
return super.visitMethod(tree, metadata);
|
||||
}
|
||||
|
||||
metadata
|
||||
.getAnnotation(tree)
|
||||
.ifPresent(
|
||||
annotation -> {
|
||||
SuggestedFix.Builder fixBuilder = SuggestedFix.builder();
|
||||
buildAttributeFixes(metadata, annotation, tree, state).forEach(fixBuilder::merge);
|
||||
|
||||
fixBuilder.merge(migrateAnnotation(annotation, tree));
|
||||
state.reportMatch(describeMatch(tree, fixBuilder.build()));
|
||||
});
|
||||
return super.visitMethod(tree, metadata);
|
||||
}
|
||||
}.scan(tree, null);
|
||||
|
||||
/* All suggested fixes are already directly reported to the `VisitorState`. */
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
private static ImmutableList<SuggestedFix> buildAttributeFixes(
|
||||
TestNgMetadata metadata,
|
||||
AnnotationMetadata annotationMetadata,
|
||||
MethodTree methodTree,
|
||||
VisitorState state) {
|
||||
return annotationMetadata.getAttributes().entrySet().stream()
|
||||
.flatMap(
|
||||
entry ->
|
||||
trySuggestFix(metadata, annotationMetadata, entry.getKey(), methodTree, state)
|
||||
.stream())
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
private static boolean canMigrateTest(
|
||||
MethodTree methodTree,
|
||||
TestNgMetadata metadata,
|
||||
AnnotationMetadata annotationMetadata,
|
||||
VisitorState state) {
|
||||
ImmutableList<TestAnnotationAttribute> attributes =
|
||||
annotationMetadata.getAttributes().keySet().stream()
|
||||
.map(TestAnnotationAttribute::fromString)
|
||||
.flatMap(Optional::stream)
|
||||
.collect(toImmutableList());
|
||||
return (annotationMetadata.getAttributes().isEmpty() || !attributes.isEmpty())
|
||||
&& attributes.stream()
|
||||
.allMatch(
|
||||
kind ->
|
||||
kind.getAttributeMigrator()
|
||||
.migrate(metadata, annotationMetadata, methodTree, state)
|
||||
.isPresent());
|
||||
}
|
||||
|
||||
private static boolean canMigrateAllTestsInClass(TestNgMetadata metadata, VisitorState state) {
|
||||
return metadata.getMethodAnnotations().entrySet().stream()
|
||||
.allMatch(entry -> canMigrateTest(entry.getKey(), metadata, entry.getValue(), state));
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix> trySuggestFix(
|
||||
TestNgMetadata metadata,
|
||||
AnnotationMetadata annotation,
|
||||
String attributeName,
|
||||
MethodTree methodTree,
|
||||
VisitorState state) {
|
||||
return TestAnnotationAttribute.fromString(attributeName)
|
||||
.map(TestAnnotationAttribute::getAttributeMigrator)
|
||||
.flatMap(migrator -> migrator.migrate(metadata, annotation, methodTree, state))
|
||||
.or(
|
||||
() ->
|
||||
UnsupportedAttributeMigrator.migrate(annotation, methodTree, attributeName, state));
|
||||
}
|
||||
|
||||
private static SuggestedFix migrateAnnotation(
|
||||
AnnotationMetadata annotationMetadata, MethodTree methodTree) {
|
||||
SuggestedFix.Builder fixBuilder =
|
||||
SuggestedFix.builder().delete(annotationMetadata.getAnnotationTree());
|
||||
if (!annotationMetadata.getAttributes().containsKey("dataProvider")) {
|
||||
fixBuilder.prefixWith(methodTree, "@org.junit.jupiter.api.Test\n ");
|
||||
}
|
||||
|
||||
return fixBuilder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.matchers.TestNgMatchers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
|
||||
/**
|
||||
* A collection of TestNG-specific helper methods and {@link Matcher}s.
|
||||
*
|
||||
* <p>These constants and methods are additions to the ones found in {@link TestNgMatchers}.
|
||||
*/
|
||||
final class TestNGMatchers {
|
||||
/**
|
||||
* Matches the TestNG {@code Test} annotation specifically. As {@link
|
||||
* TestNgMatchers#hasTestNgAnnotation(ClassTree)} also other TestNG annotations.
|
||||
*/
|
||||
public static final Matcher<AnnotationTree> TESTNG_TEST_ANNOTATION =
|
||||
isType("org.testng.annotations.Test");
|
||||
|
||||
/** Matches the TestNG {@code DataProvider} annotation specifically. */
|
||||
public static final Matcher<MethodTree> TESTNG_VALUE_FACTORY_METHOD =
|
||||
hasAnnotation("org.testng.annotations.DataProvider");
|
||||
|
||||
private TestNGMatchers() {}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
|
||||
import static com.google.errorprone.matchers.Matchers.hasModifier;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static tech.picnic.errorprone.testngjunit.TestNGMatchers.TESTNG_TEST_ANNOTATION;
|
||||
import static tech.picnic.errorprone.testngjunit.TestNGMatchers.TESTNG_VALUE_FACTORY_METHOD;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AssignmentTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.CompilationUnitTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.AnnotationMetadata;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.DataProviderMetadata;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.SetupTeardownType;
|
||||
|
||||
/**
|
||||
* A {@link TreeScanner} which will scan a {@link com.sun.source.tree.CompilationUnitTree} and
|
||||
* collect data required for the migration from each class in the compilation unit.
|
||||
*
|
||||
* <p>This data can be retrieved using {@link #collectMetadataForClasses(CompilationUnitTree)}.
|
||||
*/
|
||||
final class TestNGScanner extends TreeScanner<@Nullable Void, TestNgMetadata.Builder> {
|
||||
private static final Matcher<MethodTree> TESTNG_TEST_METHOD =
|
||||
anyOf(
|
||||
hasAnnotation("org.testng.annotations.Test"),
|
||||
allOf(hasModifier(Modifier.PUBLIC), not(hasModifier(Modifier.STATIC))));
|
||||
|
||||
private final ImmutableMap.Builder<ClassTree, TestNgMetadata> metadataBuilder =
|
||||
ImmutableMap.builder();
|
||||
private final VisitorState state;
|
||||
|
||||
TestNGScanner(VisitorState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Void visitClass(ClassTree tree, TestNgMetadata.Builder unused) {
|
||||
TestNgMetadata.Builder builder = TestNgMetadata.builder();
|
||||
builder.setClassTree(tree);
|
||||
getTestNgAnnotation(tree, state).ifPresent(builder::setClassLevelAnnotationMetadata);
|
||||
super.visitClass(tree, builder);
|
||||
metadataBuilder.put(tree, builder.build());
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Void visitMethod(MethodTree tree, TestNgMetadata.Builder builder) {
|
||||
if (ASTHelpers.isGeneratedConstructor(tree)) {
|
||||
return super.visitMethod(tree, builder);
|
||||
}
|
||||
|
||||
if (TESTNG_VALUE_FACTORY_METHOD.matches(tree, state) && DataProviderMigrator.canFix(tree)) {
|
||||
builder
|
||||
.dataProviderMetadataBuilder()
|
||||
.put(tree.getName().toString(), DataProviderMetadata.create(tree));
|
||||
return super.visitMethod(tree, builder);
|
||||
}
|
||||
|
||||
Optional<SetupTeardownType> setupTeardownType = SetupTeardownType.matchType(tree, state);
|
||||
if (setupTeardownType.isPresent()) {
|
||||
builder.setupTeardownBuilder().put(tree, setupTeardownType.orElseThrow());
|
||||
return super.visitMethod(tree, builder);
|
||||
}
|
||||
|
||||
if (TESTNG_TEST_METHOD.matches(tree, state)) {
|
||||
getTestNgAnnotation(tree, state)
|
||||
.or(builder::getClassLevelAnnotationMetadata)
|
||||
.ifPresent(annotation -> builder.methodAnnotationsBuilder().put(tree, annotation));
|
||||
}
|
||||
|
||||
return super.visitMethod(tree, builder);
|
||||
}
|
||||
|
||||
public ImmutableMap<ClassTree, TestNgMetadata> collectMetadataForClasses(
|
||||
CompilationUnitTree tree) {
|
||||
scan(tree, null);
|
||||
return metadataBuilder.build();
|
||||
}
|
||||
|
||||
@CanIgnoreReturnValue
|
||||
private static Optional<AnnotationMetadata> getTestNgAnnotation(Tree tree, VisitorState state) {
|
||||
return ASTHelpers.getAnnotations(tree).stream()
|
||||
.filter(annotation -> TESTNG_TEST_ANNOTATION.matches(annotation, state))
|
||||
.findFirst()
|
||||
.map(
|
||||
annotationTree ->
|
||||
AnnotationMetadata.create(
|
||||
annotationTree,
|
||||
annotationTree.getArguments().stream()
|
||||
.filter(AssignmentTree.class::isInstance)
|
||||
.map(AssignmentTree.class::cast)
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
assignment ->
|
||||
((IdentifierTree) assignment.getVariable())
|
||||
.getName()
|
||||
.toString(),
|
||||
AssignmentTree::getExpression))));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* POJO containing data collected using {@link TestNGScanner} for use in {@link
|
||||
* TestNGJUnitMigration}.
|
||||
*/
|
||||
@AutoValue
|
||||
abstract class TestNgMetadata {
|
||||
abstract ClassTree getClassTree();
|
||||
|
||||
abstract Optional<AnnotationMetadata> getClassLevelAnnotationMetadata();
|
||||
|
||||
abstract ImmutableMap<MethodTree, AnnotationMetadata> getMethodAnnotations();
|
||||
|
||||
abstract ImmutableMap<MethodTree, SetupTeardownType> getSetupTeardown();
|
||||
|
||||
/**
|
||||
* Retrieve the tests that can be migrated.
|
||||
*
|
||||
* @return An {@link ImmutableMap} with mapping {@code DataProvider}'s name to its respective
|
||||
* metadata.
|
||||
*/
|
||||
public abstract ImmutableMap<String, DataProviderMetadata> getDataProviderMetadata();
|
||||
|
||||
final ImmutableList<DataProviderMetadata> getDataProvidersInUse() {
|
||||
return getDataProviderMetadata().entrySet().stream()
|
||||
.filter(
|
||||
entry ->
|
||||
getAnnotations().stream()
|
||||
.anyMatch(
|
||||
annotation -> {
|
||||
ExpressionTree dataProviderNameExpression =
|
||||
annotation.getAttributes().get("dataProvider");
|
||||
if (dataProviderNameExpression == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ASTHelpers.constValue(dataProviderNameExpression, String.class)
|
||||
.equals(entry.getKey());
|
||||
}))
|
||||
.map(Map.Entry::getValue)
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
final ImmutableSet<AnnotationMetadata> getAnnotations() {
|
||||
return ImmutableSet.copyOf(getMethodAnnotations().values());
|
||||
}
|
||||
|
||||
final Optional<AnnotationMetadata> getAnnotation(MethodTree methodTree) {
|
||||
return Optional.ofNullable(getMethodAnnotations().get(methodTree));
|
||||
}
|
||||
|
||||
static Builder builder() {
|
||||
return new AutoValue_TestNgMetadata.Builder();
|
||||
}
|
||||
|
||||
@AutoValue.Builder
|
||||
abstract static class Builder {
|
||||
abstract ImmutableMap.Builder<MethodTree, AnnotationMetadata> methodAnnotationsBuilder();
|
||||
|
||||
abstract ImmutableMap.Builder<MethodTree, SetupTeardownType> setupTeardownBuilder();
|
||||
|
||||
abstract ImmutableMap.Builder<String, DataProviderMetadata> dataProviderMetadataBuilder();
|
||||
|
||||
abstract Builder setClassTree(ClassTree value);
|
||||
|
||||
abstract Optional<AnnotationMetadata> getClassLevelAnnotationMetadata();
|
||||
|
||||
abstract Builder setClassLevelAnnotationMetadata(AnnotationMetadata value);
|
||||
|
||||
abstract Builder setMethodAnnotations(ImmutableMap<MethodTree, AnnotationMetadata> value);
|
||||
|
||||
abstract Builder setSetupTeardown(ImmutableMap<MethodTree, SetupTeardownType> value);
|
||||
|
||||
abstract Builder setDataProviderMetadata(ImmutableMap<String, DataProviderMetadata> value);
|
||||
|
||||
abstract TestNgMetadata build();
|
||||
}
|
||||
|
||||
/**
|
||||
* POJO containing data for a specific {@code Test} annotation for use in {@link
|
||||
* TestNGJUnitMigration}.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract static class AnnotationMetadata {
|
||||
/**
|
||||
* Get the {@link AnnotationTree} of this metadata instance.
|
||||
*
|
||||
* @return the annotation tree this metadata contains information on.
|
||||
*/
|
||||
public abstract AnnotationTree getAnnotationTree();
|
||||
|
||||
/**
|
||||
* A mapping for all attributes in the annotation to their value.
|
||||
*
|
||||
* @return an {@link ImmutableMap} mapping each annotation attribute to their respective value.
|
||||
*/
|
||||
public abstract ImmutableMap<String, ExpressionTree> getAttributes();
|
||||
|
||||
/**
|
||||
* Instantiate a new {@link AnnotationMetadata}.
|
||||
*
|
||||
* @param annotationTree The annotation tree.
|
||||
* @param attributes The attributes in that annotation tree.
|
||||
* @return The new {@link AnnotationMetadata} instance.
|
||||
*/
|
||||
public static AnnotationMetadata create(
|
||||
AnnotationTree annotationTree, ImmutableMap<String, ExpressionTree> attributes) {
|
||||
return new AutoValue_TestNgMetadata_AnnotationMetadata(annotationTree, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ImmutableEnumChecker" /* Matcher instances are final. */)
|
||||
public enum SetupTeardownType {
|
||||
// XXX: Consider using `@BeforeAll` to more accurately preserve behavior. However, note that it
|
||||
// requires a static method and therefore may introduce breaking changes.
|
||||
BEFORE_TEST(
|
||||
"org.testng.annotations.BeforeTest",
|
||||
"org.junit.jupiter.api.BeforeEach",
|
||||
/* requiresStaticMethod= */ false),
|
||||
BEFORE_CLASS(
|
||||
"org.testng.annotations.BeforeClass",
|
||||
"org.junit.jupiter.api.BeforeAll",
|
||||
/* requiresStaticMethod= */ true),
|
||||
BEFORE_METHOD(
|
||||
"org.testng.annotations.BeforeMethod",
|
||||
"org.junit.jupiter.api.BeforeEach",
|
||||
/* requiresStaticMethod= */ false),
|
||||
// XXX: Consider using `@AfterAll` to more accurately preserve behavior. However, note that it
|
||||
// requires a static method and therefore may introduce breaking changes.
|
||||
AFTER_TEST(
|
||||
"org.testng.annotations.AfterTest",
|
||||
"org.junit.jupiter.api.AfterEach",
|
||||
/* requiresStaticMethod= */ false),
|
||||
AFTER_CLASS(
|
||||
"org.testng.annotations.AfterClass",
|
||||
"org.junit.jupiter.api.AfterAll",
|
||||
/* requiresStaticMethod= */ true),
|
||||
AFTER_METHOD(
|
||||
"org.testng.annotations.AfterMethod",
|
||||
"org.junit.jupiter.api.AfterEach",
|
||||
/* requiresStaticMethod= */ false);
|
||||
|
||||
private final Matcher<AnnotationTree> annotationMatcher;
|
||||
private final Matcher<MethodTree> methodTreeMatcher;
|
||||
private final String junitAnnotationClass;
|
||||
private final boolean requiresStaticMethod;
|
||||
|
||||
SetupTeardownType(
|
||||
String testNgAnnotationClass, String junitAnnotationClass, boolean requiresStaticMethod) {
|
||||
annotationMatcher = isType(testNgAnnotationClass);
|
||||
methodTreeMatcher = hasAnnotation(testNgAnnotationClass);
|
||||
this.junitAnnotationClass = junitAnnotationClass;
|
||||
this.requiresStaticMethod = requiresStaticMethod;
|
||||
}
|
||||
|
||||
static Optional<SetupTeardownType> matchType(MethodTree methodTree, VisitorState state) {
|
||||
return Arrays.stream(values())
|
||||
.filter(v -> v.methodTreeMatcher.matches(methodTree, state))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
Matcher<AnnotationTree> getAnnotationMatcher() {
|
||||
return annotationMatcher;
|
||||
}
|
||||
|
||||
String getJunitAnnotationClass() {
|
||||
return junitAnnotationClass;
|
||||
}
|
||||
|
||||
// XXX: Improve method name.
|
||||
boolean requiresStaticMethod() {
|
||||
return requiresStaticMethod;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains data for a {@code DataProvider} annotation for use in {@link TestNGJUnitMigration}.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract static class DataProviderMetadata {
|
||||
abstract MethodTree getMethodTree();
|
||||
|
||||
abstract String getName();
|
||||
|
||||
/**
|
||||
* Instantiate a new {@link DataProviderMetadata} instance.
|
||||
*
|
||||
* @param methodTree The value factory method tree.
|
||||
* @return A new {@link DataProviderMetadata} instance.
|
||||
*/
|
||||
public static DataProviderMetadata create(MethodTree methodTree) {
|
||||
return new AutoValue_TestNgMetadata_DataProviderMetadata(
|
||||
methodTree, methodTree.getName().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import tech.picnic.errorprone.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link AttributeMigrator} that migrates the {@code org.testng.annotations.Test#timeOut}
|
||||
* attribute. The TestNG {@code org.testng.annotations.Test#timeOut} attribute is always in
|
||||
* milliseconds, the JUnit variant {@code @Timeout} takes a value in seconds by default, hence we
|
||||
* add the {@link java.util.concurrent.TimeUnit#MILLISECONDS} attribute.
|
||||
*/
|
||||
@Immutable
|
||||
final class TimeOutAttributeMigrator implements AttributeMigrator {
|
||||
@Override
|
||||
public Optional<SuggestedFix> migrate(
|
||||
TestNgMetadata metadata,
|
||||
TestNgMetadata.AnnotationMetadata annotation,
|
||||
MethodTree methodTree,
|
||||
VisitorState state) {
|
||||
return Optional.ofNullable(annotation.getAttributes().get("timeOut"))
|
||||
.map(
|
||||
timeOut ->
|
||||
SuggestedFix.builder()
|
||||
.addImport("org.junit.jupiter.api.Timeout")
|
||||
.addStaticImport(TimeUnit.class.getCanonicalName() + ".MILLISECONDS")
|
||||
.prefixWith(
|
||||
methodTree,
|
||||
String.format(
|
||||
"@Timeout(value = %s, unit = MILLISECONDS)%n",
|
||||
SourceCode.treeToString(timeOut, state)))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link AttributeMigrator} that leaves a comment for attributes that aren't supported in the
|
||||
* migration.
|
||||
*/
|
||||
@Immutable
|
||||
final class UnsupportedAttributeMigrator {
|
||||
private UnsupportedAttributeMigrator() {}
|
||||
|
||||
static Optional<SuggestedFix> migrate(
|
||||
TestNgMetadata.AnnotationMetadata annotation,
|
||||
MethodTree methodTree,
|
||||
String attributeName,
|
||||
VisitorState state) {
|
||||
return Optional.ofNullable(annotation.getAttributes().get(attributeName))
|
||||
.map(
|
||||
value ->
|
||||
SuggestedFix.prefixWith(
|
||||
methodTree,
|
||||
String.format(
|
||||
"// XXX: Attribute `%s` is not supported, value: `%s`%n",
|
||||
attributeName, SourceCode.treeToString(value, state))));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/** TestNG to JUnit migration using Error-Prone. */
|
||||
@com.google.errorprone.annotations.CheckReturnValue
|
||||
@org.jspecify.annotations.NullMarked
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
@@ -0,0 +1,127 @@
|
||||
package tech.picnic.errorprone.testngjunit.refasterrules;
|
||||
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.testng.Assert;
|
||||
|
||||
/** Refaster rules to replace TestNG assertions with JUnit equivalents. */
|
||||
@SuppressWarnings("StaticImport")
|
||||
final class TestNgAssertionsToJUnitRules {
|
||||
private TestNgAssertionsToJUnitRules() {}
|
||||
|
||||
@SuppressWarnings("AssertEqual")
|
||||
static final class AssertEquals {
|
||||
@BeforeTemplate
|
||||
void before(Object expected, Object actual) {
|
||||
Assert.assertEquals(actual, expected);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object expected, Object actual) {
|
||||
Assertions.assertEquals(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("AssertEqualWithMessage")
|
||||
static final class AssertEqualsMessage {
|
||||
@BeforeTemplate
|
||||
void before(Object expected, Object actual, String message) {
|
||||
Assert.assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object expected, Object actual, String message) {
|
||||
Assertions.assertEquals(expected, actual, message);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("AssertUnequal")
|
||||
static final class AssertNotEquals {
|
||||
@BeforeTemplate
|
||||
void before(Object expected, Object actual) {
|
||||
Assert.assertNotEquals(actual, expected);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object expected, Object actual) {
|
||||
Assertions.assertNotEquals(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("AssertUnequalWithMessage")
|
||||
static final class AssertNotEqualsMessage {
|
||||
@BeforeTemplate
|
||||
void before(Object expected, Object actual, String message) {
|
||||
Assert.assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object expected, Object actual, String message) {
|
||||
Assertions.assertNotEquals(expected, actual, message);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"AssertFalse", "AssertThatIsFalse"})
|
||||
static final class AssertFalseCondition {
|
||||
@BeforeTemplate
|
||||
void before(boolean condition) {
|
||||
Assert.assertFalse(condition);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(boolean condition) {
|
||||
Assertions.assertFalse(condition);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"AssertFalseWithMessage", "AssertThatWithFailMessageStringIsFalse"})
|
||||
static final class AssertFalseConditionMessage {
|
||||
@BeforeTemplate
|
||||
void before(boolean condition, String message) {
|
||||
Assert.assertFalse(condition, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(boolean condition, String message) {
|
||||
Assertions.assertFalse(condition, message);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"AssertThatIsTrue", "AssertTrue"})
|
||||
static final class AssertTrueCondition {
|
||||
@BeforeTemplate
|
||||
void before(boolean condition) {
|
||||
Assert.assertTrue(condition);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(boolean condition) {
|
||||
Assertions.assertTrue(condition);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"AssertThatWithFailMessageStringIsTrue", "AssertTrueWithMessage"})
|
||||
static final class AssertTrueConditionMessage {
|
||||
@BeforeTemplate
|
||||
void before(boolean condition, String message) {
|
||||
Assert.assertTrue(condition, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(boolean condition, String message) {
|
||||
Assertions.assertTrue(condition, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/** Picnic Refaster rules. */
|
||||
@com.google.errorprone.annotations.CheckReturnValue
|
||||
@org.jspecify.annotations.NullMarked
|
||||
package tech.picnic.errorprone.testngjunit.refasterrules;
|
||||
@@ -0,0 +1,27 @@
|
||||
package tech.picnic.errorprone.util;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.sun.source.tree.Tree;
|
||||
|
||||
/**
|
||||
* A collection of Error Prone utility methods for dealing with the source code representation of
|
||||
* AST nodes.
|
||||
*/
|
||||
// XXX: This is a duplicate of `error-prone-contrib`s `SourceCode`, improve this.
|
||||
public final class SourceCode {
|
||||
private SourceCode() {}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the given {@link Tree}, preferring the original source code
|
||||
* (if available) over its prettified representation.
|
||||
*
|
||||
* @param tree The AST node of interest.
|
||||
* @param state A {@link VisitorState} describing the context in which the given {@link Tree} is
|
||||
* found.
|
||||
* @return A non-{@code null} string.
|
||||
*/
|
||||
public static String treeToString(Tree tree, VisitorState state) {
|
||||
String src = state.getSourceForNode(tree);
|
||||
return src != null ? src : tree.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,405 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TestNGJUnitMigrationTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(TestNGJUnitMigration.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"@Test",
|
||||
"public class A {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void classLevelAnnotation() {}",
|
||||
"",
|
||||
" public static void staticNotATest() {}",
|
||||
"",
|
||||
" private void notATest() {}",
|
||||
"",
|
||||
" @Test(description = \"bar\")",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void methodAnnotation() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" public static class B {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void nestedClass() {}",
|
||||
" }",
|
||||
"",
|
||||
" @Test(dataProvider = \"\")",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void dataProviderEmptyString() {}",
|
||||
"",
|
||||
" @Test(dataProvider = \"dataProviderTestCases\")",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void dataProvider(int foo) {}",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private static Object[][] dataProviderTestCases() {",
|
||||
" return new Object[][] {{1}, {2}};",
|
||||
" }",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private static Object[][] unusedDataProvider() {",
|
||||
" return new Object[][] {{1}, {2}};",
|
||||
" }",
|
||||
"",
|
||||
" @Test(dataProvider = \"notMigratableDataProviderTestCases\")",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void notMigratableDataProvider(int foo) {}",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private static Object[][] notMigratableDataProviderTestCases() {",
|
||||
" return Stream.of(1, 2, 3).map(i -> new Object[] {i}).toArray(Object[][]::new);",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void identificationConservativeMode() {
|
||||
CompilationTestHelper.newInstance(TestNGJUnitMigration.class, getClass())
|
||||
.setArgs("-XepOpt:TestNGJUnitMigration:ConservativeMode=true")
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"@Test",
|
||||
"public class A {",
|
||||
" public void classLevelAnnotation() {}",
|
||||
"",
|
||||
" @Test(description = \"bar\")",
|
||||
" public void methodAnnotation() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" public static class B {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void nestedClass() {}",
|
||||
" }",
|
||||
"",
|
||||
" @Test(dataProvider = \"notMigratableDataProviderTestCases\")",
|
||||
" public void notMigratableDataProvider(int foo) {}",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private static Object[][] notMigratableDataProviderTestCases() {",
|
||||
" return Stream.of(1, 2, 3).map(i -> new Object[] {i}).toArray(Object[][]::new);",
|
||||
" }",
|
||||
"}")
|
||||
.addSourceLines(
|
||||
"B.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"@Test",
|
||||
"public class B {",
|
||||
" public void classLevelAnnotation() {}",
|
||||
"",
|
||||
" @Test(description = \"bar\")",
|
||||
" public void methodAnnotation() {}",
|
||||
"",
|
||||
" @Test(testName = \"unsupportedAttribute\")",
|
||||
" public void unsupportedAttribute() {}",
|
||||
"",
|
||||
" @Test(testName = \"unsupportedAttribute\", suiteName = \"unsupportedAttribute\")",
|
||||
" public void multipleUnsupportedAttributes() {}",
|
||||
"}")
|
||||
.addSourceLines(
|
||||
"C.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"@Test",
|
||||
"public class C {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void classLevelAnnotation() {}",
|
||||
"",
|
||||
" @Test(description = \"bar\")",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void methodAnnotation() {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(TestNGJUnitMigration.class, getClass())
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import static org.testng.Assert.*;",
|
||||
"",
|
||||
"import org.testng.annotations.AfterClass;",
|
||||
"import org.testng.annotations.AfterMethod;",
|
||||
"import org.testng.annotations.AfterTest;",
|
||||
"import org.testng.annotations.BeforeClass;",
|
||||
"import org.testng.annotations.BeforeMethod;",
|
||||
"import org.testng.annotations.BeforeTest;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"@Test",
|
||||
"class A {",
|
||||
" @BeforeTest",
|
||||
" private void setupTest() {}",
|
||||
"",
|
||||
" @BeforeClass",
|
||||
" private void setupClass() {}",
|
||||
"",
|
||||
" @BeforeMethod",
|
||||
" private void setup() {}",
|
||||
"",
|
||||
" @AfterTest",
|
||||
" private void teardownTest() {}",
|
||||
"",
|
||||
" @AfterClass",
|
||||
" private void teardownClass() {}",
|
||||
"",
|
||||
" @AfterMethod",
|
||||
" private void teardown() {}",
|
||||
"",
|
||||
" public void classLevelAnnotation() {}",
|
||||
"",
|
||||
" @Test(priority = 1, timeOut = 500, description = \"unit\")",
|
||||
" public void priorityTimeOutAndDescription() {}",
|
||||
"",
|
||||
" @Test(testName = \"unsupportedAttribute\")",
|
||||
" public void unsupportedAttribute() {}",
|
||||
"",
|
||||
" @Test(testName = \"unsupportedAttribute\", suiteName = \"unsupportedAttribute\")",
|
||||
" public void multipleUnsupportedAttributes() {}",
|
||||
"",
|
||||
" @Test(dataProvider = \"dataProviderTestCases\")",
|
||||
" public void dataProvider(String string, int number) {}",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private Object[][] dataProviderTestCases() {",
|
||||
" int[] values = new int[] {1, 2, 3};",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", values[0], getClass()},",
|
||||
" {\"2\", values[1], this.getClass()},",
|
||||
" {\"3\", /* inline comment */ values[2], getClass()}",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @Test(dataProvider = \"dataProviderFieldReturnValueTestCases\")",
|
||||
" public void dataProviderFieldReturnValue(int foo, int bar) {}",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private Object[][] dataProviderFieldReturnValueTestCases() {",
|
||||
" Object[][] foo = new Object[][] {{1, 2}};",
|
||||
" return foo;",
|
||||
" }",
|
||||
"",
|
||||
" @Test(dataProvider = \"dataProviderThrowsTestCases\")",
|
||||
" public void dataProviderThrows(String foo, int bar) {}",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private Object[][] dataProviderThrowsTestCases() throws RuntimeException {",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", 0},",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @Test(expectedExceptions = RuntimeException.class)",
|
||||
" public void singleExpectedException() {",
|
||||
" throw new RuntimeException(\"foo\");",
|
||||
" }",
|
||||
"",
|
||||
" @Test(expectedExceptions = {RuntimeException.class})",
|
||||
" public void singleExpectedExceptionArray() {",
|
||||
" throw new RuntimeException(\"foo\");",
|
||||
" }",
|
||||
"",
|
||||
" @Test(expectedExceptions = {})",
|
||||
" public void emptyExpectedExceptions() {}",
|
||||
"",
|
||||
" @Test(expectedExceptions = {IllegalArgumentException.class, RuntimeException.class})",
|
||||
" public void multipleExpectedExceptions() {",
|
||||
" throw new RuntimeException(\"foo\");",
|
||||
" }",
|
||||
"",
|
||||
" @Test(enabled = false)",
|
||||
" public void disabledTest() {}",
|
||||
"",
|
||||
" @Test(enabled = true)",
|
||||
" public void enabledTest() {}",
|
||||
"",
|
||||
" @Test(groups = \"foo\")",
|
||||
" public void groupsTest() {}",
|
||||
"",
|
||||
" @Test(groups = {\"foo\", \"bar\"})",
|
||||
" public void multipleGroupsTest() {}",
|
||||
"",
|
||||
" @Test(groups = {})",
|
||||
" public void emptyGroupsTest() {}",
|
||||
"",
|
||||
" @Test(groups = \"\")",
|
||||
" public void invalidGroupsNameTest() {}",
|
||||
"",
|
||||
" @Test(groups = \" whitespace \")",
|
||||
" public void whitespaceGroupsNameTest() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static java.util.concurrent.TimeUnit.MILLISECONDS;",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.junit.jupiter.api.Disabled;",
|
||||
"import org.junit.jupiter.api.DisplayName;",
|
||||
"import org.junit.jupiter.api.MethodOrderer;",
|
||||
"import org.junit.jupiter.api.Order;",
|
||||
"import org.junit.jupiter.api.Tag;",
|
||||
"import org.junit.jupiter.api.TestMethodOrder;",
|
||||
"import org.junit.jupiter.api.Timeout;",
|
||||
"import org.junit.jupiter.params.ParameterizedTest;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.junit.jupiter.params.provider.MethodSource;",
|
||||
"import org.testng.annotations.AfterClass;",
|
||||
"import org.testng.annotations.AfterMethod;",
|
||||
"import org.testng.annotations.AfterTest;",
|
||||
"import org.testng.annotations.BeforeClass;",
|
||||
"import org.testng.annotations.BeforeMethod;",
|
||||
"import org.testng.annotations.BeforeTest;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"@TestMethodOrder(MethodOrderer.OrderAnnotation.class)",
|
||||
"class A {",
|
||||
" @org.junit.jupiter.api.BeforeEach",
|
||||
" private void setupTest() {}",
|
||||
"",
|
||||
" @org.junit.jupiter.api.BeforeAll",
|
||||
" private static void setupClass() {}",
|
||||
"",
|
||||
" @org.junit.jupiter.api.BeforeEach",
|
||||
" private void setup() {}",
|
||||
"",
|
||||
" @org.junit.jupiter.api.AfterEach",
|
||||
" private void teardownTest() {}",
|
||||
"",
|
||||
" @org.junit.jupiter.api.AfterAll",
|
||||
" private static void teardownClass() {}",
|
||||
"",
|
||||
" @org.junit.jupiter.api.AfterEach",
|
||||
" private void teardown() {}",
|
||||
"",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void classLevelAnnotation() {}",
|
||||
"",
|
||||
" @Order(1)",
|
||||
" @Timeout(value = 500, unit = MILLISECONDS)",
|
||||
" @DisplayName(\"unit\")",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void priorityTimeOutAndDescription() {}",
|
||||
"",
|
||||
" // XXX: Attribute `testName` is not supported, value: `\"unsupportedAttribute\"`",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void unsupportedAttribute() {}",
|
||||
"",
|
||||
" // XXX: Attribute `testName` is not supported, value: `\"unsupportedAttribute\"`",
|
||||
" // XXX: Attribute `suiteName` is not supported, value: `\"unsupportedAttribute\"`",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void multipleUnsupportedAttributes() {}",
|
||||
"",
|
||||
" @ParameterizedTest",
|
||||
" @MethodSource(\"dataProviderTestCases\")",
|
||||
" public void dataProvider(String string, int number) {}",
|
||||
"",
|
||||
" private static Stream<Arguments> dataProviderTestCases() {",
|
||||
" int[] values = new int[] {1, 2, 3};",
|
||||
" return Stream.of(",
|
||||
" arguments(\"1\", values[0], A.class),",
|
||||
" arguments(\"2\", values[1], A.class),",
|
||||
" arguments(\"3\", /* inline comment */ values[2], A.class));",
|
||||
" }",
|
||||
"",
|
||||
" // XXX: Attribute `dataProvider` is not supported, value:",
|
||||
" // `\"dataProviderFieldReturnValueTestCases\"`",
|
||||
"",
|
||||
" public void dataProviderFieldReturnValue(int foo, int bar) {}",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private Object[][] dataProviderFieldReturnValueTestCases() {",
|
||||
" Object[][] foo = new Object[][] {{1, 2}};",
|
||||
" return foo;",
|
||||
" }",
|
||||
"",
|
||||
" @ParameterizedTest",
|
||||
" @MethodSource(\"dataProviderThrowsTestCases\")",
|
||||
" public void dataProviderThrows(String foo, int bar) {}",
|
||||
"",
|
||||
" private static Stream<Arguments> dataProviderThrowsTestCases() throws RuntimeException {",
|
||||
" return Stream.of(arguments(\"1\", 0));",
|
||||
" }",
|
||||
"",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void singleExpectedException() {",
|
||||
" org.junit.jupiter.api.Assertions.assertThrows(",
|
||||
" RuntimeException.class,",
|
||||
" () -> {",
|
||||
" throw new RuntimeException(\"foo\");",
|
||||
" });",
|
||||
" }",
|
||||
"",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void singleExpectedExceptionArray() {",
|
||||
" org.junit.jupiter.api.Assertions.assertThrows(",
|
||||
" RuntimeException.class,",
|
||||
" () -> {",
|
||||
" throw new RuntimeException(\"foo\");",
|
||||
" });",
|
||||
" }",
|
||||
"",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void emptyExpectedExceptions() {}",
|
||||
"",
|
||||
" // XXX: Removed handling of `RuntimeException.class` because this migration doesn't support",
|
||||
" // XXX: multiple expected exceptions.",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void multipleExpectedExceptions() {",
|
||||
" org.junit.jupiter.api.Assertions.assertThrows(",
|
||||
" IllegalArgumentException.class,",
|
||||
" () -> {",
|
||||
" throw new RuntimeException(\"foo\");",
|
||||
" });",
|
||||
" }",
|
||||
"",
|
||||
" @Disabled",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void disabledTest() {}",
|
||||
"",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void enabledTest() {}",
|
||||
"",
|
||||
" @Tag(\"foo\")",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void groupsTest() {}",
|
||||
"",
|
||||
" @Tag(\"foo\")",
|
||||
" @Tag(\"bar\")",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void multipleGroupsTest() {}",
|
||||
"",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void emptyGroupsTest() {}",
|
||||
"",
|
||||
" // XXX: Attribute `groups` is not supported, value: `\"\"`",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void invalidGroupsNameTest() {}",
|
||||
"",
|
||||
" @Tag(\"whitespace\")",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void whitespaceGroupsNameTest() {}",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.AnnotationTreeMatcher;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TestNGMatchersTest {
|
||||
@Test
|
||||
void matches() {
|
||||
CompilationTestHelper.newInstance(TestNGMatchersTestChecker.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"// BUG: Diagnostic contains: TestNG annotation",
|
||||
"@Test",
|
||||
"class A {",
|
||||
" // BUG: Diagnostic contains: TestNG annotation",
|
||||
" @Test",
|
||||
" void basic() {}",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: TestNG annotation",
|
||||
" @Test(dataProvider = \"dataProviderTestCases\")",
|
||||
" void withDataProvider() {}",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" // BUG: Diagnostic contains: TestNG value factory method",
|
||||
" private static Object[][] dataProviderTestCases() {",
|
||||
" return new Object[][] {};",
|
||||
" }",
|
||||
"",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" void junitTest() {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
/** A {@link BugChecker} used to report TestNG annotations as errors for testing purposes. */
|
||||
@BugPattern(summary = "Interacts with `TestNGMatchers` for testing purposes", severity = ERROR)
|
||||
public static final class TestNGMatchersTestChecker extends BugChecker
|
||||
implements MethodTreeMatcher, AnnotationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Description matchAnnotation(AnnotationTree annotationTree, VisitorState visitorState) {
|
||||
return TestNGMatchers.TESTNG_TEST_ANNOTATION.matches(annotationTree, visitorState)
|
||||
? buildDescription(annotationTree).setMessage("TestNG annotation").build()
|
||||
: Description.NO_MATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchMethod(MethodTree methodTree, VisitorState visitorState) {
|
||||
return TestNGMatchers.TESTNG_VALUE_FACTORY_METHOD.matches(methodTree, visitorState)
|
||||
? buildDescription(methodTree).setMessage("TestNG value factory method").build()
|
||||
: Description.NO_MATCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
package tech.picnic.errorprone.testngjunit;
|
||||
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static java.util.function.Predicate.isEqual;
|
||||
import static java.util.function.Predicate.not;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.CompilationUnitTreeMatcher;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.CompilationUnitTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.Name;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tech.picnic.errorprone.testngjunit.TestNgMetadata.AnnotationMetadata;
|
||||
|
||||
final class TestNGScannerTest {
|
||||
@Test
|
||||
void classLevelAndMethodLevel() {
|
||||
CompilationTestHelper.newInstance(TestChecker.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"// BUG: Diagnostic contains: Class: A attributes: {}",
|
||||
"@Test",
|
||||
"class A {",
|
||||
"",
|
||||
" public void inferClassLevelAnnotation() {}",
|
||||
"",
|
||||
" void packagePrivateNotATest() {}",
|
||||
"",
|
||||
" private void privateNotATest() {}",
|
||||
"",
|
||||
" static void notATest() {}",
|
||||
"",
|
||||
" public static void staticNotATest() {}",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: Class: A attributes: {}",
|
||||
" @Test",
|
||||
" public void localAnnotation() {}",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: Class: A attributes: {description=\"foo\"}",
|
||||
" @Test(description = \"foo\")",
|
||||
" public void singleArgument() {}",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: Class: A attributes: {priority=1, description=\"foo\"}",
|
||||
" @Test(priority = 1, description = \"foo\")",
|
||||
" public void multipleArguments() {}",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: Class: A attributes: {dataProvider=\"dataProviderTestCases\"}",
|
||||
" @Test(dataProvider = \"dataProviderTestCases\")",
|
||||
" public void dataProvider() {}",
|
||||
"",
|
||||
" @SuppressWarnings(\"onlyMatchTestNGAnnotations\")",
|
||||
" // BUG: Diagnostic contains: Class: B attributes: {description=\"nested\"}",
|
||||
" @Test(description = \"nested\")",
|
||||
" class B {",
|
||||
" public void nestedTest() {}",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: Class: B attributes: {priority=1}",
|
||||
" @Test(priority = 1)",
|
||||
" public void nestedTestWithArguments() {}",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
// XXX: Here we need to add some edge cases for the DataProvider probably?
|
||||
@Test
|
||||
void dataProvider() {
|
||||
CompilationTestHelper.newInstance(TestChecker.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" // BUG: Diagnostic contains: Class: A DataProvider: dataProviderTestCases",
|
||||
" private static Object[][] dataProviderTestCases() {",
|
||||
" return new Object[][] {{1}, {2}};",
|
||||
" }",
|
||||
"",
|
||||
" private static Object[][] notMigratableDataProviderTestCases() {",
|
||||
" return Stream.of(1, 2, 3).map(i -> new Object[] {i}).toArray(Object[][]::new);",
|
||||
" }",
|
||||
"",
|
||||
" private static Object[][] notMigratableDataProvider2TestCases() {",
|
||||
" Object[][] testCases = new Object[][] {{1}, {2}};",
|
||||
" return testCases;",
|
||||
" }",
|
||||
"",
|
||||
" private static Object[] notMigratableDataProvider3TestCases() {",
|
||||
" return new Object[] {new Object[] {1}, new Object[] {2}};",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void junitTestClass() {
|
||||
CompilationTestHelper.newInstance(TestChecker.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test",
|
||||
" private void foo() {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void normalClass() {
|
||||
CompilationTestHelper.newInstance(TestChecker.class, getClass())
|
||||
.addSourceLines("A.java", "class A {", " private void foo() {}", "}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void teardownAndSetupMethods() {
|
||||
CompilationTestHelper.newInstance(TestChecker.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.AfterClass;",
|
||||
"import org.testng.annotations.AfterMethod;",
|
||||
"import org.testng.annotations.BeforeClass;",
|
||||
"import org.testng.annotations.BeforeMethod;",
|
||||
"",
|
||||
"class A {",
|
||||
" @BeforeClass",
|
||||
" // BUG: Diagnostic contains: Class: A SetupTearDown: BEFORE_CLASS",
|
||||
" private static void beforeClass() {}",
|
||||
"",
|
||||
" @BeforeMethod",
|
||||
" // BUG: Diagnostic contains: Class: A SetupTearDown: BEFORE_METHOD",
|
||||
" private void beforeMethod() {}",
|
||||
"",
|
||||
" @AfterClass",
|
||||
" // BUG: Diagnostic contains: Class: A SetupTearDown: AFTER_CLASS",
|
||||
" private static void afterClass() {}",
|
||||
"",
|
||||
" @AfterMethod",
|
||||
" // BUG: Diagnostic contains: Class: A SetupTearDown: AFTER_METHOD",
|
||||
" private void afterMethod() {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags classes with a diagnostics message that indicates, whether a
|
||||
* TestNG element was collected.
|
||||
*/
|
||||
@BugPattern(severity = ERROR, summary = "Interacts with `TestNGScanner` for testing purposes")
|
||||
public static final class TestChecker extends BugChecker
|
||||
implements CompilationUnitTreeMatcher, ClassTreeMatcher, MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
// XXX: find better way to do this
|
||||
private ImmutableMap<ClassTree, TestNgMetadata> classMetaData = ImmutableMap.of();
|
||||
|
||||
@Override
|
||||
public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) {
|
||||
TestNGScanner scanner = new TestNGScanner(state);
|
||||
classMetaData = scanner.collectMetadataForClasses(tree);
|
||||
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchClass(ClassTree tree, VisitorState state) {
|
||||
Optional.ofNullable(classMetaData.get(tree))
|
||||
.flatMap(TestNgMetadata::getClassLevelAnnotationMetadata)
|
||||
.ifPresent(annotation -> reportAnnotationMessage(tree, annotation, state));
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchMethod(MethodTree tree, VisitorState state) {
|
||||
ClassTree classTree = state.findEnclosing(ClassTree.class);
|
||||
Optional<TestNgMetadata> metadata = Optional.ofNullable(classTree).map(classMetaData::get);
|
||||
|
||||
if (metadata.isEmpty()) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
reportClassLevelAnnotation(classTree, metadata.orElseThrow(), state);
|
||||
reportTestMethods(tree, classTree, metadata.orElseThrow(), state);
|
||||
reportDataProviderMethods(tree, classTree, metadata.orElseThrow(), state);
|
||||
reportSetupTeardownMethods(tree, classTree, metadata.orElseThrow(), state);
|
||||
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
private void reportClassLevelAnnotation(
|
||||
ClassTree classTree, TestNgMetadata metadata, VisitorState state) {
|
||||
metadata
|
||||
.getClassLevelAnnotationMetadata()
|
||||
.ifPresent(annotation -> reportAnnotationMessage(classTree, annotation, state));
|
||||
}
|
||||
|
||||
private void reportTestMethods(
|
||||
MethodTree tree, ClassTree classTree, TestNgMetadata metadata, VisitorState state) {
|
||||
metadata
|
||||
.getClassLevelAnnotationMetadata()
|
||||
.filter(not(isEqual(metadata.getMethodAnnotations().get(tree))))
|
||||
.map(unused -> metadata.getMethodAnnotations().get(tree))
|
||||
.ifPresent(annotation -> reportAnnotationMessage(classTree, annotation, state));
|
||||
}
|
||||
|
||||
private void reportSetupTeardownMethods(
|
||||
MethodTree tree, ClassTree classTree, TestNgMetadata metadata, VisitorState state) {
|
||||
metadata.getSetupTeardown().entrySet().stream()
|
||||
.filter(entry -> entry.getKey().equals(tree))
|
||||
.findFirst()
|
||||
.ifPresent(
|
||||
entry ->
|
||||
reportMethodMessage(
|
||||
classTree.getSimpleName(),
|
||||
"SetupTearDown",
|
||||
entry.getValue().name(),
|
||||
entry.getKey(),
|
||||
state));
|
||||
}
|
||||
|
||||
private void reportDataProviderMethods(
|
||||
MethodTree tree, ClassTree classTree, TestNgMetadata metadata, VisitorState state) {
|
||||
metadata.getDataProviderMetadata().entrySet().stream()
|
||||
.filter(entry -> entry.getValue().getMethodTree().equals(tree))
|
||||
.findFirst()
|
||||
.map(Map.Entry::getValue)
|
||||
.ifPresent(
|
||||
dataProvider -> {
|
||||
reportMethodMessage(
|
||||
classTree.getSimpleName(),
|
||||
"DataProvider",
|
||||
dataProvider.getName(),
|
||||
dataProvider.getMethodTree(),
|
||||
state);
|
||||
});
|
||||
}
|
||||
|
||||
private void reportAnnotationMessage(
|
||||
ClassTree classTree, AnnotationMetadata annotation, VisitorState state) {
|
||||
state.reportMatch(
|
||||
buildDescription(annotation.getAnnotationTree())
|
||||
.setMessage(createMetaDataMessage(classTree, annotation))
|
||||
.build());
|
||||
}
|
||||
|
||||
private void reportMethodMessage(
|
||||
Name className, String message, String name, Tree tree, VisitorState state) {
|
||||
state.reportMatch(
|
||||
buildDescription(tree)
|
||||
.setMessage(String.format("Class: %s %s: %s", className, message, name))
|
||||
.build());
|
||||
}
|
||||
|
||||
private static String createMetaDataMessage(
|
||||
ClassTree classTree, AnnotationMetadata annotationMetadata) {
|
||||
return String.format(
|
||||
"Class: %s attributes: %s",
|
||||
classTree.getSimpleName(), annotationMetadata.getAttributes());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
releases:
|
||||
- version: 0.16.1
|
||||
compatible:
|
||||
- "2.27.1"
|
||||
- "2.27.0"
|
||||
- "2.26.1"
|
||||
- "2.26.0"
|
||||
- "2.25.0"
|
||||
@@ -11,6 +13,8 @@ releases:
|
||||
- "2.23.0"
|
||||
- version: 0.16.0
|
||||
compatible:
|
||||
- "2.27.1"
|
||||
- "2.27.0"
|
||||
- "2.26.1"
|
||||
- "2.26.0"
|
||||
- "2.25.0"
|
||||
@@ -19,6 +23,8 @@ releases:
|
||||
- "2.23.0"
|
||||
- version: 0.15.0
|
||||
compatible:
|
||||
- "2.27.1"
|
||||
- "2.27.0"
|
||||
- "2.26.1"
|
||||
- "2.26.0"
|
||||
- "2.25.0"
|
||||
@@ -27,6 +33,8 @@ releases:
|
||||
- "2.23.0"
|
||||
- version: 0.14.0
|
||||
compatible:
|
||||
- "2.27.1"
|
||||
- "2.27.0"
|
||||
- "2.26.1"
|
||||
- "2.26.0"
|
||||
- "2.25.0"
|
||||
|
||||
Reference in New Issue
Block a user