Skip to content

Commit 1b29062

Browse files
committed
[#337] Automatically add junit-platform-launcher to testRuntimeOnly
... for JUnit Platform projects to avoid "UNKNOWN_ERROR" or: "NoClassDefFoundError: org.junit.platform.launcher.core.LauncherFactory" with PIT 1.14.0+ (with pitest-junit-plugin 1.2.0+). That dependency is no longer shaded. More details: #337
1 parent cbc3201 commit 1b29062

File tree

7 files changed

+122
-13
lines changed

7 files changed

+122
-13
lines changed

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,18 @@
22

33
## 1.14.0 - Unreleased
44

5-
- Remove deprecated Project.getConvention() usage (in Gradle 8.2+) - [#343](https://github.com/szpak/gradle-pitest-plugin/issues/343)
5+
- Automatically add `junit-platform-launcher` dependency to `testRuntimeOnly` for JUnit Platform projects - [#337](https://github.com/szpak/gradle-pitest-plugin/issues/337) - help from [Björn Kautler](https://github.com/Vampire)
6+
- Remove deprecated `Project.getConvention()` usage (in Gradle 8.2+) - [#343](https://github.com/szpak/gradle-pitest-plugin/issues/343)
67
- Basic regression testing with Gradle up to 8.2
78

9+
**Compatibility notes**
10+
Starting with PIT 1.14.0 (with pitest-junit-plugin 1.2.0+) `junit-platform-launcher` is no longer shaded and has to be explicitly added to avoid:
11+
"Minion exited abnormally due to UNKNOWN_ERROR" or "NoClassDefFoundError: org.junit.platform.launcher.core.LauncherFactory".
12+
13+
As an experimental (incubating) feature, `junit-platform-launcher` is automatically added to the `testRuntimeOnly` configuration for the JUnit Platform projects.
14+
15+
**PLEASE NOTE**. This feature is experimental and might not work as expected in some corner cases. In that situation, just disable it with `addJUnitPlatformLauncher = false` and add the required dependency 'junit-platform-launcher' in a proper version to 'testRuntimeOnly' manually. More information: https://github.com/szpak/gradle-pitest-plugin/issues/337
16+
817

918
## 1.9.11 - 2022-11-27
1019

src/funcTest/groovy/info/solidsoft/gradle/pitest/functional/Junit5FunctionalSpec.groovy

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ class Junit5FunctionalSpec extends AbstractPitestFunctionalSpec {
3333
result.standardOutput.contains('Generated 2 mutations Killed 2 (100%)')
3434
}
3535

36-
@Issue(["https://github.com/szpak/gradle-pitest-plugin/issues/177", "https://github.com/szpak/gradle-pitest-plugin/issues/300"])
36+
@Issue(["https://github.com/szpak/gradle-pitest-plugin/issues/177", "https://github.com/szpak/gradle-pitest-plugin/issues/300",
37+
"https://github.com/szpak/gradle-pitest-plugin/issues/337"])
3738
void "should work with junit5 without explicitly adding dependency (#description)"() {
3839
given:
3940
copyResources("testProjects/junit5simple", "")
@@ -51,7 +52,8 @@ class Junit5FunctionalSpec extends AbstractPitestFunctionalSpec {
5152
result.standardOutput.contains("junit-platform-commons-${expectedJUnitPlatformVersion}.jar")
5253
where:
5354
buildFileName || expectedJunitPluginVersion | expectedJUnitJupiterVersion | expectedJUnitPlatformVersion
54-
'build.gradle' || "1.0.0" | "5.8.0" | "1.8.0"
55+
'build.gradle' || "1.2.0" | "5.10.0" | "1.10.0"
56+
'build-pit-plugin-1.0.0-junit-5.8.gradle' || "1.0.0" | "5.8.0" | "1.8.0"
5557
'build-pit-1.8-junit-platform-5.7.gradle' || "0.14" | "5.7.0" | "1.7.0"
5658
5759
description = "plugin $expectedJunitPluginVersion, junit $expectedJUnitJupiterVersion, platform $expectedJUnitPlatformVersion"

src/funcTest/resources/testProjects/junit5kotlin/build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
buildscript {
22
ext.kotlin_version = '1.3.61'
3-
ext.junit5Version = '5.7.0'
4-
ext.junitPlatformVersion = '1.7.0'
3+
ext.junit5Version = '5.10.0'
4+
ext.junitPlatformVersion = '1.10.0'
55

66
repositories {
77
mavenCentral()
@@ -31,7 +31,7 @@ dependencies {
3131
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit5Version"
3232
testImplementation "org.junit.platform:junit-platform-runner:$junitPlatformVersion"
3333

34-
pitest 'org.pitest:pitest-junit5-plugin:1.0.0'
34+
pitest 'org.pitest:pitest-junit5-plugin:1.2.0'
3535
}
3636

3737
compileKotlin {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apply plugin: 'java'
2+
apply plugin: 'info.solidsoft.pitest'
3+
4+
/*
5+
//Local/current version of the plugin should be put on a classpath earlier to override that plugin version
6+
buildscript {
7+
repositories {
8+
mavenCentral()
9+
mavenLocal()
10+
}
11+
dependencies {
12+
classpath 'info.solidsoft.gradle.pitest:gradle-pitest-plugin:X.Y.Z-SNAPSHOT'
13+
}
14+
}
15+
*/
16+
17+
repositories {
18+
mavenCentral()
19+
}
20+
21+
group = "pitest.test"
22+
23+
dependencies {
24+
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.0'
25+
}
26+
27+
test {
28+
useJUnitPlatform()
29+
}
30+
31+
pitest {
32+
junit5PluginVersion = "1.0.0"
33+
}

src/funcTest/resources/testProjects/junit5simple/build.gradle

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,18 @@ repositories {
1818
mavenCentral()
1919
}
2020

21-
dependencies {
22-
// //Not needed, 'junit5PluginVersion' should implicitly add it in requested version
23-
// pitest 'org.pitest:pitest-junit5-plugin:1.0.0'
24-
}
25-
2621
group = "pitest.test"
2722

2823
dependencies {
29-
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.0'
24+
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
3025
}
3126

3227
test {
3328
useJUnitPlatform()
3429
}
3530

3631
pitest {
37-
junit5PluginVersion = "1.0.0"
32+
pitestVersion = "1.14.4"
33+
junit5PluginVersion = "1.2.0" //with no longer shaded junit-platform-launcher
34+
verbose = true //for "ClassNotFoundException: org.junit.platform.launcher.core.LauncherFactory" which should not happen
3835
}

src/main/groovy/info/solidsoft/gradle/pitest/PitestPlugin.groovy

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ import org.gradle.api.GradleException
2323
import org.gradle.api.Plugin
2424
import org.gradle.api.Project
2525
import org.gradle.api.artifacts.Configuration
26+
import org.gradle.api.artifacts.ModuleVersionIdentifier
27+
import org.gradle.api.artifacts.result.ResolutionResult
28+
import org.gradle.api.artifacts.result.ResolvedComponentResult
2629
import org.gradle.api.file.FileCollection
2730
import org.gradle.api.logging.Logger
2831
import org.gradle.api.logging.Logging
@@ -112,6 +115,7 @@ class PitestPlugin implements Plugin<Project> {
112115
extension.fileExtensionsToFilter.set(DEFAULT_FILE_EXTENSIONS_TO_FILTER_FROM_CLASSPATH)
113116
extension.useClasspathFile.set(false)
114117
extension.verbosity.set("NO_SPINNER")
118+
extension.addJUnitPlatformLauncher.set(true)
115119
}
116120

117121
private void failWithMeaningfulErrorMessageOnUnsupportedConfigurationInRootProjectBuildScript() {
@@ -239,6 +243,52 @@ class PitestPlugin implements Plugin<Project> {
239243
dependencies.add(project.dependencies.create(junit5PluginDependencyAsString))
240244
}
241245
}
246+
247+
addJUnitPlatformLauncherDependencyIfNeeded()
248+
}
249+
250+
private void addJUnitPlatformLauncherDependencyIfNeeded() {
251+
Configuration testImplementation = project.configurations.findByName("testImplementation")
252+
testImplementation.withDependencies { directDependencies ->
253+
if (!extension.addJUnitPlatformLauncher.isPresent() || !extension.addJUnitPlatformLauncher.get()) {
254+
log.info("'addJUnitPlatformLauncher' feature explicitly disabled in configuration. " +
255+
"Add junit-platform-launcher manually or expect 'Minion exited abnormally due to UNKNOWN_ERROR' or 'NoClassDefFoundError'")
256+
return
257+
}
258+
259+
//Note: For simplicity, adding also for older pitest-junit5-plugin versions (<1.2.0), which is not needed
260+
261+
final String orgJUnitPlatformGroup = "org.junit.platform"
262+
263+
log.debug("Direct ${testImplementation.name} dependencies (${directDependencies.size()}): ${directDependencies}")
264+
265+
//copy() seems to copy also something that refers to original configuration and generates StackOverflow on getting components
266+
Configuration tmpTestImplementation = project.configurations.maybeCreate("tmpTestImplementation")
267+
directDependencies.each { directDependency ->
268+
tmpTestImplementation.dependencies.add(directDependency)
269+
}
270+
271+
ResolutionResult resolutionResult = tmpTestImplementation.incoming.resolutionResult
272+
Set<ResolvedComponentResult> allResolvedComponents = resolutionResult.allComponents
273+
log.debug("All resolved components ${testImplementation.name} (${allResolvedComponents.size()}): ${allResolvedComponents}")
274+
275+
ResolvedComponentResult foundJunitPlatformComponent = allResolvedComponents.find { ResolvedComponentResult componentResult ->
276+
ModuleVersionIdentifier moduleVersion = componentResult.moduleVersion
277+
return moduleVersion.group == orgJUnitPlatformGroup &&
278+
(moduleVersion.name == "junit-platform-engine" || moduleVersion.name == "junit-platform-commons")
279+
}
280+
281+
if (!foundJunitPlatformComponent) {
282+
log.info("No ${orgJUnitPlatformGroup} components founds in ${testImplementation.name}, junit-platform-launcher will not be added")
283+
return
284+
}
285+
286+
String junitPlatformLauncherDependencyAsString = "${orgJUnitPlatformGroup}:junit-platform-launcher:${foundJunitPlatformComponent.moduleVersion.version}"
287+
log.info("${orgJUnitPlatformGroup} component (${foundJunitPlatformComponent}) found in ${testImplementation.name}, " +
288+
"adding junit-platform-launcher (${junitPlatformLauncherDependencyAsString}) to testRuntimeOnly")
289+
project.configurations.findByName("testRuntimeOnly").dependencies.add(
290+
project.dependencies.create(junitPlatformLauncherDependencyAsString))
291+
}
242292
}
243293

244294
private void suppressPassingDeprecatedTestPluginForNewerPitVersions(PitestTask pitestTask) {

src/main/groovy/info/solidsoft/gradle/pitest/PitestPluginExtension.groovy

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,23 @@ class PitestPluginExtension {
250250
@Incubating
251251
final ListProperty<String> fileExtensionsToFilter
252252

253+
/**
254+
* Adds 'junit-platform-launcher' automatically to the 'testRuntimeOnly' configuration.
255+
*
256+
* Starting with PIT 1.14.0 (with pitest-junit-plugin 1.2.0+) that dependency is no longer shaded and has to be explicitly added to avoid:
257+
* "Minion exited abnormally due to UNKNOWN_ERROR" or "NoClassDefFoundError: org.junit.platform.launcher.core.LauncherFactory".
258+
* This feature is enabled by default if junit-platform is found on the testImplementation classes.
259+
*
260+
* PLEASE NOTE. This feature is experimental and might not work as expected in some corner cases. In that situation, just disable it and add
261+
* required dependency 'junit-platform-launcher' in a proper version to 'testRuntimeOnly' manually.
262+
*
263+
* More information: https://github.com/szpak/gradle-pitest-plugin/issues/337
264+
*
265+
* @since 1.14.0
266+
*/
267+
@Incubating
268+
final Property<Boolean> addJUnitPlatformLauncher
269+
253270
final ReportAggregatorProperties reportAggregatorProperties
254271

255272
PitestPluginExtension(Project project) {
@@ -303,6 +320,7 @@ class PitestPluginExtension {
303320
outputCharset = of.property(Charset)
304321
features = nullListPropertyOf(p, String)
305322
fileExtensionsToFilter = nullListPropertyOf(p, String)
323+
addJUnitPlatformLauncher = of.property(Boolean)
306324
reportAggregatorProperties = new ReportAggregatorProperties(of)
307325
}
308326

0 commit comments

Comments
 (0)