Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Checkstyle integration test #865

Merged
merged 4 commits into from
Oct 30, 2023
Merged

Conversation

Stephan202
Copy link
Member

@Stephan202 Stephan202 commented Oct 28, 2023

Suggested commit message:

Introduce Checkstyle integration test (#865)

The new `integration-tests/checkstyle-10.12.4.sh` script can be executed
locally or by adding a comment containing the string `/integration-test`
to a pull request.

The integration test comprises a shell script that:
1. Checks out a Checkstyle release tag.
2. Applies a small patch to enable Error Prone Support.
3. Runs the build in Error Prone patch mode, until no further changes
   are applied.
4. Validates that:
   - The build (including tests) still passes.
   - The set of changes matches a pre-recorded diff.
   - The set of Error Prone Support-emitted warnings matches a
     pre-recorded list.

The script also has a `--sync` mode using which the expected diff and 
warnings can be updated. Combined, the script makes it easy to assess
and track the impact of changes to Error Prone Support in relation to
the Checkstyle code base.

Over time this setup will be generalized to include integration tests
against other code bases.

@Stephan202 Stephan202 added this to the 0.15.0 milestone Oct 28, 2023
@github-actions
Copy link

Looks good. No mutations were possible for these changes.
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@rickie rickie self-requested a review October 28, 2023 15:03
Copy link
Member Author

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some context. Testing the GitHub action integration is TBD (I expect issues with the git commit command). Might also want to run ShellCheck.

Comment on lines 1 to 31
# If requested by means of a pull request comment, runs integration tests
# against the project, using the code found on the pull request branch.
# XXX: Generalize this to a matrix build of multiple integration tests,
# possibly using multiple JDK or OS versions.
name: "Integration tests"
on:
issue_comment:
types: [ created ]
permissions:
contents: read
jobs:
run-integration-tests:
name: On-demand integration test
if: |
github.event.issue.pull_request && contains(github.event.comment.body, '/integration-test')
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
with:
java-version: 17.0.8
distribution: temurin
cache: maven
- name: Install project to local Maven repository
run: mvn -T1C install -DskipTests -Dverification.skip
- name: Run integration test
run: ./integration-tests/checkstyle-10.12.4.sh
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The contents in this file are an adaptation of work by @oxkitsune; tnx!

+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>${assertj.version}</version>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version is indeed always supplied externally. The reasoning behind this decision is that it is likely to minimize maintenance impact; ideally we don't need to frequently modify initial patch files such as this one.

Comment on lines +96 to +93
+ // XXX: Don't reorder arguments to `picocli.CommandLine.Option#names`.
+ @SuppressWarnings("LexicographicalAnnotationAttributeListing")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should file a separate improvement PR for this.

Comment on lines +119 to +118
+ // XXX: File PR to replace `.collect(toSet())` with `.collect(toCollection(HashSet::new))`.
+ // XXX: Update `CollectorMutability` to recognize cases such as this one, where the created
+ // collection is clearly modified.
+ @SuppressWarnings("CollectorMutability")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any takers? :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oxkitsune are you interested?

Comment on lines +154 to +151
+ // XXX: Don't suggest `ImmutableSet.of(elem)` for nullable `elem`.
+ @SuppressWarnings("ImmutableSetOf1")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I once started a branch to enable this sort of thing, but hit some roadblocks. To be reviewed.

test_name="$(basename "${0}" .sh)"
project=checkstyle
[email protected]:checkstyle/checkstyle.git
revision=checkstyle-10.12.4
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we can also convince Renovate to track new tags. TBD whether that's worth it.

Comment on lines 59 to 86
# Make sure that the targeted tag of the project's Git repository is checked
# out.
project_root="${repos_root}/${project}"
if [ ! -d "${project_root}" ]; then
# The repository has not yet been cloned; create a shallow clone.
git clone --branch "${revision}" --depth 1 "${repository}" "${project_root}"
else
# The repository does already appear to exist. Try to check out the requested
# tag if possible, and fetch it otherwise.
#
# Under certain circumstances this does not cause the relevant tag to be
# created, so if necessary we manually create it.
git -C "${project_root}" checkout --force "${revision}" 2>/dev/null \
|| (
git -C "${project_root}" fetch --depth 1 "${repository}" "${revision}" \
&& git -C "${project_root}" checkout --force FETCH_HEAD \
&& git -C "${project_root}" tag "${revision}" || true
)
fi
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On GitHub Actions there'll always be a new checkout, but for local development it's very convenient if repeated script runs don't trigger new fetch/clone operations.

Comment on lines +102 to +129
if ! git diff --exit-code; then
git commit -m 'minor: Apply patches' .

# Changes were applied, so another compilation round may apply yet more
# changes. For performance reasons we perform incremental compilation,
# enabled using a misleading flag. (See
# https://issues.apache.org/jira/browse/MCOMPILER-209 for details.)
apply_patch '-Dmaven.compiler.useIncrementalCompilation=false'
elif [ "${extra_build_args}" != 'clean' ]; then
# No changes were applied. We'll attempt one more round in which all files
# are recompiled, because there are cases in which violations are missed
# during incremental compilation.
apply_patch 'clean'
fi
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is a bit awkward, but the added complexity seems worth the reduced runtime, at least for a build as large as this one. (Current runtime on my machine is ~11 minutes. Don't recall how long it is without this tweak; might do a new timing later.)


# Perist or validate the warnings reported by Error Prone Support checks.
baseline_warnings="${integration_test_root}/${test_name}-expected-warnings.txt"
generated_warnings="$((grep -oP "(?<=^\\Q[WARNING] ${PWD}/\\E).*" "${validation_log_file}" | grep -P '\] \[' || true) | sort)"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and elsewhere, the use of grep -P likely doesn't make this script Macos-compatible; that's something to review separately.

@Stephan202
Copy link
Member Author

Alright, probably the first of quite a lot of trial-and-error: /integration-test.

@Stephan202
Copy link
Member Author

Ah right, this might not work, given that the PR isn't merged yet. Will try something else later.

@Stephan202
Copy link
Member Author

I temporarily changed the default branch to be this PR's branch. So, take two: /integration-test

@Stephan202
Copy link
Member Author

That worked. I restored the default branch.

@Stephan202 Stephan202 force-pushed the sschroevers/integration-tests branch from 8757913 to 96bcb98 Compare October 29, 2023 10:06
@Stephan202
Copy link
Member Author

Validated in this build that artifacts are attached on build failure.

The new `integration-tests/checkstyle-10.12.4.sh` script can be executed
locally or by adding a comment containing the string `/integration-test`
to a pull request.

The integration test comprises a shell script that:
1. Checks out a Checkstyle release tag.
2. Applies a small patch to enable Error Prone Support.
3. Runs the build in Error Prone patch mode, until no further changes
   are applied.
4. Validates that:
   - The build (including tests) still passes.
   - The set of changes matches a pre-recorded diff.
   - The set of Error Prone Support-emitted warnings matches a
     pre-recorded list.

The script also has a `--sync` mode using which the expected diff and
warnings can be updated. Combined, the script makes it easy to assess
and track the impact of changes to Error Prone Support in relation to
the Checkstyle code base.

Over time this setup will be generalized to include integration tests
against other code bases.
@Stephan202 Stephan202 force-pushed the sschroevers/integration-tests branch from 2e3099a to ba0d08e Compare October 29, 2023 14:59
@github-actions
Copy link

Looks good. No mutations were possible for these changes.
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@Stephan202 Stephan202 marked this pull request as ready for review October 29, 2023 15:03
@Stephan202 Stephan202 requested a review from oxkitsune October 29, 2023 15:03
@Stephan202
Copy link
Member Author

Alright, I think this PR is ready for review. Once merged we can start working on adding additional projects (something @oxkitsune has already worked on).

@github-actions
Copy link

Looks good. No mutations were possible for these changes.
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Copy link
Member

@rickie rickie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a small commit.

How will the structure in integration-tests look like when we add more? And more versions? Do we want a directory per library? I see you changed some names and based on that I kind of assume you have already something in mind. Happy to think along, but for now going to call it a day :).

# required to run Error Prone with Error Prone Support and (b) formatting the
# code using the same method by which it will be formatted after each
# compilation round. The initial formatting operation ensures that subsequent
# moifications can be rendered in a clean manner.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# moifications can be rendered in a clean manner.
# modifications can be rendered in a clean manner.


pushd "${project_root}"

# Make sure that Git is sufficient configured to enable committing to the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Make sure that Git is sufficient configured to enable committing to the
# Make sure that Git is sufficiently configured to enable committing to the

Right?

# XXX: This "diff of diffs" also contains vacuous sections, introduced due to
# line offset differences. Try to omit those from the final output.
if ! diff -u "${expected_changes}" "${actual_changes}"; then
echo 'There are unexpected changes.'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be great to output that to a file and upload that? I'd say that is of great value.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That already happens; see the actions/upload-artifact goal. (And this comment.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant the "diff of the diffs". IIUC only the actual changes, actual logs and actual warnings are logged. While in cases of new changes you really want to see what the diff of the diffs is and inspect that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That diff-of-diffs is in the build log, and also easy to reproduce. Not opposed to adding this, but perhaps something for another PR. (I can't justify spending time on this right now.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine with deferring but I think its nice to add :).

# moifications can be rendered in a clean manner.
git clean -fdx
git apply < "${integration_test_root}/${test_name}-init.patch"
git commit -m 'dependency: Introduce Error Prone Support' .
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we are adhering to the guidelines of Checkstyle here, which makes me wonder, how do we want to generalize this script for the next runs? The setup is nice and seems to be written with a generalization in mind.

The reason I'm asking is that I started wondering if we always want to use the minor and dependency in the commit messages.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely we'll want to make some compromises. Either by disabling additional checks or by making this parametric. While I already spent significant time on making the script reusable, there is certainly more to do. That's why the file is simply named integration-tests/checkstyle-10.12.4.sh ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that makes sense, sounds good for now :).

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

@rickie
Copy link
Member

rickie commented Oct 29, 2023

Forgot to say something important; man this setup is amazing, cool stuff ! ✨ 💖 This will be so useful!

Copy link
Member Author

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will the structure in integration-tests look like when we add more? And more versions? Do we want a directory per library? I see you changed some names and based on that I kind of assume you have already something in mind. Happy to think along, but for now going to call it a day :).

Good questions. I'm undecided about the value of tracking multiple versions per library, so perhaps dropping the versions from the file names make sense. Something to decide once we wish to update the tag against which we tag. W.r.t. whether to introduce subdirectories: also not sure; the current shared prefixes may suffice. The biggest thing to figure out is how to maximally reuse test script logic. I guess we'll figure it out as we go.

# moifications can be rendered in a clean manner.
git clean -fdx
git apply < "${integration_test_root}/${test_name}-init.patch"
git commit -m 'dependency: Introduce Error Prone Support' .
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely we'll want to make some compromises. Either by disabling additional checks or by making this parametric. While I already spent significant time on making the script reusable, there is certainly more to do. That's why the file is simply named integration-tests/checkstyle-10.12.4.sh ;)

# XXX: This "diff of diffs" also contains vacuous sections, introduced due to
# line offset differences. Try to omit those from the final output.
if ! diff -u "${expected_changes}" "${actual_changes}"; then
echo 'There are unexpected changes.'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That already happens; see the actions/upload-artifact goal. (And this comment.)

+ "reported to standard out in plain format. Checkstyle requires a configuration "
+ "XML file that configures the checks to apply.",
mixinStandardHelpOptions = true)
+ // XXX: Don't reorder arguments to `picocli.CommandLine.Option#names`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or I don't get this sentence or it should be "from" instead of "to" 😋.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One specifies arguments to a function. (In this case it's an annotation attribute, but still.)

Comment on lines +119 to +118
+ // XXX: File PR to replace `.collect(toSet())` with `.collect(toCollection(HashSet::new))`.
+ // XXX: Update `CollectorMutability` to recognize cases such as this one, where the created
+ // collection is clearly modified.
+ @SuppressWarnings("CollectorMutability")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oxkitsune are you interested?

Copy link
Member

@rickie rickie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll take a while before I'll understand all specifics of the bash script, but overall it looks good to me and I'm looking forward to start using this!

@rickie
Copy link
Member

rickie commented Oct 30, 2023

I'd say we can use "new feature" for this, or add a new label "integration-tests"/"integration-tests-framework"? I can file a PR such that it will get it's own section in the release notes.

@Stephan202
Copy link
Member Author

Good point. This is primarily relevant for contributors, with as a secondary benefit that hopefully the average quality of releases will go up. Perhaps the existing improvement label suffices?

@rickie
Copy link
Member

rickie commented Oct 30, 2023

Fine by me, I just imagine that we will have quite some PRs related to this test framework so it could be convenient to have a label for it. Having it under improvement also works for now.

Copy link
Contributor

@oxkitsune oxkitsune left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!
On the topic of making it more generic for other repos, I think we can extract a large part of the script to a generic run-integration-tests.sh, and have the more repo specific stuff in a <repo>-<tag>.sh script 😄

@rickie rickie merged commit e8d1499 into master Oct 30, 2023
17 checks passed
@rickie rickie deleted the sschroevers/integration-tests branch October 30, 2023 09:34
@Stephan202 Stephan202 added chore A task not related to code (build, formatting, process, ...) and removed improvement labels Dec 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
chore A task not related to code (build, formatting, process, ...)
Development

Successfully merging this pull request may close these issues.

3 participants