diff --git a/build.gradle.kts b/build.gradle.kts index cb422a3..e8fff64 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,6 +8,49 @@ plugins { group = "de.nowchess" version = "1.0-SNAPSHOT" +// Canonical coverage exclusions — glob patterns consumed by Sonar directly; +// converted to scoverage regexes via globToScoverageRegex for instrumentation-time exclusion. +val coverageExclusions = listOf( + // UI renders JavaFX components; headless test environments cannot exercise rendering paths + "modules/ui/**", + // FastParse macro-generated combinators produce synthetic branches that scoverage marks as uncovered + "modules/io/src/main/scala/de/nowchess/io/fen/FenParserFastParse*", + // NNUE inference pipeline — coverage requires a trained model file not present in CI + "**/bot/**/NNUE.scala", + "**/bot/**/NNUEBot.scala", + "**/bot/**/EvaluationNNUE.scala", + // NBAI binary format loader/writer — error paths require crafted corrupt files; migrator is a one-shot tool + "**/bot/**/NbaiLoader.scala", + "**/bot/**/NbaiModel.scala", + "**/bot/**/NbaiMigrator.scala", + "**/bot/**/NbaiWriter.scala", + // PolyglotBook — binary I/O and dead-code guards (bit-masked fields can never exceed valid range) + "**/bot/**/PolyglotBook.scala", + "**/bot/**/MoveOrdering.scala", + "**/bot/**/AlphaBetaSearch.scala", + // DTO case class synthetic methods (Scala compiler-generated apply/$default params) + "**/api/src/main/scala/de/nowchess/api/dto/**Dto.scala", + // Core infrastructure: exception classes, config, registry implementation, game entry + "**/core/src/main/scala/de/nowchess/chess/exception/**", + "**/core/src/main/scala/de/nowchess/chess/config/**", + "**/core/src/main/scala/de/nowchess/chess/registry/GameEntry.scala", + "**/core/src/main/scala/de/nowchess/chess/registry/GameRegistryImpl.scala", + // GameResource — REST integration layer with @Inject var fields; mocking dependencies for unit tests is infeasible with Quarkus DI; integration tests would require @QuarkusTest which Scoverage doesn't instrument + "**/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala" +) + +// Converts a Sonar-style glob to a scoverage regex (matched against full source path). +// Order matters: protect ** before converting lone *, escape dots last. +fun globToScoverageRegex(glob: String): String = + glob + .replace("**", "^@") + .replace("*", "[^/]*") + .replace(".", "\\.") + .replace("^@", ".*") + .let { ".*$it" } + +extra["SCOVERAGE_EXCLUDED"] = coverageExclusions.map(::globToScoverageRegex) + sonar { properties { property("sonar.projectKey", "Now-Chess-Systems") @@ -22,35 +65,7 @@ sonar { }.joinToString(",") property("sonar.scala.coverage.reportPaths", scoverageReports) - property( - "sonar.coverage.exclusions", - // UI renders JavaFX components; headless test environments cannot exercise rendering paths - "modules/ui/**," + - // FastParse macro-generated combinators produce synthetic branches that scoverage marks as uncovered - "modules/io/src/main/scala/de/nowchess/io/fen/FenParserFastParse*," + - // NNUE inference pipeline — coverage requires a trained model file not present in CI - "**/bot/**/NNUE.scala," + - "**/bot/**/NNUEBot.scala," + - "**/bot/**/EvaluationNNUE.scala," + - // NBAI binary format loader/writer — error paths require crafted corrupt files; migrator is a one-shot tool - "**/bot/**/NbaiLoader.scala," + - "**/bot/**/NbaiModel.scala," + - "**/bot/**/NbaiMigrator.scala," + - "**/bot/**/NbaiWriter.scala," + - // PolyglotBook — binary I/O and dead-code guards (bit-masked fields can never exceed valid range) - "**/bot/**/PolyglotBook.scala," + - "**/bot/**/MoveOrdering.scala," + - "**/bot/**/AlphaBetaSearch.scala," + - // DTO case class synthetic methods (Scala compiler-generated apply/$default params) - "**/api/src/main/scala/de/nowchess/api/dto/**Dto.scala," + - // Core infrastructure: exception classes, config, registry implementation, game entry - "**/core/src/main/scala/de/nowchess/chess/exception/**," + - "**/core/src/main/scala/de/nowchess/chess/config/**," + - "**/core/src/main/scala/de/nowchess/chess/registry/GameEntry.scala," + - "**/core/src/main/scala/de/nowchess/chess/registry/GameRegistryImpl.scala," + - // GameResource — REST integration layer with @Inject var fields; mocking dependencies for unit tests is infeasible with Quarkus DI; integration tests would require @QuarkusTest which Scoverage doesn't instrument - "**/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala" - ) + property("sonar.coverage.exclusions", coverageExclusions.joinToString(",")) } } diff --git a/modules/api/build.gradle.kts b/modules/api/build.gradle.kts index d4b4fba..934c6eb 100644 --- a/modules/api/build.gradle.kts +++ b/modules/api/build.gradle.kts @@ -8,6 +8,8 @@ version = "1.0-SNAPSHOT" @Suppress("UNCHECKED_CAST") val versions = rootProject.extra["VERSIONS"] as Map +@Suppress("UNCHECKED_CAST") +val scoverageExcluded = rootProject.extra["SCOVERAGE_EXCLUDED"] as List repositories { mavenCentral() @@ -19,9 +21,7 @@ scala { scoverage { scoverageVersion.set(versions["SCOVERAGE"]!!) - excludedFiles.set(listOf( - ".*Dto\\.scala" - )) + excludedFiles.set(scoverageExcluded) } configurations.scoverage { diff --git a/modules/bot/build.gradle.kts b/modules/bot/build.gradle.kts index fe3d3e8..f861543 100644 --- a/modules/bot/build.gradle.kts +++ b/modules/bot/build.gradle.kts @@ -8,6 +8,8 @@ version = "1.0-SNAPSHOT" @Suppress("UNCHECKED_CAST") val versions = rootProject.extra["VERSIONS"] as Map +@Suppress("UNCHECKED_CAST") +val scoverageExcluded = rootProject.extra["SCOVERAGE_EXCLUDED"] as List repositories { mavenCentral() @@ -26,18 +28,7 @@ scoverage { "de\\.nowchess\\.bot\\.util\\.PolyglotBook", ) ) - excludedFiles.set( - listOf( - ".*NNUE\\.scala", - ".*NNUEBot\\.scala", - ".*NbaiLoader\\.scala", - ".*NbaiMigrator\\.scala", - ".*NbaiWriter\\.scala", - ".*PolyglotBook\\.scala", - ".*MoveOrdering\\.scala", - ".*AlphaBetaSearch\\.scala", - ) - ) + excludedFiles.set(scoverageExcluded) } tasks.withType { diff --git a/modules/core/build.gradle.kts b/modules/core/build.gradle.kts index 4e4bec3..3e5df5e 100644 --- a/modules/core/build.gradle.kts +++ b/modules/core/build.gradle.kts @@ -9,6 +9,8 @@ version = "1.0-SNAPSHOT" @Suppress("UNCHECKED_CAST") val versions = rootProject.extra["VERSIONS"] as Map +@Suppress("UNCHECKED_CAST") +val scoverageExcluded = rootProject.extra["SCOVERAGE_EXCLUDED"] as List repositories { mavenCentral() @@ -20,9 +22,7 @@ scala { scoverage { scoverageVersion.set(versions["SCOVERAGE"]!!) - excludedFiles.set(listOf( - ".*GameResource\\.scala" - )) + excludedFiles.set(scoverageExcluded) } tasks.withType { diff --git a/modules/io/build.gradle.kts b/modules/io/build.gradle.kts index d0027de..752ab28 100644 --- a/modules/io/build.gradle.kts +++ b/modules/io/build.gradle.kts @@ -8,6 +8,8 @@ version = "1.0-SNAPSHOT" @Suppress("UNCHECKED_CAST") val versions = rootProject.extra["VERSIONS"] as Map +@Suppress("UNCHECKED_CAST") +val scoverageExcluded = rootProject.extra["SCOVERAGE_EXCLUDED"] as List repositories { mavenCentral() @@ -19,7 +21,7 @@ scala { scoverage { scoverageVersion.set(versions["SCOVERAGE"]!!) - excludedFiles.set(listOf(".*FenParserFastParse.*")) + excludedFiles.set(scoverageExcluded) } tasks.withType {