tree: a5bdda15c7106fe4ac30edb47fd689981129a5c7 [search]
  1. build.gradle.kts
  2. ReadMe.md
compiler/test-coverage/ReadMe.md

Test coverage for the compiler frontend (FIR) area

This module aggregates JaCoCo test coverage for the FIR frontend (org.jetbrains.kotlin.fir.* classes) from the test tasks agreed on in KQA-3083:

  • :compiler:test
  • :compiler:tests-integration:test
  • :compiler:fir:analysis-tests:test
  • :compiler:fir:fir2ir:test
  • :compiler:fir:raw-fir:psi2fir:test
  • :compiler:fir:raw-fir:light-tree2fir:test

This list is a snapshot for orientation — the authoritative list is the jacocoAggregation(...) dependencies in build.gradle.kts, which may grow to include more test tasks or additional FIR production modules over time.

Coverage collection is disabled by default and is a strict no-op for regular builds. It is enabled with the Gradle property kotlin.build.coverage.enabled (also settable in local.properties). The property must be passed to every coverage-related invocation — including the report task — otherwise the test JVMs are not instrumented and the report is skipped.

The setup lives in two places only, so the coverage engine can be swapped without touching the test modules themselves:

  • repo/gradle-build-conventions/test-coverage-convention — applies JaCoCo to the producer projects when the property is set;
  • this module — aggregates execution data into a report via Gradle‘s jacoco-report-aggregation plugin (Project-Isolation-compatible: data flows through outgoing configurations, no project reaches into another project’s build directory).

How to generate the report

The report task triggers the required test tasks through Gradle's dependency graph — running the report alone is enough:

./gradlew :compiler:test-coverage:firCoverageReport -Pkotlin.build.coverage.enabled=true

Or, in IDEA, run the checked-in FIR Coverage Report run configuration (Tests folder), which runs the same task with the property already set.

Outputs:

  • HTML: compiler/test-coverage/build/reports/jacoco/firCoverageReport/html/index.html
  • XML: compiler/test-coverage/build/reports/jacoco/firCoverageReport/firCoverageReport.xml

To collect coverage from a subset of the suites, exclude the unwanted test tasks with -x; the report is built from whatever execution data exists:

./gradlew :compiler:test-coverage:firCoverageReport -Pkotlin.build.coverage.enabled=true \
  -x :compiler:test -x :compiler:tests-integration:test \
  -x :compiler:fir:analysis-tests:test -x :compiler:fir:fir2ir:test
# keeps only the raw-fir suites

Raw execution data lands in each producing project's build/jacoco/test.exec. Re-running a test task with a different --tests filter overwrites its .exec file, so collect one scope per invocation.

Note: with kotlin.build.coverage.enabled=true the test tasks are configured with ignoreFailures = true, so failing tests don't abort the build and the report is still generated from the collected data. The failures are still printed in the test output.

How to open the report

Open the HTML report in a browser, or in IntelliJ IDEA — double-click html/index.html: IDEA shows coverage for loaded modules in the Coverage tool window and renders per-file coverage in the Project view. (IDEA refuses to open reports located outside the project directory.)

Known limitations

  • Only org.jetbrains.kotlin.* classes are instrumented (see test-coverage-convention). This is deliberate: instrumenting everything breaks reflection-sensitive tests — debugger stepping/local-variable tests fail because com.sun.tools.jdi reflects over JDWP constant classes and trips on JaCoCo's synthetic members, and some codegen tests reflect over the bytecode they generate and load. Widen the filter here and in the report module together if the report scope ever grows beyond org.jetbrains.kotlin.
  • Only code running inside the test JVMs is instrumented; tests that spawn external processes (some :compiler:tests-integration scenarios) don't contribute the coverage of those processes.
  • JaCoCo reports branch data on Kotlin bytecode constructs (inline functions, when mappings, suspend state machines) that can look noisier than Kotlin-aware engines — keep this in mind when reading branch counters.