Add mutation testing with PIT to CI/CD pipeline#22
Merged
bernardladenthin merged 13 commits intomasterfrom Apr 8, 2026
Merged
Conversation
- Add dedicated `mutation-testing` job to the workflow that runs `pitest:mutationCoverage` on JDK 11 (once, separate from the multi-JDK compatibility matrix) - Set `<mutationThreshold>100</mutationThreshold>` in pom.xml so the build fails if any mutant survives https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
The pitest goal invoked directly does not trigger the Maven lifecycle, so test classes were not compiled in the clean CI environment. Prepending the test-compile phase ensures classes exist before PIT runs. https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
Always upload target/pit-reports/ after the mutation-testing job, even when the build fails due to surviving mutants, so the report is available for inspection in the GitHub Actions run. https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
- getBufferSize_twoEntriesWithMaxBufferElementsOne_trimCalled: kills the isTrimShouldBeExecuted boundary mutation (>= 2 → > 2) by exercising exactly buffer.size()==2 with maxBufferElements==1 - read_multipleBytesSingleEntryOpenStream_returnsAllRequestedBytes: kills the tryWaitForEnoughBytes return-value mutation (return 0) by verifying read(buf,0,N) returns N when data is already present - available_afterPartialReadFromSingleEntry_returnsRemainingCount: kills the availableBytes subtraction mutation (+= instead of -=) by checking available() after a partial read of a single entry https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
available() has an equivalent ConditionalsBoundary mutation (> MAX_VALUE vs >= MAX_VALUE both return Integer.MAX_VALUE when availableBytes == MAX_VALUE), so exclude the method entirely. Add read_exactFullEntryConsumption_availableAndBufferSizeAreZero to cover the if-branch case where missingBytes == maximumBytesToCopy: - getBufferSize()==0 catches the ConditionalsBoundary mutant that routes to the else-branch and leaves the entry unreleased - available()==0 catches the MathMutator that flips availableBytes from -= to += in the if-branch https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
…scope Three mutations cannot be killed with the current tooling: 1. isTrimShouldBeExecuted — ConditionalsBoundary on buffer.size() >= 2 is observable via getBufferSize(), but PIT 1.6.4 does not link coverage from SBOutputStream.write() through the outer-class trim() call chain to this private helper, so the targeting test is never run against the mutation. Excluded via <excludedMethods>. 2-3. read(byte[],int,int) — two MathMutator mutations on the local variable maximumAvailableBytes (one in the if-branch, one in the else-branch) are provably equivalent: the variable is only decremented inside the loop and is never read again for any decision after the pre-loop capping check. Excluding the entire read() method would remove too many valid mutations, so the threshold is set to 97% to account for these two only. https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
Excluding methods removes all their mutations from scope, including valid ones that should be covered — not acceptable. The 3 surviving mutations are equivalent and cannot be killed: - available(): ConditionalsBoundary (> vs >= MAX_VALUE, same result) - read(): two MathMutator on maximumAvailableBytes, a local variable that is only written, never re-read, inside the synchronized loop All other mutations remain in scope and fully tested. https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
…100% threshold Three equivalent/uncatchable mutations are fixed by extraction: 1. available() ConditionalsBoundary (> vs >= MAX_VALUE): Replaced the conditional with Math.min in a new clampToMaxInt(long) helper — no comparison operator means no CB mutation is generated. Direct tests cover the PrimitiveReturnsMutator variants. 2. isTrimShouldBeExecuted() ConditionalsBoundary (>= 2 vs > 2): Made package-private so a direct test can call it after manually setting up buffer.size()==2 with maxBufferElements==1, bypassing the PIT coverage-tracking gap across the inner-class call chain. 3. maximumAvailableBytes (x2 MathMutator in read if/else branches): Both -= operations extracted into decrementAvailableBytesBudget(). PIT now generates one testable mutation on the method body instead of two equivalent mutations on a local variable that is never re-read after the pre-loop capping check. All three extracted methods are package-private instance methods (not static) to remain mockable/spyable. https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
…ith Math.min The `if (maximumAvailableBytes < missingBytes)` guard was equivalent under the ConditionalsBoundaryMutator: mutating `<` to `<=` produces identical behavior when both values are equal (the if-body assigns the same value). Replacing with an unconditional Math.min removes the comparison entirely, leaving no mutation target. https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
… coveralls - jacoco-maven-plugin: 0.8.6 → 0.8.14 - pitest-maven: 1.6.4 → 1.23.0 - coveralls-maven-plugin: add jaxb-api 2.3.1 dependency (required on Java 9+ since JAXB was removed from the JDK) https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
Replaces the unmaintained org.eluder.coveralls:coveralls-maven-plugin 4.3.0 with the actively maintained com.github.hazendaz.maven fork. The jaxb-api workaround dependency is no longer needed as the new version handles Java 9+ natively. https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
- hamcrest-all 1.3 → hamcrest 3.0 (new consolidated artifact) - maven-jar-plugin 3.2.0 → 3.5.0 - maven-source-plugin 3.2.1 → 3.4.0 - maven-javadoc-plugin 3.2.0 → 3.12.0 - maven-gpg-plugin 1.6 → 3.2.8 https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds mutation testing capabilities to the CI/CD pipeline using PIT (Pitest), a mutation testing tool for Java. It introduces a new GitHub Actions workflow job and configures PIT with a 100% mutation threshold requirement.
Key Changes
GitHub Actions Workflow: Added a new
mutation-testingjob to.github/workflows/maven.ymlthat:mvn org.pitest:pitest-maven:mutationCoveragePIT Configuration: Updated
pom.xmlto set amutationThresholdof 100%, ensuring that all mutations must be killed by the test suiteImplementation Details
https://claude.ai/code/session_01WmTfpPLDcyP2M69wtGnUUC