Repository: GradleUp/shadow
Pull request #1152 (2025-01-13 02:17:20)

Polish Shadow plugin tests
Follow up #1117.


Patch:
diff --git a/build.gradle.kts b/build.gradle.kts
index 9fa451fd..ff1cb6d9 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -100,7 +100,6 @@ testing.suites {
     dependencies {
       // Seems we can't ref project() here due to some limitations of rootProject.
       implementation(sourceSets.main.get().output)
-      implementation(libs.apache.ant)
       implementation(libs.apache.maven.modelBuilder)
       implementation(libs.moshi)
       implementation(libs.moshi.kotlin)


Test patch:
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt
similarity index 53%
rename from src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt
rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt
index 8f3ea3e9..720df401 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt
@@ -3,19 +3,24 @@ package com.github.jengelman.gradle.plugins.shadow
 import assertk.all
 import assertk.assertThat
 import assertk.assertions.contains
-import assertk.assertions.containsAtLeast
+import assertk.assertions.containsOnly
 import assertk.assertions.exists
 import assertk.assertions.isEqualTo
+import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME
+import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME
+import com.github.jengelman.gradle.plugins.shadow.util.JarPath
 import com.github.jengelman.gradle.plugins.shadow.util.containsEntries
+import com.github.jengelman.gradle.plugins.shadow.util.getContent
 import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr
-import com.github.jengelman.gradle.plugins.shadow.util.isRegular
+import com.github.jengelman.gradle.plugins.shadow.util.getStream
+import java.util.zip.ZipFile
 import kotlin.io.path.appendText
+import kotlin.io.path.outputStream
 import kotlin.io.path.readText
 import kotlin.io.path.writeText
-import org.apache.tools.zip.ZipFile
 import org.junit.jupiter.api.Test
 
-class ApplicationTest : BasePluginTest() {
+class ApplicationPluginTest : BasePluginTest() {
   @Test
   fun integrationWithApplicationPluginAndJavaToolchains() {
     prepare(
@@ -26,7 +31,7 @@ class ApplicationTest : BasePluginTest() {
       """.trimIndent(),
       settingsBlock = """
         plugins {
-          id('org.gradle.toolchains.foojay-resolver-convention') version '0.7.0'
+          id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0'
         }
       """.trimIndent(),
       runShadowBlock = """
@@ -36,21 +41,14 @@ class ApplicationTest : BasePluginTest() {
       """.trimIndent(),
     )
 
-    val result = run(runShadowTask)
+    val result = run(SHADOW_RUN_TASK_NAME)
 
     assertThat(result.output).contains(
       "Running application with JDK 17",
       "TestApp: Hello World! (foo)",
     )
 
-    assertThat(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")).useAll {
-      containsEntries(
-        "a.properties",
-        "a2.properties",
-        "myapp/Main.class",
-      )
-      getMainAttr("Main-Class").isEqualTo("myapp.Main")
-    }
+    commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar"))
 
     assertThat(path("build/install/myapp-shadow/bin/myapp")).all {
       exists()
@@ -71,37 +69,49 @@ class ApplicationTest : BasePluginTest() {
   @Test
   fun shadowApplicationDistributionsShouldUseShadowJar() {
     prepare(
-      projectBlock = """
-        dependencies {
-           shadow 'shadow:a:1.0'
-        }
-      """.trimIndent(),
+      dependenciesBlock = "shadow 'shadow:a:1.0'",
     )
 
     run("shadowDistZip")
 
-    val zip = path("build/distributions/myapp-shadow-1.0.zip")
-    assertThat(zip).exists()
+    ZipFile(path("build/distributions/myapp-shadow-1.0.zip").toFile()).use { zip ->
+      val fileEntries = zip.entries().toList().map { it.name }.filter { !it.endsWith("/") }
+      assertThat(fileEntries).containsOnly(
+        "myapp-shadow-1.0/bin/myapp",
+        "myapp-shadow-1.0/bin/myapp.bat",
+        "myapp-shadow-1.0/lib/myapp-1.0-all.jar",
+        "myapp-shadow-1.0/lib/a-1.0.jar",
+      )
 
-    val entries = ZipFile(zip.toFile()).use { it.entries }.toList().map { it.name }
-    assertThat(entries).containsAtLeast(
-      "myapp-shadow-1.0/lib/myapp-1.0-all.jar",
-      "myapp-shadow-1.0/lib/a-1.0.jar",
-    )
+      val extractedJar = path("extracted/myapp-1.0-all.jar")
+      zip.getStream("myapp-shadow-1.0/lib/myapp-1.0-all.jar")
+        .use { it.copyTo(extractedJar.outputStream()) }
+      commonAssertions(JarPath(extractedJar), entriesContained = arrayOf("myapp/Main.class"))
+
+      assertThat(zip.getContent("myapp-shadow-1.0/bin/myapp")).contains(
+        "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar",
+        "-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"",
+        "exec \"\$JAVACMD\" \"\$@\"",
+      )
+      assertThat(zip.getContent("myapp-shadow-1.0/bin/myapp.bat")).contains(
+        "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar",
+      )
+    }
   }
 
   @Test
   fun installShadowDoesNotExecuteDependentShadowTask() {
     prepare()
 
-    run(ShadowApplicationPlugin.SHADOW_INSTALL_TASK_NAME)
+    run(SHADOW_INSTALL_TASK_NAME)
 
-    assertThat(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")).isRegular()
+    commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar"))
   }
 
   private fun prepare(
     projectBlock: String = "",
     settingsBlock: String = "",
+    dependenciesBlock: String = "implementation 'shadow:a:1.0'",
     runShadowBlock: String = "",
   ) {
     path("src/main/java/myapp/Main.java").appendText(
@@ -122,7 +132,7 @@ class ApplicationTest : BasePluginTest() {
           mainClass = 'myapp.Main'
         }
         dependencies {
-          implementation 'shadow:a:1.0'
+          $dependenciesBlock
         }
         $runShadow {
           args 'foo'
@@ -137,4 +147,18 @@ class ApplicationTest : BasePluginTest() {
       ),
     )
   }
+
+  private fun commonAssertions(
+    jarPath: JarPath,
+    entriesContained: Array<String> = arrayOf("a.properties", "a2.properties", "myapp/Main.class"),
+  ) {
+    assertThat(jarPath).useAll {
+      containsEntries(*entriesContained)
+      getMainAttr("Main-Class").isEqualTo("myapp.Main")
+    }
+  }
+
+  private companion object {
+    val runShadow = "tasks.named('$SHADOW_RUN_TASK_NAME')".trim()
+  }
 }
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt
index 2564ddfa..1eabb117 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt
@@ -6,9 +6,7 @@ import assertk.assertThat
 import assertk.assertions.doesNotContain
 import assertk.assertions.isEqualTo
 import assertk.assertions.isNotNull
-import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME
 import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME
-import com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec
 import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
 import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
 import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository
@@ -81,7 +79,6 @@ abstract class BasePluginTest {
   }
 
   open val shadowJarTask = ":$SHADOW_JAR_TASK_NAME"
-  open val runShadowTask = ":$SHADOW_RUN_TASK_NAME"
   val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME"
 
   val projectScriptPath: Path get() = path("build.gradle")
@@ -323,10 +320,6 @@ abstract class BasePluginTest {
       tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name})
     """.trimIndent()
 
-    val runShadow = """
-      tasks.named('$SHADOW_RUN_TASK_NAME', ${JavaJarExec::class.java.name})
-    """.trimIndent()
-
     val commonArguments = listOf(
       "--warning-mode=fail",
       "--configuration-cache",
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt
similarity index 96%
rename from src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt
rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt
index 94361f32..2171debe 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt
@@ -29,7 +29,7 @@ import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.condition.DisabledForJreRange
 import org.junit.jupiter.api.condition.JRE
 
-class ShadowPluginTest : BasePluginTest() {
+class JavaPluginTest : BasePluginTest() {
   @Test
   fun applyPlugin() {
     val projectName = "my-shadow"
@@ -638,40 +638,6 @@ class ShadowPluginTest : BasePluginTest() {
     assertThat(outputShadowJar).isRegular()
   }
 
-  @Issue(
-    "https://github.com/GradleUp/shadow/issues/609",
-  )
-  @Test
-  fun doesNotErrorWhenUsingApplicationMainClassProperty() {
-    projectScriptPath.appendText(
-      """
-        apply plugin: 'application'
-
-        application {
-          mainClass = 'myapp.Main'
-        }
-        $runShadow {
-          args 'foo'
-        }
-      """.trimIndent(),
-    )
-
-    path("src/main/java/myapp/Main.java").writeText(
-      """
-        package myapp;
-        public class Main {
-          public static void main(String[] args) {
-            System.out.println("TestApp: Hello World! (" + args[0] + ")");
-          }
-        }
-      """.trimIndent(),
-    )
-
-    val result = run(runShadowTask)
-
-    assertThat(result.output).contains("TestApp: Hello World! (foo)")
-  }
-
   @Issue(
     "https://github.com/GradleUp/shadow/issues/459",
     "https://github.com/GradleUp/shadow/issues/852",
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt
index 04807f7f..a32a8aa9 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt
@@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers
 
 import assertk.assertThat
 import assertk.assertions.isEqualTo
+import com.github.jengelman.gradle.plugins.shadow.util.getContent
 import kotlin.io.path.appendText
 import org.junit.jupiter.api.Test
 
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt
index c373895f..a13a43db 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt
@@ -10,6 +10,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionMo
 import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MERGED_MODULE_VERSION
 import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR
 import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR
+import com.github.jengelman.gradle.plugins.shadow.util.getContent
 import java.nio.file.Path
 import kotlin.io.path.appendText
 import org.junit.jupiter.api.Test
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt
index dded3008..41f21525 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt
@@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers
 
 import assertk.assertThat
 import assertk.assertions.isEqualTo
+import com.github.jengelman.gradle.plugins.shadow.util.getContent
 import kotlin.io.path.appendText
 import org.junit.jupiter.api.Test
 
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt
index 9e10f339..ec66a913 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt
@@ -4,8 +4,10 @@ import assertk.Assert
 import assertk.all
 import assertk.assertions.isNotEmpty
 import assertk.fail
+import java.io.InputStream
 import java.nio.file.Path
 import java.util.jar.JarFile
+import java.util.zip.ZipFile
 
 /**
  * A wrapper for [JarFile] that also implements [Path].
@@ -20,11 +22,15 @@ class JarPath(val path: Path) :
   fun getMainAttr(name: String): String? {
     return manifest.mainAttributes.getValue(name)
   }
+}
 
-  fun getContent(entryName: String): String {
-    val entry = getEntry(entryName) ?: error("Entry not found: $entryName")
-    return getInputStream(entry).bufferedReader().use { it.readText() }
-  }
+fun ZipFile.getContent(entryName: String): String {
+  return getStream(entryName).bufferedReader().use { it.readText() }
+}
+
+fun ZipFile.getStream(entryName: String): InputStream {
+  val entry = getEntry(entryName) ?: error("Entry not found: $entryName")
+  return getInputStream(entry)
 }
 
 /**