Skip to content

[BUG]: Domain coverage fails with JaCoCo double instrumentation after removing stale test exemptions #6500

Description

@adhiamboperes

Describe the bug

#6444 surfaced an issue in the code coverage workflow.

Removing the test_file_not_required exemptions for the two DragDropSortInput providers in #6444 exposes a coverage build failure. Coverage for DragDropSortInputHasElementXBeforeElementYClassifierProviderTest fails while instrumenting a transitive RatioInput dependency, before tests execute.

The reported CI log contains:

Caused by: java.lang.IllegalStateException: Cannot process instrumented class
org/oppia/android/domain/classify/rules/ratioinput/RatioInputHasSpecificTermEqualToRuleClassifierProvider$createRuleClassifier$$inlined$createDoubleInputClassifier$1.
Please supply original non-instrumented classes.

The stack passes through JacocoInstrumentationKt.createCoverageInstrumentedJar and InstrSupport.assertNotInstrumented. JaCoCo version: 0.8.9.202303300400/461ebf3. The failed build target is //domain/src/main/java/org/oppia/android/domain/classify/rules/ratioinput:ratio_input_providers_kt; its output JAR is consequently missing.

Steps To Reproduce

On the develp branch, use the repository's normal build configuration and run:

bazel coverage \
  //domain:src/test/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputHasElementXBeforeElementYClassifierProviderTest \
  --instrumentation_filter=//domain/... \
  --test_output=errors

To exercise the exemption trigger, run //scripts:run_coverage for domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputHasElementXBeforeElementYClassifierProvider.kt before and after removing its test_file_not_required exemption. Before removal, the script skips this source's coverage. After removal, it attempts coverage for its test.

Expected Behavior

Removing a stale test-file exemption should allow coverage to run successfully. Kotlin compilation should consume original bytecode and coverage instrumentation should not process classes that already contain JaCoCo instrumentation.

Screenshots/Videos

No response

What device/emulator are you using?

No response

Which Android version is your device/emulator running?

No response

Which version of the Oppia Android app are you using?

No response

Additional Context

I put this through Codex to analyze the stacktrace. This is what we came up with:

The coverage failure is caused by JaCoCo double-instrumenting Kotlin-generated inline classes across Bazel target boundaries.

BazelClient.runCoverageForTestTarget() applies --instrumentation_filter=//domain/..., making all eligible dependencies within the domain module subject to instrumentation, not just the source under test. Although RatioInputHasSpecificTermEqualToRuleClassifierProvider.kt is marked source_file_is_incompatible_with_code_coverage: true, this exemption only prevents coverage from being run directly for that source. It does not exclude the class from Bazel instrumentation when it is a dependency of another test.

I double-checked this through bytecode inspection. GenericRuleClassifier.Factory.createDoubleInputClassifier() is an inline function defined in a separate Bazel target. It calls another inline function, InteractionObjectTypeExtractorRepository.getExtractor(), which generates a lambda. The generic classifier's compile/ABI JAR already contains JaCoCo-instrumented versions of these generated classes, confirmed by the presence of $jacocoData, $jacocoInit(), and Offline.getProbes. When Kotlin inlines the factory into consuming classifiers such as RatioInput and FractionInput, it copies this already-instrumented bytecode. The consumer's coverage step then attempts to instrument it again, causing JaCoCo to fail with Cannot process instrumented class.

  • Running the test normally: PASS.
  • Running coverage with --instrumentation_filter=//domain/...: FAIL, during compilation, before any tests execute.
  • Running coverage with the generic classifier's Bazel package excluded: PASS, successfully generating coverage.dat.
'--instrumentation_filter=//domain/...,-//domain/src/main/java/org/oppia/android/domain/classify/rules:'

The trailing colon limits the exclusion to the factory's exact Bazel package, allowing classifier subpackages to remain instrumented. This package exclusion is just a diagnostic workaround but reduces coverage for the generic classifier package.

A suggested fix:
https://github.com/bazel-contrib/rules_kotlin/blob/master/docs/kotlin.md

Address the rules_kotlin compile/ABI JAR pipeline ensuring that consumers receive uninstrumented bytecode for Kotlin inlining while instrumented bytecode remains available for runtime coverage collection.

Set experimental_use_abi_jars = True in define_kt_toolchain(name = "kotlin_16_jdk9_toolchain", ...) in tools/kotlin/BUILD.bazel.

Verification

  1. Both affected DragDropSortInput tests and representative RatioInput/FractionInput tests.
  2. Run normal tests and an app build to check annotation processing and compiler-plugin compatibility.
  3. Add a CI coverage regression check using the original unrestricted filter.

Activity

  1. added theissue type on Oct 9, 2026
  2. added
    bugEnd user-perceivable behaviors which are not desirable.
    Impact: HighHigh perceived user impact (breaks a critical feature or blocks a release).
    Work: LowSolution is clear and broken into good-first-issue-sized chunks.
    on Oct 9, 2026
  3. adhiamboperes commented on Oct 9, 2026

    @adhiamboperes
    ContributorAuthor

    @siddhigupta075, PTAL as this is urgent and blocking successful CI runs.

  4. added
    good first issueThis item is good for new contributors to make their pull request.
    on Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Impact: HighHigh perceived user impact (breaks a critical feature or blocks a release).Work: LowSolution is clear and broken into good-first-issue-sized chunks.bugEnd user-perceivable behaviors which are not desirable.good first issueThis item is good for new contributors to make their pull request.

Type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions