Skip to content

Commit ffc0064

Browse files
committed
Add RelocatorRemapperTest
1 parent 02756fb commit ffc0064

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package com.github.jengelman.gradle.plugins.shadow.internal
2+
3+
import assertk.assertThat
4+
import assertk.assertions.contains
5+
import assertk.assertions.isEqualTo
6+
import assertk.assertions.isTrue
7+
import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator
8+
import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsPath
9+
import com.github.jengelman.gradle.plugins.shadow.util.noOpDelegate
10+
import java.io.File
11+
import java.lang.classfile.Attributes
12+
import java.lang.classfile.ClassFile
13+
import java.nio.file.Path
14+
import kotlin.io.path.copyTo
15+
import kotlin.io.path.createParentDirectories
16+
import kotlin.reflect.KClass
17+
import org.gradle.api.file.FileCopyDetails
18+
import org.junit.jupiter.api.Test
19+
import org.junit.jupiter.api.io.TempDir
20+
21+
class RelocatorRemapperTest {
22+
@TempDir lateinit var tempDir: Path
23+
24+
// Relocator used across all relocation tests: moves the test package to a distinct target.
25+
private val relocators =
26+
setOf(
27+
SimpleRelocator(
28+
"com.github.jengelman.gradle.plugins.shadow.internal",
29+
"com.example.relocated",
30+
)
31+
)
32+
33+
// Internal name of the relocated FixtureBase for use in assertions.
34+
private val relocatedFixtureBase = $$"com/example/relocated/RelocatorRemapperTest$FixtureBase"
35+
36+
@Test
37+
fun remapClassNotModified() {
38+
val details = FixtureSubject::class.toFileCopyDetails()
39+
// Relocator pattern does not match – original bytes must be returned as-is.
40+
val noMatchRelocators = setOf(SimpleRelocator("org.unrelated", "org.other"))
41+
42+
val result = details.remapClass(noMatchRelocators)
43+
44+
assertThat(result.contentEquals(details.file.readBytes())).isTrue()
45+
}
46+
47+
@Test
48+
fun remapClassNameIsRelocated() {
49+
val details = FixtureSubject::class.toFileCopyDetails()
50+
51+
val result = details.remapClass(relocators)
52+
53+
val classModel = ClassFile.of().parse(result)
54+
assertThat(classModel.thisClass().asInternalName())
55+
.isEqualTo($$"com/example/relocated/RelocatorRemapperTest$FixtureSubject")
56+
}
57+
58+
@Test
59+
fun remapSuperclassIsRelocated() {
60+
val details = FixtureSubject::class.toFileCopyDetails()
61+
62+
val result = details.remapClass(relocators)
63+
64+
val classModel = ClassFile.of().parse(result)
65+
assertThat(classModel.superclass().get().asInternalName()).isEqualTo(relocatedFixtureBase)
66+
}
67+
68+
@Test
69+
fun remapFieldDescriptorIsRelocated() {
70+
val details = FixtureSubject::class.toFileCopyDetails()
71+
72+
val result = details.remapClass(relocators)
73+
74+
val classModel = ClassFile.of().parse(result)
75+
val fieldDescriptors = classModel.fields().map { it.fieldType().stringValue() }
76+
assertThat(fieldDescriptors).contains("L$relocatedFixtureBase;")
77+
}
78+
79+
@Test
80+
fun remapMethodDescriptorIsRelocated() {
81+
val details = FixtureSubject::class.toFileCopyDetails()
82+
83+
val result = details.remapClass(relocators)
84+
85+
val classModel = ClassFile.of().parse(result)
86+
val methodDescriptors = classModel.methods().map { it.methodType().stringValue() }
87+
assertThat(methodDescriptors).contains("(L$relocatedFixtureBase;)L$relocatedFixtureBase;")
88+
}
89+
90+
@Test
91+
fun remapAnnotationIsRelocated() {
92+
val details = FixtureSubject::class.toFileCopyDetails()
93+
94+
val result = details.remapClass(relocators)
95+
96+
val classModel = ClassFile.of().parse(result)
97+
val annotationsAttr = classModel.findAttribute(Attributes.runtimeVisibleAnnotations())
98+
assertThat(annotationsAttr.isPresent).isTrue()
99+
val annotationDescriptors =
100+
annotationsAttr.get().annotations().map { it.className().stringValue() }
101+
assertThat(annotationDescriptors)
102+
.contains($$"Lcom/example/relocated/RelocatorRemapperTest$FixtureAnnotation;")
103+
}
104+
105+
@Test
106+
fun remapBaseClassNameIsRelocated() {
107+
// Verify relocation also works on a simple class (FixtureBase has no fields/methods
108+
// referencing the target package beyond its own class name).
109+
val details = FixtureBase::class.toFileCopyDetails()
110+
111+
val result = details.remapClass(relocators)
112+
113+
val classModel = ClassFile.of().parse(result)
114+
assertThat(classModel.thisClass().asInternalName()).isEqualTo(relocatedFixtureBase)
115+
}
116+
117+
private fun KClass<*>.toFileCopyDetails() =
118+
object : FileCopyDetails by noOpDelegate() {
119+
private val _path = java.name.replace('.', '/') + ".class"
120+
private val _file =
121+
tempDir
122+
.resolve(_path)
123+
.createParentDirectories()
124+
.also { requireResourceAsPath(_path).copyTo(it) }
125+
.toFile()
126+
127+
override fun getPath(): String = _path
128+
129+
override fun getFile(): File = _file
130+
}
131+
132+
// ---------------------------------------------------------------------------
133+
// Fixture classes – declared as nested classes so their bytecode is compiled
134+
// into the test output directory and can be fetched via requireResourceAsPath.
135+
// ---------------------------------------------------------------------------
136+
137+
@Retention(AnnotationRetention.RUNTIME)
138+
@Target(AnnotationTarget.CLASS)
139+
annotation class FixtureAnnotation
140+
141+
open class FixtureBase
142+
143+
@Suppress("unused") // Used by parsing bytecode.
144+
@FixtureAnnotation
145+
class FixtureSubject : FixtureBase() {
146+
val field: FixtureBase = FixtureBase()
147+
148+
fun method(arg: FixtureBase): FixtureBase = arg
149+
}
150+
}

0 commit comments

Comments
 (0)