Skip to content

Introduce JaCoCo Method Filter (JMF) into root-pom — opt-in, backward compatible #22

Description

@miroslavpojer

Background

We use JaCoCo (instruction coverage) with Scala. Scala emits large amounts of compiler boilerplate
(case-class helpers, lambdas, default-param accessors, value-class bridges, …), which inflates method
counts and distorts coverage %.

Today, filtering is done by the ABSA JaCoCo fork
za.co.absa.jacoco:jacoco-maven-plugin (doScalaMethodFiltration=true) configured in root-pom's
code-coverage profile. The fork is pinned at 0.8.11 and unmaintained, which also blocks moving
to a current stock JaCoCo.

The replacement is MoranaApps JMFio.github.moranaapps:jacoco-method-filter-maven-plugin.
It rewrites compiled classes to add @CoverageGenerated to matched methods so stock JaCoCo
ignores them. Rules live in a jmf-rules.txt file; with no rules file the plugin is a pass-through
(unfiltered report, no failure), and strict enforcement is available via opt-in jmf.requireRules.
Use the JMF release ≥ the one carrying MoranaApps/jacoco-method-filter#70 (release TBD).

AbsaOSS/commons#147 first added JMF locally in Commons (108-line pom.xml profile override).
Review consensus (@wajda, @miroslavpojer): this is a common standard → it belongs in root-pom,
released as a new version, opt-in so projects adopt it explicitly. Commons then becomes the
first consumer and its PR shrinks to a parent bump + jmf-rules.txt + workflow.

Goal

root-pom's code-coverage profile supports JMF filtering, activated only when the consumer
opts in (by adding jmf-rules.txt). A project that does not opt in sees no behaviour change when
it bumps the parent — same coverage path, same numbers.

Why the JMF profile must be gated

The JMF path neutralizes the za.co.absa.jacoco fork (<phase>none</phase>) and switches to stock
org.jacoco 0.8.15. If it were always-on, a non-adopter with no jmf-rules.txt would silently lose
the fork's doScalaMethodFiltration and get unfiltered Scala coverage — a coverage-number shift
on a plain parent bump. Gating on jmf-rules.txt existence keeps "bump the parent, change nothing
else → identical numbers" true, and spares non-adopters the extra rewrite / report /
resources-overlay executions.

Design decisions to resolve

# Decision Proposed default
D1 Activation mechanism Nested profile inside code-coverage + <activation><file><exists>${project.basedir}/jmf-rules.txt</exists></file></activation>. Fallback if it clashes with the existing <property><name>code-coverage</name></property> activation: jmf.skip property defaulting to true, adopter overrides via a pom property / -Djmf.skip=false.
D2 Default / global rules Ship a root-pom-maintained global rules set, passed via <globalRules> (bundled file path resolvable in every consumer, or a raw URL). jmf-rules.txt then only carries project-specific +/exclude rules. Trade-off: URL fetch at build time / offline builds.
D3 Fate of the za.co.absa.jacoco fork Keep as the default code-coverage path for non-adopters; inside the JMF path set its executions to <phase>none</phase> and use stock org.jacoco:jacoco-maven-plugin 0.8.15. Verify no version clash.
D4 root-pom version bump Minor if D1 keeps it fully backward compatible (nothing changes unless the file is added); major if any default behaviour shifts.

Scope

  • Resolve D1–D4.
  • Add the gated JMF profile to root-pom's code-coverage:
    • neutralize za.co.absa.jacoco:jacoco-maven-plugin executions (<phase>none</phase>)
    • org.jacoco:jacoco-maven-plugin 0.8.15 — prepare-agent, propertyName=jacoco.argLine
    • io.github.moranaapps:jacoco-method-filter-maven-plugin <pinned release>rewrite@process-test-classes, report@verify (reportFormats=html,xml)
    • maven-resources-plugin — overlay ${project.build.directory}/classes-filtered over classes @ process-test-classes
    • org.scalatest:scalatest-maven-plugin<argLine>${jacoco.argLine}</argLine>
  • Provide / host the shared global rules file (D2).
  • mvn install a SNAPSHOT; run the three test scenarios:
    • A (back-compat): a consumer with no jmf-rules.txt + -Pcode-coverage → old coverage path, no failure, numbers unchanged.
    • B (JMF active): a consumer with jmf-rules.txtrewrite + filtered report run, target/jacoco.xml produced, boilerplate filtered.
    • C (non-Scala / plain Maven): consumer with new parent, no rules file → no-op.
  • CHANGELOG + migration guide (see SPEC.md §7) into docs/ and the GitHub release.
  • Cut release/X.Y.Z; verify on Maven Central.

Acceptance criteria

  • A consumer that bumps the parent and does nothing else builds and reports coverage exactly as before.
  • A consumer that adds jmf-rules.txt gets a filtered HTML + XML report under target/.
  • mvn compile jacoco-method-filter:verify lists filtered methods for review.
  • The migration guide is in the release notes.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions