Android Quick Tip: fixing SonarQube “line out of range” issue when using Jetpack Compose

André Theilacker
2 min readJan 15, 2024

--

Photo by Bermix Studio on Unsplash

One issue I found annoying when developing projects using Jetpack Compose is generating code coverage reports that properly display the coverage for composable functions. In most projects, I use JaCoCo for generating reports, that are then published to SonarQube to generate metrics.

JaCoCo is still unable to properly cover composable functions, reporting multiple invisible lines and branches as missing in the final report, since Compose generates a lot of bytecode behind the scenes and JaCoCo doesn’t offer any filter to hide those in the report (as it does with most of Kotlin syntactic sugar).

In a project I worked on some months ago, I noticed an even worse issue: some files were displaying no coverage at all! I tracked this issue down to files with composable functions using methods such as LaunchedEffect (and some others).

The JaCoCo report seemed to be working fine, but when it was analyzed by the SonarQube plugin an error popped up:

[...]
Importing 1 report(s). Turn your logs in debug mode in order to see the exhaustive list.
Cannot import coverage information for file 'path/HelloWorld.kt', coverage data is invalid. Error: {}
java.lang.IllegalStateException: Line 65535 is out of range in the file path/HelloWorld.kt
[...]

The issue is that the report analyzed by the plugin points to the nonexistent line 65535. After some digging, it seems that this is an issue in the Compose compiler, and although JaCoCo doesn’t have issues reporting a line that doesn’t exist, it does break the Sonar plugin.

There’s already an open issue for this bug in Google’s issue tracker, but it doesn’t seem to be a priority currently. Anyway, there’s a temporary fix available to fix the issue for now; you need to add the following snippet to your JaCoCo task configuration:

For Gradle scripts using Kotlin DSL:

doLast {
val reportFile = reports.xml.outputLocation.asFile.get()
val newContent = reportFile.readText().replace("<line[^>]+nr=\"65535\"[^>]*>".toRegex(), "")
reportFile.writeText(newContent)
}

For Gradle scripts using Groovy:

doLast {
def reportFile = reports.xml.outputLocation.asFile.get()
def newContent = reportFile.text.replaceAll("<line[^>]+nr=\"65535\"[^>]*>", "")
reportFile.text = newContent
}

If you configure your JaCoCo task through a convention plugin, you will need to adapt this snippet a little but it should work the same way. The idea is to remove the line in the final report that points to the nonexistent line, so the Sonar plugin can interpret it without issues.

Useful links:

That’s it! I hope you found this article useful. If you have other useful tips to improve or fix coverage issues when dealing with Compose, feel free to comment!

Uma versão em português deste artigo está disponível aqui.

--

--