Skip to content

@Preview composable excluded only when not wrapped in themed @Composable #82

@Gh0stahCZ

Description

@Gh0stahCZ

Custom exclusion annotation works for a plain @Preview but fails when the preview wraps content in an annotated theme function.

Reproducer: fresh clone of repo https://github.com/jacoco/example-jacoco-kotlin-compose Command: ./gradlew clean test jacocoTestReport

Snippet:

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
annotation class ExcludeFromJacocoGeneratedReport
package org.example

import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*

@Composable
fun example() {
    var counter by remember { mutableStateOf(0) }
    if (counter == 2) {
        return
    }
    Button(onClick = { counter++ }) {
        if (counter == 0) {
            Text("Click me")
        } else {
            Text("Click me again")
        }
    }

}

@Composable
@ExcludeFromJacocoGeneratedReport
fun DummyTheme(content: @Composable () -> Unit) {
    content()
}


@Preview
@Composable
@ExcludeFromJacocoGeneratedReport
fun NoThemePreview() {
    example()
}

@Preview
@Composable
@ExcludeFromJacocoGeneratedReport
fun ThemedPreview() {
    DummyTheme @ExcludeFromJacocoGeneratedReport {
        example()
    }
}

Expected: Both previews fully excluded.
Actual: NoThemePreview excluded, ThemedPreview counted (lines attributed to generated synthetic methods).

Question:

  1. Is this an expected limitation of how the Compose compiler rewrites @Composable / @Preview functions?
  2. What is the recommended JaCoCo filter (class/method name patterns or annotations) to exclude every @Preview function and any helper lambdas they generate?

===========

Description

Generated jacocoTestReport.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE report PUBLIC "-//JACOCO//DTD Report 1.1//EN" "report.dtd">
<report name="example-jacoco-kotlin-compose">
    <sessioninfo id="Tomass-MacBook-Pro-3.local-6e2358ea" start="1760083990908" dump="1760083992584"/>
    <package name="org/example">
        <class name="org/example/ExampleKt" sourcefilename="Example.kt">
            <method name="example" desc="(Landroidx/compose/runtime/Composer;I)V" line="9">
                <counter type="INSTRUCTION" missed="0" covered="74"/>
                <counter type="BRANCH" missed="0" covered="2"/>
                <counter type="LINE" missed="0" covered="6"/>
                <counter type="COMPLEXITY" missed="0" covered="2"/>
                <counter type="METHOD" missed="0" covered="1"/>
            </method>
            <method name="example$lambda$1" desc="(Landroidx/compose/runtime/MutableState;)I" line="10">
                <counter type="INSTRUCTION" missed="0" covered="10"/>
                <counter type="LINE" missed="0" covered="1"/>
                <counter type="COMPLEXITY" missed="0" covered="1"/>
                <counter type="METHOD" missed="0" covered="1"/>
            </method>
            <method name="example$lambda$2" desc="(Landroidx/compose/runtime/MutableState;I)V" line="10">
                <counter type="INSTRUCTION" missed="0" covered="12"/>
                <counter type="LINE" missed="0" covered="1"/>
                <counter type="COMPLEXITY" missed="0" covered="1"/>
                <counter type="METHOD" missed="0" covered="1"/>
            </method>
            <method name="example$lambda$4$0" desc="(Landroidx/compose/runtime/MutableState;)Lkotlin/Unit;" line="14">
                <counter type="INSTRUCTION" missed="0" covered="10"/>
                <counter type="LINE" missed="0" covered="1"/>
                <counter type="COMPLEXITY" missed="0" covered="1"/>
                <counter type="METHOD" missed="0" covered="1"/>
            </method>
            <method name="example$lambda$5" desc="(Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/layout/RowScope;Landroidx/compose/runtime/Composer;I)Lkotlin/Unit;" line="15">
                <counter type="INSTRUCTION" missed="0" covered="67"/>
                <counter type="BRANCH" missed="0" covered="2"/>
                <counter type="LINE" missed="0" covered="5"/>
                <counter type="COMPLEXITY" missed="0" covered="2"/>
                <counter type="METHOD" missed="0" covered="1"/>
            </method>
            <counter type="INSTRUCTION" missed="0" covered="173"/>
            <counter type="BRANCH" missed="0" covered="4"/>
            <counter type="LINE" missed="0" covered="11"/>
            <counter type="COMPLEXITY" missed="0" covered="7"/>
            <counter type="METHOD" missed="0" covered="5"/>
            <counter type="CLASS" missed="0" covered="1"/>
        </class>
        <class name="org/example/ComposableSingletons$ExampleKt$lambda$-545336751$1" sourcefilename="Example.kt"/>
        <class name="org/example/ExcludeFromJacocoGeneratedReport" sourcefilename="ExcludeFromJacocoGeneratedReport.kt"/>
        <class name="org/example/ComposableSingletons$ExampleKt" sourcefilename="Example.kt">
            <method name="&lt;clinit&gt;" desc="()V" line="42">
                <counter type="INSTRUCTION" missed="7" covered="0"/>
                <counter type="LINE" missed="1" covered="0"/>
                <counter type="COMPLEXITY" missed="1" covered="0"/>
                <counter type="METHOD" missed="1" covered="0"/>
            </method>
            <counter type="INSTRUCTION" missed="7" covered="0"/>
            <counter type="LINE" missed="1" covered="0"/>
            <counter type="COMPLEXITY" missed="1" covered="0"/>
            <counter type="METHOD" missed="1" covered="0"/>
            <counter type="CLASS" missed="1" covered="0"/>
        </class>
        <sourcefile name="Example.kt">
            <line nr="9" mi="0" ci="4" mb="0" cb="0"/>
            <line nr="10" mi="0" ci="41" mb="0" cb="0"/>
            <line nr="11" mi="0" ci="5" mb="0" cb="2"/>
            <line nr="12" mi="0" ci="4" mb="0" cb="0"/>
            <line nr="14" mi="0" ci="45" mb="0" cb="0"/>
            <line nr="15" mi="0" ci="12" mb="0" cb="2"/>
            <line nr="16" mi="0" ci="22" mb="0" cb="0"/>
            <line nr="17" mi="0" ci="9" mb="0" cb="0"/>
            <line nr="18" mi="0" ci="22" mb="0" cb="0"/>
            <line nr="20" mi="0" ci="2" mb="0" cb="0"/>
            <line nr="22" mi="0" ci="7" mb="0" cb="0"/>
            <line nr="42" mi="7" ci="0" mb="0" cb="0"/>
            <counter type="INSTRUCTION" missed="7" covered="173"/>
            <counter type="BRANCH" missed="0" covered="4"/>
            <counter type="LINE" missed="1" covered="11"/>
            <counter type="COMPLEXITY" missed="1" covered="7"/>
            <counter type="METHOD" missed="1" covered="5"/>
            <counter type="CLASS" missed="1" covered="1"/>
        </sourcefile>
        <sourcefile name="ExcludeFromJacocoGeneratedReport.kt"/>
        <counter type="INSTRUCTION" missed="7" covered="173"/>
        <counter type="BRANCH" missed="0" covered="4"/>
        <counter type="LINE" missed="1" covered="11"/>
        <counter type="COMPLEXITY" missed="1" covered="7"/>
        <counter type="METHOD" missed="1" covered="5"/>
        <counter type="CLASS" missed="1" covered="1"/>
    </package>
    <counter type="INSTRUCTION" missed="7" covered="173"/>
    <counter type="BRANCH" missed="0" covered="4"/>
    <counter type="LINE" missed="1" covered="11"/>
    <counter type="COMPLEXITY" missed="1" covered="7"/>
    <counter type="METHOD" missed="1" covered="5"/>
    <counter type="CLASS" missed="1" covered="1"/>
</report>```

Metadata

Metadata

Assignees

No one assigned

    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