diff --git a/CHANGELOG.md b/CHANGELOG.md index 10a398072..9ea110064 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ We use [semantic versioning](http://semver.org/): - [fix] _agent_: With `duplicates=FAIL`, the error for a duplicate, non-identical class now names the class and how to resolve the problem. - [fix] _agent_, _teamscale-gradle-plugin_, _teamscale-maven-plugin_: A corrupt class file whose class name cannot be read is skipped with a warning instead of failing the coverage report. - [fix] _impacted-test-engine_: A test that a failing assumption aborts is reported as skipped instead of as an error. +- [fix] _teamscale-gradle-plugin_: The default excludes now match Mockito mocks (e.g. `com.company.IFoo$MockitoMock$123`) and Guice fast classes (e.g. `com.company.Foo$$FastClassByGuice$$abc`). Previously the profiler instrumented them, and the testwise coverage report warned that it found coverage for classes that were not provided. # 39.0.0 - [breaking] _agent_: The deprecated Docker image `cqse/teamscale-jacoco-agent` is no longer published. Use `cqse/teamscale-java-profiler` instead, which has replaced it since version 36.5.0. diff --git a/teamscale-gradle-plugin/src/main/kotlin/com/teamscale/TeamscalePlugin.kt b/teamscale-gradle-plugin/src/main/kotlin/com/teamscale/TeamscalePlugin.kt index ea6260885..2393c41f2 100755 --- a/teamscale-gradle-plugin/src/main/kotlin/com/teamscale/TeamscalePlugin.kt +++ b/teamscale-gradle-plugin/src/main/kotlin/com/teamscale/TeamscalePlugin.kt @@ -58,8 +58,8 @@ class TeamscalePlugin : Plugin { "okhttp3.*", "okio.*", "retrofit2.*", - "*.MockitoMock.*", - "*.FastClassByGuice.*", + "*\$MockitoMock\$*", + "*\$\$FastClassByGuice\$\$*", "*.ConstructorAccess" ) diff --git a/teamscale-gradle-plugin/src/test/kotlin/com/teamscale/TeamscalePluginDefaultExcludesTest.kt b/teamscale-gradle-plugin/src/test/kotlin/com/teamscale/TeamscalePluginDefaultExcludesTest.kt new file mode 100644 index 000000000..f0623d311 --- /dev/null +++ b/teamscale-gradle-plugin/src/test/kotlin/com/teamscale/TeamscalePluginDefaultExcludesTest.kt @@ -0,0 +1,41 @@ +package com.teamscale + +import org.assertj.core.api.Assertions.assertThat +import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.named +import org.gradle.testfixtures.ProjectBuilder +import org.gradle.testing.jacoco.plugins.JacocoTaskExtension +import org.jacoco.core.runtime.WildcardMatcher +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +/** Tests which classes the plugin's default excludes match. */ +class TeamscalePluginDefaultExcludesTest { + + @ParameterizedTest + @ValueSource(strings = ["com/x/IFoo\$MockitoMock\$123", "com/x/Foo\$\$FastClassByGuice\$\$abc"]) + fun `default excludes match Mockito mocks and Guice fast classes`(internalClassName: String) { + assertThat(isExcludedByDefault(internalClassName)) + .`as`("The default excludes should match the generated class $internalClassName") + .isTrue() + } + + @ParameterizedTest + @ValueSource(strings = ["com/x/Foo\$Bar", "com/x/Foo\$1"]) + fun `default excludes do not match ordinary nested or anonymous classes`(internalClassName: String) { + assertThat(isExcludedByDefault(internalClassName)) + .`as`("The default excludes should not match the ordinary nested or anonymous class $internalClassName") + .isFalse() + } + + private fun isExcludedByDefault(internalClassName: String): Boolean { + val project = ProjectBuilder.builder().build() + project.pluginManager.apply(TeamscalePlugin::class.java) + val excludes = project.tasks.named("test").get().extensions.getByType().excludes + + // JaCoCo's agent replaces the dots of each pattern with slashes and matches it against the JVM internal class name. + val excludesMatcher = WildcardMatcher(excludes.orEmpty().joinToString(":").replace('.', '/')) + return excludesMatcher.matches(internalClassName) + } +}