Skip to content

Add fresh code quality gates for coverage and mutation testing#656

Open
andrii0lomakin wants to merge 5 commits intodevelopfrom
fresh-code-gate
Open

Add fresh code quality gates for coverage and mutation testing#656
andrii0lomakin wants to merge 5 commits intodevelopfrom
fresh-code-gate

Conversation

@andrii0lomakin
Copy link
Collaborator

@andrii0lomakin andrii0lomakin commented Feb 16, 2026

Motivation:

The project relied on Qodana for code coverage tracking, which only provided visibility but no enforcement. This PR introduces automated quality gates that ensure new code meets coverage and mutation testing standards before merging, with higher thresholds for AI-assisted code. It also consolidates all CI/CD documentation into a dedicated docs/ directory.

Changes:

1. Qodana coverage tracking removed

  • Removed coverage data download and CoverageReport exclusion from Qodana job
  • Qodana now runs as a pure static analysis tool
  • JaCoCo data collection and PR coverage report retained (renamed output dir from .qodana/code-coverage/ to .coverage/reports/)

2. Code coverage gate (new CI job: coverage-gate)

  • Line coverage enforced via diff-cover on new/changed lines only
  • Branch coverage enforced via custom Python script (.github/scripts/check-branch-coverage.py) that parses JaCoCo XML line-level branch data
  • Threshold: 85% if any commit is co-authored with Claude Code, 70% otherwise
  • Both unit test and integration test JaCoCo reports are merged during analysis

3. Mutation testing gate (new CI job: mutation-testing)

  • PIT (Pitest) v1.22.1 configured via mutation-testing Maven profile
  • Only mutates changed production classes (computed via git diff)
  • 85% mutation score threshold — build fails if below
  • Uses both unit tests and Ekstazi-selected integration tests to kill mutations
  • Ekstazi cache restored from base branch (develop) so PR changes are seen as "new"
  • Generated code (SQL parser, GQL parser) excluded from mutation analysis

4. CLAUDE.md guideline

  • Added test coverage requirements section: prefer existing test classes, target 85% line and branch coverage

5. Documentation consolidated in docs/

  • Moved ci-cd-diagram.md and testflows-runner-setup.md from .github/workflows/ to docs/
  • Updated CI/CD diagram with new quality gate jobs (coverage-gate, mutation-testing)
  • Updated Qodana description (static analysis only, no coverage tracking)
  • Updated test matrix description (temurin, oracle on self-hosted Hetzner runners)
  • Added PR Quality Gates summary table
  • Created docs/README.md as TOC linking all documentation
  • Created docs/test-quality-requirements.md covering coverage thresholds, mutation testing, test writing guidelines, and CI enforcement summary

🤖 Generated with Claude Code

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @andrii0lomakin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the project's code quality assurance by implementing automated and enforced quality gates within the CI pipeline. It shifts from passive coverage monitoring to active enforcement of both code coverage and mutation testing standards for new and modified code, including specific thresholds for AI-assisted contributions. The changes are complemented by clear documentation to guide developers on these new requirements and processes.

Highlights

  • Code Coverage Gates: Introduced a new CI job, coverage-gate, to enforce line and branch coverage on new or changed lines. Thresholds are 85% for AI-assisted code and 70% otherwise, utilizing diff-cover for line coverage and a custom Python script for branch coverage.
  • Mutation Testing Gate: Added a new CI job, mutation-testing, which uses PIT (Pitest) to enforce an 85% mutation score on changed production classes. It integrates with Ekstazi for efficient integration test selection.
  • Qodana Integration Update: Removed Qodana's responsibility for coverage tracking, allowing it to function purely as a static analysis tool. JaCoCo reports are now stored in a dedicated .coverage/reports/ directory.
  • Documentation and Guidelines: Updated CLAUDE.md with test coverage requirements and created a new comprehensive documentation file, docs/test-quality-requirements.md, detailing all new quality gates, thresholds, and test writing guidelines.
Changelog
  • .github/scripts/check-branch-coverage.py
    • Added a new Python script to calculate branch coverage specifically for new or changed lines by parsing JaCoCo XML reports.
  • .gitignore
    • Updated the ignore list to include the newly designated .coverage/ directory for coverage reports.
  • CLAUDE.md
    • Appended a new section detailing test coverage requirements, emphasizing the preference for existing test classes and target coverage percentages for new code.
  • docs/README.md
    • Created a new documentation directory and an initial README.md to serve as a table of contents for project documentation.
  • docs/test-quality-requirements.md
    • Added a comprehensive document outlining the project's test quality requirements, including detailed explanations of code coverage thresholds, mutation testing (PIT, Ekstazi), test writing guidelines, and CI enforcement.
  • pom.xml
    • Integrated the Pitest Maven plugin (v1.22.1) into the project's build configuration.
    • Modified JaCoCo plugin configurations to output coverage reports to the new .coverage/reports/ directory.
    • Introduced a new Maven profile named mutation-testing to configure Pitest with specific parameters such as mutation threshold, thread count, timeouts, and exclusions for generated code.
  • qodana.yaml
    • Removed the CoverageReport exclusion from the Qodana configuration, indicating that Qodana will no longer be responsible for coverage reporting.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/maven-pipeline.yml
Activity
  • The pull request was created by andrii0lomakin.
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces significant improvements to the project's quality gates by adding automated checks for code coverage and mutation testing. The changes are well-structured, including new CI jobs, updates to the build configuration, and comprehensive documentation. My review focuses on the new Python script for branch coverage checking. I've provided one suggestion to optimize its performance by improving the file matching logic. Overall, this is a valuable addition to ensure code quality.

@github-actions
Copy link

Coverage Report

Overall Project 55.92% 🍏

There is no coverage information present for the Files changed

@github-actions
Copy link

github-actions bot commented Feb 16, 2026

Qodana for JVM

37 new problems were found

Inspection name Severity Problems
Spelling ◽️ Notice 37

💡 Qodana analysis was run in the pull request mode: only the changed files were checked
☁️ View the detailed Qodana report

Contact Qodana team

Contact us at qodana-support@jetbrains.com

Comment on lines 299 to 309
- name: Run Ekstazi Selection for Integration Tests
if: steps.changes.outputs.skip != 'true'
id: ekstazi
run: |
# Run Ekstazi select on already-compiled classes to generate exclude files.
# The Ekstazi profile skips compilation (classes already exist from previous step).
# The restored cache is from the base branch (develop), so Ekstazi sees the
# PR's changes as "new" and selects only the affected integration tests.
./mvnw -s .github/workflows/settings.xml \
-pl ${{ steps.changes.outputs.modules }} \
process-test-classes -P ekstazi -B || true
Copy link
Collaborator

@lesley29 lesley29 Feb 17, 2026

Choose a reason for hiding this comment

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

I took a closer look on Ekstazi and here are a few thoughts:

Also (based on the docs), it looks like we need to pass a couple extra flags to make it work:

  • -Djava.security.manager=allow is missing everywhere (required for JDK 21+, which we use)
  • -Djdk.attach.allowAttachSelf=true is missing in the mutation-testing job

There are unreleased fixes on master (ASM 9.6, SecurityManager removal), but nothing published since 5.3.0. Given we have

|| true

I suspect it might be failing silently. Have you checked this?

Given all that, IMO, PIT alone (scoped to changed classes) would likely be enough, especially since we already limit mutation testing to pr-changed code. WDYT?

PS: just want to make sure this dependency is justified

Copy link
Collaborator Author

@andrii0lomakin andrii0lomakin Feb 17, 2026

Choose a reason for hiding this comment

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

Hi, @lesley29
Could you elaborate more on how Ekstazi and PIT are connected? They there for a different purposes in this change. Without Ekstazi, we run a full set of IT tests, which will take hours, while with Ekstazi, only single tests from this suite are running.

Copy link
Collaborator Author

@andrii0lomakin andrii0lomakin Feb 17, 2026

Choose a reason for hiding this comment

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

|| true

@lesley29 I have already used it on several tests, if integration tests are affected they triggered, and then next task
EnricoMi/publish-unit-test-result-action@v2
Reports failure.

It can fall silently, that is for sure,for example, when the Ekstazi cache is absent, but that is the reason for this safeguard.

Copy link
Collaborator Author

@andrii0lomakin andrii0lomakin Feb 17, 2026

Choose a reason for hiding this comment

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

@lesley29

java.security.manager

It is NOP in JDK 21, so not needed.

Copy link
Collaborator Author

@andrii0lomakin andrii0lomakin Feb 17, 2026

Choose a reason for hiding this comment

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

@lesley29

-Djdk.attach.allowAttachSelf=true is missing in the mutation-testing job

It is not needed because PIT does not attach itself at runtime. It is already attached as part of the forked process. Could you elaborate more where you found this requirement in PIT plugin ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hi, @lesley29 Could you elaborate more on how Ekstazi and PIT are connected? They there for a different purposes in this change. Without Ekstazi, we run a full set of IT tests, which will take hours, while with Ekstazi, only single tests from this suite are running.

I probably understood, you, PIT will mutate, only changed classes, but it will not run only tests that are related to those classes, it will run the full test suite.

Copy link
Collaborator Author

@andrii0lomakin andrii0lomakin Feb 17, 2026

Choose a reason for hiding this comment

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

nit: not sure if https://www.ekstazi.org/ even exists,

It took time for me to find this URL, I will change it in the description of the buildflow.
As for || true let me add annotated warning there to be sure that we did not miss anything.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done, changed, both

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hi, @lesley29 Could you elaborate more on how Ekstazi and PIT are connected? They there for a different purposes in this change. Without Ekstazi, we run a full set of IT tests, which will take hours, while with Ekstazi, only single tests from this suite are running.

I probably understood, you, PIT will mutate, only changed classes, but it will not run only tests that are related to those classes, it will run the full test suite.

Got it, thanks for clarifying. I was confused a bit with test selection and class scoping for mutation indeed, thanks!

andrii0lomakin and others added 5 commits February 17, 2026 16:52
- Remove Qodana code coverage tracking, keep JaCoCo data collection
- Rename coverage output directory from .qodana/code-coverage to .coverage/reports
- Add coverage-gate CI job with diff-cover (line) and custom script (branch)
  - 85% threshold for Claude Code co-authored commits, 70% otherwise
- Add PIT mutation testing quality gate (85% kill rate) for changed code only
  - Ekstazi integration for IT test selection using base branch cache
- Add test coverage requirements guideline to CLAUDE.md
- Create docs/ with TOC and test quality requirements documentation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move ci-cd-diagram.md and testflows-runner-setup.md from .github/workflows/ to docs/
- Update CI/CD diagram with new quality gate jobs (coverage-gate, mutation-testing)
- Update Qodana description (static analysis only, no coverage tracking)
- Update test matrix description (temurin, oracle on self-hosted Hetzner runners)
- Add PR Quality Gates summary table
- Fix cross-document links for new directory structure
- Update docs/README.md TOC with all three documents

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-process changed files into a basename-keyed lookup map before
matching against JaCoCo XML source file entries. Previously, for every
<sourcefile> element in every JaCoCo report, the code iterated over all
changed files to find a path suffix match, resulting in
O(xml_files * packages * sourcefiles * changed_files) complexity.

The new approach builds a dictionary mapping each file's basename
(e.g. "Foo.java") to its full path(s) upfront. When processing a
JaCoCo <sourcefile>, we perform an O(1) dict lookup by basename, then
only check the (typically very short) list of same-named files for a
full path suffix match. This reduces the inner loop from scanning all
changed files to scanning only files sharing the same basename, which
is a significant improvement for larger projects with many modules or
many changed files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace silent `|| true` with an explicit GitHub Actions warning
annotation when Ekstazi selection fails. This makes it visible in the
Actions UI that PIT is falling back to running against all integration
tests. Also fix Ekstazi URL in documentation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants