plugins { `java-library` `maven-publish` id("info.solidsoft.pitest") version "1.19.0" // the released Gradle plugin; the PIT plugin it adds is this project, see the pitest task below id("de.javagil.werkcrappit") version "0.26.0" } val pitVersion = "1.30.0" dependencies { // PIT loads this JAR into its own process, so its API is there at run time. compileOnly("org.pitest:pitest-entry:$pitVersion") implementation("org.jacoco:org.jacoco.core:0.8.15") // the same ASM JaCoCo and PIT 1.30.0 bring, used to list the methods a class file declares implementation("org.ow2.asm:asm:9.10.1") testImplementation("org.pitest:pitest-entry:$pitVersion") testImplementation("io.cucumber:gherkin:42.0.1") testImplementation("com.networknt:json-schema-validator:3.0.8") } tasks.jar { manifest { attributes("Implementation-Title" to project.name, "Implementation-Version" to project.version) } } publishing { publications { create("pitPlugin") { from(components["java"]) } } repositories { // What the functional test consumes: the packaged JAR with its published // metadata, never the classes of this build. maven { name = "functionalTest" url = uri(rootProject.layout.buildDirectory.dir("functional-test-repo")) } } } val functionalTest = sourceSets.create("functionalTest") configurations[functionalTest.implementationConfigurationName].extendsFrom(configurations.testImplementation.get()) configurations[functionalTest.runtimeOnlyConfigurationName].extendsFrom(configurations.testRuntimeOnly.get()) dependencies { "functionalTestImplementation"(gradleTestKit()) "functionalTestImplementation"(sourceSets.test.get().output) "functionalTestImplementation"("com.fasterxml.jackson.core:jackson-databind:2.22.3") } val functionalTestTask = tasks.register("functionalTest") { description = "Runs PIT with the packaged plugin in a real Gradle build." group = "verification" testClassesDirs = functionalTest.output.classesDirs classpath = functionalTest.runtimeClasspath dependsOn("publishPitPluginPublicationToFunctionalTestRepository") systemProperty("werkcrappit.repo", rootProject.layout.buildDirectory.dir("functional-test-repo").get().asFile.absolutePath) systemProperty("werkcrappit.version", project.version.toString()) systemProperty("werkcrappit.pitestVersion", pitVersion) } // The pages of the CodeCharta export draw with a script; these tests run it in a headless Chromium // (RFC 0003). The browser is downloaded once into .playwright/, outside build/, so a clean keeps it. val browserTest = sourceSets.create("browserTest") configurations[browserTest.implementationConfigurationName].extendsFrom(configurations.testImplementation.get()) configurations[browserTest.runtimeOnlyConfigurationName].extendsFrom(configurations.testRuntimeOnly.get()) val playwrightVersion = "1.63.0" val playwrightBrowsers = rootProject.layout.projectDirectory.dir(".playwright").asFile.absolutePath dependencies { "browserTestImplementation"(sourceSets.main.get().output) "browserTestImplementation"(sourceSets.test.get().output) "browserTestImplementation"("com.microsoft.playwright:playwright:$playwrightVersion") } val installBrowser = tasks.register("installBrowser") { description = "Downloads the headless Chromium of Playwright, unless it is there already." group = "verification" classpath = browserTest.runtimeClasspath mainClass = "com.microsoft.playwright.CLI" args("install", "--only-shell", "chromium") environment("PLAYWRIGHT_BROWSERS_PATH", playwrightBrowsers) } val browserTestTask = tasks.register("browserTest") { description = "Runs the scripts of the report pages in a headless Chromium." group = "verification" testClassesDirs = browserTest.output.classesDirs classpath = browserTest.runtimeClasspath dependsOn(installBrowser) environment("PLAYWRIGHT_BROWSERS_PATH", playwrightBrowsers) environment("PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD", "1") } tasks.check { dependsOn(functionalTestTask) dependsOn(browserTestTask) } // Mutation testing of this module with its own Mutation-CRAP: not part of `check`, run by the nightly // build definition `mutation` in .werkator.yml. The unit tests only; the functional tests start // real Gradle builds with PIT, minutes per mutant. pitest { pitestVersion = pitVersion junit5PluginVersion = "1.2.3" targetClasses = setOf("de.javagil.werkcrappit.pit.*") targetTests = setOf("de.javagil.werkcrappit.pit.*") // what the Test tasks set, PIT's minions need too jvmArgs = listOf("-Dwerkcrappit.version=${project.version}") threads = providers.gradleProperty("pitestThreads").map { it.toInt() }.orElse(2) // incremental: unchanged classes take their results from the history, which lives outside // build/, so that the clean command keeps it, and per worktree, so per branch enableDefaultIncrementalAnalysis = true historyInputLocation = rootProject.layout.projectDirectory.file(".pitest/werkcrappit-pit-history.bin") historyOutputLocation = rootProject.layout.projectDirectory.file(".pitest/werkcrappit-pit-history.bin") outputFormats = setOf("HTML", "XML") timestampedReports = false } // The Gradle plugin adds werkcrappit-pit to PIT's classpath by its coordinates, which are this project's, // and Gradle takes this project for them, whatever the version: werkcrappit measures itself with the // listener of the same commit. The PIT plugin does not know that this needs the JAR built first. tasks.named("pitest") { dependsOn(tasks.jar) } dependencies { // PIT 1.30 keeps its incremental history only through this plugin pitest("org.pitest:pitest-history-plugin:0.0.1") } werkcrappit { // a ratchet: the highest value, rounded up (2026-10-03: 9, three methods of complexity 9 with every // mutant killed); lower it as the hotspots shrink, never raise it. Red starts at 90 % of it, 8.1. maxMutationCrap = 9 // ratchets as well (RFC 0002), today's highest values: ExclusionRules and JacocoComplexity at LCOM4 3, // MutationCrapListener with 21 other classes it depends on maxLcom4 = 3 maxEfferentCoupling = 21 }