diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy deleted file mode 100644 index abacd4498..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy +++ /dev/null @@ -1,85 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator -import org.apache.logging.log4j.core.config.plugins.processor.PluginCache -import org.apache.tools.zip.ZipOutputStream -import spock.lang.Specification - -import static java.util.Collections.singletonList -import static org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE - -/** - * @author Paul Nelson Baker - * @since 2018-08 - * @see GitHub - * @see LinkedIn - */ -class Log4j2PluginsCacheFileTransformerSpec extends Specification { - - Log4j2PluginsCacheFileTransformer transformer - - void setup() { - transformer = new Log4j2PluginsCacheFileTransformer() - } - - void "should transform for a single file"() { - when: - transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE))) - - then: - transformer.hasTransformedResource() - } - - void "should transform"() { - given: - def relocators = singletonList(new SimpleRelocator()) - - when: - transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators)) - - then: - transformer.hasTransformedResource() - } - - void "relocate classes inside DAT file"() { - given: - String pattern = "org.apache.logging" - String destination = "new.location.org.apache.logging" - - List relocators = singletonList(new SimpleRelocator(pattern, destination)) - - when: - transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators)) - - then: - transformer.hasTransformedResource() - - when: - // Write out to a fake jar file - def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") - def fileOutputStream = new FileOutputStream(testableZipFile) - def bufferedOutputStream = new BufferedOutputStream(fileOutputStream) - def zipOutputStream = new ZipOutputStream(bufferedOutputStream) - - transformer.modifyOutputStream(zipOutputStream, true) - - zipOutputStream.close() - bufferedOutputStream.close() - fileOutputStream.close() - - then: - // Pull the data back out and make sure it was transformed - PluginCache cache = new PluginCache() - def urlString = "jar:" + testableZipFile.toURI().toURL() + "!/" + PLUGIN_CACHE_FILE - cache.loadCacheFiles(Collections.enumeration([new URL(urlString)])) - - cache.getCategory("lookup")["date"].className == "new.location.org.apache.logging.log4j.core.lookup.DateLookup" - - } - - InputStream getResourceStream(String resource) { - return this.class.getClassLoader().getResourceAsStream(resource) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy deleted file mode 100644 index 72a5e40c3..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy -import spock.lang.Unroll - -import static groovy.lang.Closure.IDENTITY - -@Unroll -class PropertiesFileTransformerSpec extends TransformerSpecSupport { - - void "Path #path #transform transformed"() { - given: - Transformer transformer = new PropertiesFileTransformer(objectFactory) - - when: - boolean actual = transformer.canTransformResource(getFileElement(path)) - - then: - actual == expected - - where: - path || expected - 'foo.properties' || true - 'foo/bar.properties' || true - 'foo.props' || false - - transform = expected ? 'can be' : 'can not be' - } - - void exerciseAllTransformConfigurations() { - given: - def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) - transformer.mergeStrategy.set(MergeStrategy.from(mergeStrategy)) - transformer.mergeSeparator.set(mergeSeparator) - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input1)) - transformer.transform(context(path, input2)) - } - - then: - output == toMap(transformer.propertiesEntries[path]) - - where: - path | mergeStrategy | mergeSeparator | input1 | input2 || output - 'f.properties' | 'first' | '' | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - 'f.properties' | 'latest' | '' | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'bar'] - 'f.properties' | 'append' | ',' | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo,bar'] - 'f.properties' | 'append' | ';' | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo;bar'] - } - - void exerciseAllTransformConfigurationsWithPaths() { - given: - def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) - transformer.paths.set(paths) - transformer.mergeStrategy.set(MergeStrategy.from('first')) - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input1)) - transformer.transform(context(path, input2)) - } - - then: - output == toMap(transformer.propertiesEntries[path]) - - where: - path | paths | input1 | input2 || output - 'f.properties' | ['f.properties'] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - 'foo.properties' | ['.*.properties'] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - 'foo.properties' | ['.*bar'] | ['foo': 'foo'] | ['foo': 'bar'] || [:] - 'foo.properties' | [] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - } - - void exerciseAllTransformConfigurationsWithMappings() { - given: - def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) - transformer.mappings.set(mappings) - transformer.mergeStrategy.set(MergeStrategy.from('latest')) - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input1)) - transformer.transform(context(path, input2)) - } - - then: - output == toMap(transformer.propertiesEntries[path]) - - where: - path | mappings | input1 | input2 || output - 'f.properties' | ['f.properties': [mergeStrategy: 'first']] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - 'f.properties' | ['f.properties': [mergeStrategy: 'latest']] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'bar'] - 'f.properties' | ['f.properties': [mergeStrategy: 'append']] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo,bar'] - 'f.properties' | ['f.properties': [mergeStrategy: 'append', mergeSeparator: ';']] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo;bar'] - 'foo.properties' | ['.*.properties': [mergeStrategy: 'first']] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - 'foo.properties' | ['.*bar': [mergeStrategy: 'first']] | ['foo': 'foo'] | ['foo': 'bar'] || [:] - } - - void appliesKeyTransformer() { - given: - def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) - transformer.keyTransformer.set(keyTransformer) - transformer.mergeStrategy.set(MergeStrategy.from('append')) - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input1)) - transformer.transform(context(path, input2)) - } - - then: - output == toMap(transformer.propertiesEntries[path]) - - where: - path | keyTransformer | input1 | input2 || output - 'foo.properties' | IDENTITY | ['foo': 'bar'] | ['FOO': 'baz'] || ['foo': 'bar', 'FOO': 'baz'] - 'foo.properties' | { key -> key.toUpperCase() } | ['foo': 'bar'] | ['FOO': 'baz'] || ['FOO': 'bar,baz'] - 'foo.properties' | { key -> 'bar.' + key.toLowerCase() } | ['foo': 'bar'] | ['FOO': 'baz'] || ['bar.foo': 'bar,baz'] - 'foo.properties' | { key -> key.replaceAll('^(foo)', 'bar.$1') } | ['foo': 'bar'] | ['FOO': 'baz'] || ['bar.foo': 'bar', 'FOO': 'baz'] - } - - void appliesCharset() { - given: - def element = getFileElement(path) - def transformer = new PropertiesFileTransformer(objectFactory) - transformer.charsetName.set(charset) - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input, charset)) - } - - then: - output == toMap(transformer.propertiesEntries[path]) - - where: - path | charset | input || output - 'utf8.properties' | 'utf-8' | ['foo': '传傳磨宿说説'] || ['foo': '传傳磨宿说説'] - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy deleted file mode 100644 index edee227ee..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy +++ /dev/null @@ -1,63 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import spock.lang.Unroll - -@Unroll -class ServiceFileTransformerSpec extends TransformerSpecSupport { - - def "#status path #path #transform transformed"() { - given: - def transformer = new ServiceFileTransformer() - if (exclude) { - transformer.exclude(path) - } - - when: - def actual = transformer.canTransformResource(getFileElement(path)) - - then: - actual == expected - - where: - path | exclude | expected - 'META-INF/services/java.sql.Driver' | false | true - 'META-INF/services/io.dropwizard.logging.AppenderFactory' | false | true - 'META-INF/services/org.apache.maven.Shade' | true | false - 'META-INF/services/foo/bar/moo.goo.Zoo' | false | true - 'foo/bar.properties' | false | false - 'foo.props' | false | false - - transform = expected ? 'can be' : 'can not be' - status = exclude ? 'excluded' : 'non-excluded' - } - - def "transforms service file"() { - given: - def element = getFileElement(path) - def transformer = new ServiceFileTransformer() - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input1)) - transformer.transform(context(path, input2)) - } - - then: - transformer.hasTransformedResource() - output == transformer.serviceEntries[path].toInputStream().text - - where: - path | input1 | input2 || output - 'META-INF/services/com.acme.Foo' | 'foo' | 'bar' || 'foo\nbar' - 'META-INF/services/com.acme.Bar' | 'foo\nbar' | 'zoo' || 'foo\nbar\nzoo' - } - - def "excludes Groovy extension module descriptor files by default"() { - given: - def transformer = new ServiceFileTransformer() - def element = getFileElement('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') - - expect: - !transformer.canTransformResource(element) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy deleted file mode 100644 index 818697e0b..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy +++ /dev/null @@ -1,61 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import org.gradle.api.file.FileTreeElement -import org.gradle.api.file.RelativePath -import org.gradle.api.internal.file.DefaultFileTreeElement -import org.gradle.testfixtures.ProjectBuilder -import spock.lang.Shared -import spock.lang.Specification - -class TransformerSpecSupport extends Specification { - - protected static final def objectFactory = ProjectBuilder.builder().build().objects - - @Shared - ShadowStats stats - - def setup() { - stats = new ShadowStats() - } - - protected static FileTreeElement getFileElement(String path) { - // TODO: this should be replace with `createDefaultFileTreeElement` once this test gets migrated to Kotlin. - return new DefaultFileTreeElement(null, RelativePath.parse(true, path), null, null) - } - - protected static InputStream toInputStream(String str) { - return new ByteArrayInputStream(str.bytes) - } - - protected static InputStream toInputStream(Properties props, String charset) { - ByteArrayOutputStream baos = new ByteArrayOutputStream() - baos.withWriter(charset) { w -> - props.store(w, '') - } - new ByteArrayInputStream(baos.toByteArray()) - } - - protected static Properties toProperties(Map map) { - map.inject(new Properties()) { Properties props, entry -> - props.put(entry.key, entry.value) - props - } - } - - protected static Map toMap(Properties props) { - props.inject([:]) { Map map, entry -> - map.put(entry.key, entry.value) - map - } - } - - protected TransformerContext context(String path, Map input, String charset = 'ISO_8859_1') { - TransformerContext.builder().path(path).inputStream(toInputStream(toProperties(input), charset)).stats(stats).build() - } - - protected TransformerContext context(String path, String input) { - TransformerContext.builder().path(path).inputStream(toInputStream(input)).stats(stats).build() - } - -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy index 23b88c502..81678db82 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy @@ -16,8 +16,6 @@ abstract class PluginSpecification extends Specification { @TempDir Path dir - public static final String SHADOW_VERSION = System.getProperty("shadowVersion") - AppendableMavenFileRepository repo def setup() { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 0871a3a6c..61d774cd6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -1,7 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow.internal +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream import java.io.File +import java.io.FileNotFoundException import java.io.InputStream +import java.nio.charset.Charset +import java.util.Properties import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.file.RelativePath @@ -45,12 +50,23 @@ internal inline fun unsafeLazy(noinline initializer: () -> T): Lazy { return lazy(LazyThreadSafetyMode.NONE, initializer) } +internal fun Properties.inputStream( + charset: Charset = Charsets.ISO_8859_1, + comments: String = "", +): ByteArrayInputStream { + val os = ByteArrayOutputStream() + os.writer(charset).use { writer -> + store(writer, comments) + } + return os.toByteArray().inputStream() +} + internal fun Class<*>.requireResourceAsText(name: String): String { return requireResourceAsStream(name).bufferedReader().readText() } private fun Class<*>.requireResourceAsStream(name: String): InputStream { - return getResourceAsStream(name) ?: error("Resource $name not found.") + return getResourceAsStream(name) ?: throw FileNotFoundException("Resource $name not found.") } private val DummyFile = File("dummy") diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index b09a42921..1e3a80a5d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -1,13 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.CleanProperties +import com.github.jengelman.gradle.plugins.shadow.internal.inputStream import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy import groovy.lang.Closure import groovy.lang.Closure.IDENTITY -import java.io.ByteArrayOutputStream import java.io.InputStream -import java.io.InputStreamReader import java.nio.charset.Charset import java.util.Properties import javax.inject.Inject @@ -102,9 +101,11 @@ import org.gradle.api.tasks.Internal public open class PropertiesFileTransformer @Inject constructor( final override val objectFactory: ObjectFactory, ) : Transformer { - private val propertiesEntries = mutableMapOf() private inline val charset get() = Charset.forName(charsetName.get()) + @get:Internal + internal val propertiesEntries = mutableMapOf() + @get:Input public open val paths: ListProperty = objectFactory.listProperty(String::class.java) @@ -226,7 +227,7 @@ public open class PropertiesFileTransformer @Inject constructor( val entry = ZipEntry(path) entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) os.putNextEntry(entry) - props.toReader().use { + props.inputStream(charset).reader(charset).use { it.copyTo(zipWriter) } zipWriter.flush() @@ -234,14 +235,6 @@ public open class PropertiesFileTransformer @Inject constructor( } } - private fun Properties.toReader(): InputStreamReader { - val os = ByteArrayOutputStream() - os.writer(charset).use { writer -> - store(writer, "") - } - return os.toByteArray().inputStream().reader(charset) - } - public enum class MergeStrategy { First, Latest, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 6d26c2a99..3f8023e64 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -11,6 +11,7 @@ import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.util.PatternFilterable import org.gradle.api.tasks.util.PatternSet @@ -34,7 +35,8 @@ public open class ServiceFileTransformer( .exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN), ) : Transformer, PatternFilterable by patternSet { - private val serviceEntries = mutableMapOf() + @get:Internal + internal val serviceEntries = mutableMapOf() override fun canTransformResource(element: FileTreeElement): Boolean { val target = if (element is ShadowCopyAction.ArchiveFileTreeElement) element.asFileTreeElement() else element diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index bf7c6cf3d..b5c9a070e 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -179,11 +179,13 @@ class SimpleRelocatorTest { .isEqualTo("META-INF/hidden.org.foo.xml") } - private fun SimpleRelocator.relocatePath(path: String): String { - return relocatePath(RelocatePathContext(path)) - } - - private fun SimpleRelocator.relocateClass(className: String): String { - return relocateClass(RelocateClassContext(className)) + private companion object { + fun SimpleRelocator.relocatePath(path: String): String { + return relocatePath(RelocatePathContext(path)) + } + + fun SimpleRelocator.relocateClass(className: String): String { + return relocateClass(RelocateClassContext(className)) + } } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt index 4d3b72b41..3c2782e61 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test /** * Modified from [org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformerTest.java). */ -class ApacheLicenseResourceTransformerTest : TransformerTestSupport() { +class ApacheLicenseResourceTransformerTest : BaseTransformerTest() { init { setupTurkishLocale() @@ -16,11 +16,11 @@ class ApacheLicenseResourceTransformerTest : TransformerTestSupport() { +class ApacheNoticeResourceTransformerTest : BaseTransformerTest() { init { setupTurkishLocale() @@ -17,12 +17,12 @@ class ApacheNoticeResourceTransformerTest : TransformerTestSupport() { +class AppendingTransformerTest : BaseTransformerTest() { init { setupTurkishLocale() @@ -18,8 +18,8 @@ class AppendingTransformerTest : TransformerTestSupport() fun testCanTransformResource() { transformer.resource.set("abcdefghijklmnopqrstuvwxyz") - assertThat(transformer.canTransformResource(getFileElement("abcdefghijklmnopqrstuvwxyz"))).isTrue() - assertThat(transformer.canTransformResource(getFileElement("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))).isTrue() - assertThat(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))).isFalse() + assertThat(transformer.canTransformResource("abcdefghijklmnopqrstuvwxyz")).isTrue() + assertThat(transformer.canTransformResource("ABCDEFGHIJKLMNOPQRSTUVWXYZ")).isTrue() + assertThat(transformer.canTransformResource("META-INF/MANIFEST.MF")).isFalse() } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt similarity index 79% rename from src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt rename to src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 1acc05f10..18b0c4dbc 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -1,8 +1,10 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Companion.create import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory +import java.io.FileNotFoundException import java.io.InputStream import java.lang.reflect.ParameterizedType import java.nio.file.Path @@ -11,11 +13,10 @@ import java.util.zip.ZipFile import kotlin.io.path.createTempFile import kotlin.io.path.outputStream import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath import org.junit.jupiter.api.BeforeEach -abstract class TransformerTestSupport { +abstract class BaseTransformerTest { protected lateinit var transformer: T private set @@ -23,7 +24,7 @@ abstract class TransformerTestSupport { get() = TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME)) protected fun requireResourceAsStream(name: String): InputStream { - return this::class.java.classLoader.getResourceAsStream(name) ?: error("Resource $name not found.") + return this::class.java.classLoader.getResourceAsStream(name) ?: throw FileNotFoundException("Resource $name not found.") } @BeforeEach @@ -35,9 +36,11 @@ abstract class TransformerTestSupport { protected companion object { const val MANIFEST_NAME: String = "META-INF/MANIFEST.MF" + val sharedStats = ShadowStats() - fun getFileElement(path: String): FileTreeElement { - return createDefaultFileTreeElement(relativePath = RelativePath.parse(true, path)) + fun Transformer.canTransformResource(path: String): Boolean { + val element = createDefaultFileTreeElement(relativePath = RelativePath.parse(true, path)) + return canTransformResource(element) } fun readFrom(jarPath: Path, resourceName: String = MANIFEST_NAME): List { @@ -59,7 +62,7 @@ abstract class TransformerTestSupport { } /** - * NOTE: The Turkish locale has an usual case transformation for the letters "I" and "i", making it a prime + * NOTE: The Turkish locale has a usual case transformation for the letters "I" and "i", making it a prime * choice to test for improper case-less string comparisons. */ fun setupTurkishLocale() { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt index d419e84e1..43640d449 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test /** * Modified from [org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformerTest.java). */ -class ComponentsXmlResourceTransformerTest : TransformerTestSupport() { +class ComponentsXmlResourceTransformerTest : BaseTransformerTest() { @Test fun testConfigurationMerging() { XMLUnit.setNormalizeWhitespace(true) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt new file mode 100644 index 000000000..48a4b1ff3 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -0,0 +1,55 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import java.io.File +import java.net.URL +import java.util.Collections +import org.apache.logging.log4j.core.config.plugins.processor.PluginCache +import org.apache.tools.zip.ZipOutputStream +import org.junit.jupiter.api.Test + +class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest() { + @Test + fun `should transform`() { + transformer.transform(context(SimpleRelocator())) + assertThat(transformer.hasTransformedResource()).isTrue() + } + + @Test + fun `should transform for a single file`() { + transformer.transform(context()) + assertThat(transformer.hasTransformedResource()).isTrue() + } + + @Test + fun `relocate classes inside DAT file`() { + val relocator = SimpleRelocator("org.apache.logging", "new.location.org.apache.logging") + transformer.transform(context(relocator)) + assertThat(transformer.hasTransformedResource()).isTrue() + + // Write out to a fake jar file + val testableZipFile = File.createTempFile("testable-zip-file-", ".jar") + ZipOutputStream(testableZipFile.outputStream().buffered()).use { zipOutputStream -> + transformer.modifyOutputStream(zipOutputStream, true) + } + + // Pull the data back out and make sure it was transformed + val cache = PluginCache() + val urlString = "jar:" + testableZipFile.toURI().toURL() + "!/" + PLUGIN_CACHE_FILE + cache.loadCacheFiles(Collections.enumeration(listOf(URL(urlString)))) + + assertThat(cache.getCategory("lookup")["date"]?.className) + .isEqualTo("new.location.org.apache.logging.log4j.core.lookup.DateLookup") + } + + private fun context(vararg relocator: SimpleRelocator): TransformerContext { + return TransformerContext(PLUGIN_CACHE_FILE, requireResourceAsStream(PLUGIN_CACHE_FILE), relocator.toList()) + } + + private companion object { + const val PLUGIN_CACHE_FILE = "META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat" + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt index c36db4cc0..e699711a7 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt @@ -8,7 +8,7 @@ import assertk.assertions.isNotEmpty import assertk.assertions.isTrue import org.junit.jupiter.api.Test -class ManifestAppenderTransformerTest : TransformerTestSupport() { +class ManifestAppenderTransformerTest : BaseTransformerTest() { @Test fun testCanTransformResource() { with(transformer) { @@ -16,8 +16,8 @@ class ManifestAppenderTransformerTest : TransformerTestSupport() { +class PropertiesFileTransformerTest : BaseTransformerTest() { @Test fun testHasTransformedResource() { transformer.transform(manifestTransformerContext) @@ -46,4 +54,282 @@ class PropertiesFileTransformerTest : TransformerTestSupport, + input2: Map, + expectedOutput: Map, + ) { + transformer.mergeStrategy.set(MergeStrategy.from(mergeStrategy)) + transformer.mergeSeparator.set(mergeSeparator) + + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input1)) + transformer.transform(context(path, input2)) + } + + assertThat(transformer.propertiesEntries[path].orEmpty()).isEqualTo(expectedOutput) + } + + @ParameterizedTest(name = "Paths={1}") + @MethodSource("transformConfigurationsWithPathsProvider") + fun exerciseAllTransformConfigurationsWithPaths( + path: String, + paths: List, + input1: Map, + input2: Map, + expectedOutput: Map, + ) { + transformer.paths.set(paths) + transformer.mergeStrategy.set(MergeStrategy.First) + + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input1)) + transformer.transform(context(path, input2)) + } + + assertThat(transformer.propertiesEntries[path].orEmpty()).isEqualTo(expectedOutput) + } + + @ParameterizedTest(name = "Mappings={1}") + @MethodSource("transformConfigurationsWithMappingsProvider") + fun exerciseAllTransformConfigurationsWithMappings( + path: String, + mappings: Map>, + input1: Map, + input2: Map, + expectedOutput: Map, + ) { + transformer.mappings.set(mappings) + transformer.mergeStrategy.set(MergeStrategy.Latest) + + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input1)) + transformer.transform(context(path, input2)) + } + + assertThat(transformer.propertiesEntries[path].orEmpty()).isEqualTo(expectedOutput) + } + + @ParameterizedTest(name = "KeyTransformer: {1}") + @MethodSource("appliesKeyTransformerProvider") + fun appliesKeyTransformer( + path: String, + keyTransformer: (String) -> String, + input1: Map, + input2: Map, + expectedOutput: Map, + ) { + transformer.mergeStrategy.set(MergeStrategy.Append) + transformer.keyTransformer.set(object : Closure(null) { + override fun call(vararg arguments: Any?): String { + return keyTransformer.invoke(arguments.first() as String) + } + }) + + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input1)) + transformer.transform(context(path, input2)) + } + + assertThat(transformer.propertiesEntries[path].orEmpty()).isEqualTo(expectedOutput) + } + + @ParameterizedTest(name = "Charset: {1}") + @MethodSource("appliesCharsetProvider") + fun appliesCharset( + path: String, + charset: String, + input: Map, + expectedOutput: Map, + ) { + transformer.charsetName.set(charset) + + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input, Charset.forName(charset))) + } + + assertThat(transformer.propertiesEntries[path].orEmpty()).isEqualTo(expectedOutput) + } + + private companion object { + fun context(path: String, input: Map, charset: Charset = Charsets.ISO_8859_1): TransformerContext { + val properties = Properties().apply { putAll(input) } + return TransformerContext(path, properties.inputStream(charset), stats = sharedStats) + } + + @JvmStatic + fun pathProvider() = listOf( + Arguments.of("foo.properties", true, "can be"), + Arguments.of("foo/bar.properties", true, "can be"), + Arguments.of("foo.props", false, "can not be"), + ) + + @JvmStatic + fun appliesCharsetProvider() = listOf( + Arguments.of( + "utf8.properties", + "utf-8", + mapOf("foo" to "传傳磨宿说説"), + mapOf("foo" to "传傳磨宿说説"), + ), + ) + + @JvmStatic + fun transformConfigurationsWithPathsProvider() = listOf( + Arguments.of( + "f.properties", + listOf("f.properties"), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "foo.properties", + listOf(".*.properties"), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "foo.properties", + listOf(".*bar"), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + emptyMap(), + ), + Arguments.of( + "foo.properties", + emptyList(), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + ) + + @JvmStatic + fun transformConfigurationsWithMappingsProvider() = listOf( + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "first")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "latest")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "bar"), + ), + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "append")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo,bar"), + ), + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "append", "mergeSeparator" to ";")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo;bar"), + ), + Arguments.of( + "foo.properties", + mapOf(".*.properties" to mapOf("mergeStrategy" to "first")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "foo.properties", + mapOf(".*bar" to mapOf("mergeStrategy" to "first")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + emptyMap(), + ), + ) + + @JvmStatic + fun transformConfigurationsProvider() = listOf( + Arguments.of( + "f.properties", + "first", + "", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "f.properties", + "latest", + "", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "bar"), + ), + Arguments.of( + "f.properties", + "append", + ",", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo,bar"), + ), + Arguments.of( + "f.properties", + "append", + ";", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo;bar"), + ), + ) + + @JvmStatic + fun appliesKeyTransformerProvider() = listOf( + Arguments.of( + "foo.properties", + { key: String -> key }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("foo" to "bar", "FOO" to "baz"), + ), + Arguments.of( + "foo.properties", + { key: String -> key.uppercase() }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("FOO" to "bar,baz"), + ), + Arguments.of( + "foo.properties", + { key: String -> "bar.${key.lowercase()}" }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("bar.foo" to "bar,baz"), + ), + Arguments.of( + "foo.properties", + { key: String -> key.replaceFirst(Regex("^(foo)"), "bar.$1") }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("bar.foo" to "bar", "FOO" to "baz"), + ), + ) + } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt new file mode 100644 index 000000000..55bec8828 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -0,0 +1,64 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +class ServiceFileTransformerTest : BaseTransformerTest() { + @ParameterizedTest(name = "{index} => path={0}, exclude={1}, expected={2}") + @MethodSource("canTransformResourceData") + fun testCanTransformResource(path: String, exclude: Boolean, expected: Boolean) { + if (exclude) { + transformer.exclude(path) + } + assertThat(transformer.canTransformResource(path)).isEqualTo(expected) + } + + @ParameterizedTest(name = "{index} => path={0}") + @MethodSource("transformsServiceFileData") + fun `test transforms service file`(path: String, input1: String, input2: String, output: String) { + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input1)) + transformer.transform(context(path, input2)) + } + + assertThat(transformer.hasTransformedResource()).isTrue() + assertThat(transformer.serviceEntries.getValue(path).toInputStream().bufferedReader().readText()) + .isEqualTo(output) + } + + @Test + fun `test excludes Groovy extension module descriptor files by default`() { + val element = "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" + assertThat(transformer.canTransformResource(element)).isFalse() + } + + private companion object { + fun context(path: String, input: String): TransformerContext { + return TransformerContext(path, input.byteInputStream(), stats = sharedStats) + } + + @JvmStatic + fun canTransformResourceData() = listOf( + // path, exclude, expected + Arguments.of("META-INF/services/java.sql.Driver", false, true), + Arguments.of("META-INF/services/io.dropwizard.logging.AppenderFactory", false, true), + Arguments.of("META-INF/services/org.apache.maven.Shade", true, false), + Arguments.of("META-INF/services/foo/bar/moo.goo.Zoo", false, true), + Arguments.of("foo/bar.properties", false, false), + Arguments.of("foo.props", false, false), + ) + + @JvmStatic + fun transformsServiceFileData() = listOf( + // path, input1, input2, output + Arguments.of("META-INF/services/com.acme.Foo", "foo", "bar", "foo\nbar"), + Arguments.of("META-INF/services/com.acme.Bar", "foo\nbar", "zoo", "foo\nbar\nzoo"), + ) + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index b9eddbf31..eddd1a2d0 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -5,7 +5,7 @@ import assertk.assertions.isFalse import assertk.assertions.isTrue import org.junit.jupiter.api.Test -class XmlAppendingTransformerTest : TransformerTestSupport() { +class XmlAppendingTransformerTest : BaseTransformerTest() { init { setupTurkishLocale() @@ -15,8 +15,8 @@ class XmlAppendingTransformerTest : TransformerTestSupport