Skip to content

Commit 26ccb74

Browse files
Refactor to Kotlin
1 parent 8d39a46 commit 26ccb74

File tree

2 files changed

+151
-140
lines changed

2 files changed

+151
-140
lines changed
Lines changed: 102 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,148 +1,158 @@
11
package datadog.gradle.plugin.csi
22

3-
import java.nio.file.Files
43
import org.gradle.testkit.runner.BuildResult
54
import org.gradle.testkit.runner.GradleRunner
65
import org.gradle.testkit.runner.UnexpectedBuildFailure
7-
import spock.lang.Specification
8-
import spock.lang.TempDir
9-
10-
class CallSiteInstrumentationPluginTest extends Specification {
6+
import org.junit.jupiter.api.Assertions.assertFalse
7+
import org.junit.jupiter.api.Assertions.assertThrows
8+
import org.junit.jupiter.api.Assertions.assertTrue
9+
import org.junit.jupiter.api.Test
10+
import org.junit.jupiter.api.io.TempDir
11+
import java.io.File
12+
import java.nio.file.Files
1113

12-
def buildGradle = '''
14+
class CallSiteInstrumentationPluginTest {
15+
private val buildGradle = """
1316
plugins {
1417
id 'java'
1518
id 'call-site-instrumentation'
1619
}
17-
20+
1821
sourceCompatibility = JavaVersion.VERSION_1_8
1922
targetCompatibility = JavaVersion.VERSION_1_8
2023
2124
csi {
2225
suffix = 'CallSite'
2326
targetFolder = project.layout.buildDirectory.dir('csi')
24-
rootFolder = file('$$ROOT_FOLDER$$')
27+
rootFolder = file('__ROOT_FOLDER__')
2528
}
26-
29+
2730
repositories {
2831
mavenCentral()
2932
}
30-
33+
3134
dependencies {
3235
implementation group: 'net.bytebuddy', name: 'byte-buddy', version: '1.18.1'
3336
implementation group: 'com.google.auto.service', name: 'auto-service-annotations', version: '1.1.1'
3437
}
35-
'''
38+
""".trimIndent()
3639

3740
@TempDir
38-
File buildDir
39-
40-
def 'test call site instrumentation plugin'() {
41-
setup:
42-
createGradleProject(buildDir, buildGradle, '''
43-
import datadog.trace.agent.tooling.csi.*;
44-
45-
@CallSite(spi = CallSites.class)
46-
public class BeforeAdviceCallSite {
47-
@CallSite.Before("java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)")
48-
public static void beforeAppend(@CallSite.This final StringBuilder self, @CallSite.Argument final String param) {
49-
}
50-
}
51-
''')
52-
53-
when:
54-
final result = buildGradleProject(buildDir)
55-
56-
then:
57-
final generated = resolve(buildDir, 'build', 'csi', 'BeforeAdviceCallSites.java')
58-
generated.exists()
59-
60-
final output = result.output
61-
!output.contains('[⨉]')
62-
output.contains('[✓] @CallSite BeforeAdviceCallSite')
41+
lateinit var buildDir: File
42+
43+
@Test
44+
fun `test call site instrumentation plugin`() {
45+
createGradleProject(
46+
buildDir, buildGradle,
47+
"""
48+
import datadog.trace.agent.tooling.csi.*;
49+
50+
@CallSite(spi = CallSites.class)
51+
public class BeforeAdviceCallSite {
52+
@CallSite.Before("java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)")
53+
public static void beforeAppend(@CallSite.This final StringBuilder self, @CallSite.Argument final String param) {
54+
}
55+
}
56+
""".trimIndent()
57+
)
58+
59+
val result = buildGradleProject(buildDir)
60+
61+
val generated = resolve(buildDir, "build", "csi", "BeforeAdviceCallSites.java")
62+
assertTrue(generated.exists())
63+
64+
val output = result.output
65+
assertFalse(output.contains("[⨉]"))
66+
assertTrue(output.contains("[✓] @CallSite BeforeAdviceCallSite"))
6367
}
6468

65-
def 'test call site instrumentation plugin with error'() {
66-
setup:
67-
createGradleProject(buildDir, buildGradle, '''
68-
import datadog.trace.agent.tooling.csi.*;
69-
70-
@CallSite(spi = CallSites.class)
71-
public class BeforeAdviceCallSite {
72-
@CallSite.Before("java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)")
73-
private void beforeAppend(@CallSite.This final StringBuilder self, @CallSite.Argument final String param) {
74-
}
75-
}
76-
''')
77-
78-
when:
79-
buildGradleProject(buildDir)
80-
81-
then:
82-
final error = thrown(UnexpectedBuildFailure)
83-
84-
final generated = resolve(buildDir, 'build', 'csi', 'BeforeAdviceCallSites.java')
85-
!generated.exists()
86-
87-
final output = error.message
88-
!output.contains('[✓]')
89-
output.contains('ADVICE_METHOD_NOT_STATIC_AND_PUBLIC')
69+
@Test
70+
fun `test call site instrumentation plugin with error`() {
71+
createGradleProject(
72+
buildDir, buildGradle,
73+
"""
74+
import datadog.trace.agent.tooling.csi.*;
75+
76+
@CallSite(spi = CallSites.class)
77+
public class BeforeAdviceCallSite {
78+
@CallSite.Before("java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)")
79+
private void beforeAppend(@CallSite.This final StringBuilder self, @CallSite.Argument final String param) {
80+
}
81+
}
82+
""".trimIndent()
83+
)
84+
85+
val error = assertThrows(UnexpectedBuildFailure::class.java) {
86+
buildGradleProject(buildDir)
87+
}
88+
89+
val generated = resolve(buildDir, "build", "csi", "BeforeAdviceCallSites.java")
90+
assertFalse(generated.exists())
91+
92+
val output = error.message ?: ""
93+
assertFalse(output.contains("[✓]"))
94+
assertTrue(output.contains("ADVICE_METHOD_NOT_STATIC_AND_PUBLIC"))
9095
}
9196

92-
private static void createGradleProject(final File buildDir, final String gradleFile, final String advice) {
93-
final projectFolder = new File(System.getProperty('user.dir')).parentFile
94-
final callSiteJar = resolve(projectFolder, 'buildSrc', 'call-site-instrumentation-plugin', 'build', 'libs', 'call-site-instrumentation-plugin-all.jar')
95-
final testCallSiteJarDir = resolve(buildDir, 'buildSrc', 'call-site-instrumentation-plugin', 'build', 'libs')
97+
private fun createGradleProject(buildDir: File, gradleFile: String, advice: String) {
98+
val projectFolder = File(System.getProperty("user.dir")).parentFile
99+
val callSiteJar = resolve(projectFolder, "buildSrc", "call-site-instrumentation-plugin", "build", "libs", "call-site-instrumentation-plugin-all.jar")
100+
val testCallSiteJarDir = resolve(buildDir, "buildSrc", "call-site-instrumentation-plugin", "build", "libs")
96101
testCallSiteJarDir.mkdirs()
97102

98103
Files.copy(
99-
callSiteJar.toPath(),
100-
testCallSiteJarDir.toPath().resolve(callSiteJar.name)
104+
callSiteJar.toPath(),
105+
testCallSiteJarDir.toPath().resolve(callSiteJar.name)
101106
)
102107

103-
final gradleFileContent = gradleFile.replace('$$ROOT_FOLDER$$', projectFolder.toString().replace("\\","\\\\"))
104-
105-
final buildGradle = resolve(buildDir, 'build.gradle')
106-
buildGradle.text = gradleFileContent
108+
val gradleFileContent = gradleFile.replace("__ROOT_FOLDER__", projectFolder.toString().replace("\\", "\\\\"))
109+
val buildGradle = resolve(buildDir, "build.gradle")
110+
buildGradle.writeText(gradleFileContent)
107111

108-
final javaFolder = resolve(buildDir, 'src', 'main', 'java')
112+
val javaFolder = resolve(buildDir, "src", "main", "java")
109113
javaFolder.mkdirs()
110114

111-
final advicePackage = parsePackage(advice)
112-
final adviceClassName = parseClassName(advice)
113-
final adviceFolder = resolve(javaFolder, advicePackage.split('\\.'))
115+
val advicePackage = parsePackage(advice)
116+
val adviceClassName = parseClassName(advice)
117+
val adviceFolder = resolve(javaFolder, *advicePackage.split("\\.").toTypedArray())
114118
adviceFolder.mkdirs()
115-
final adviceFile = resolve(adviceFolder, "${adviceClassName}.java")
116-
adviceFile.text = advice
117119

118-
final csiSource = resolve(projectFolder, 'dd-java-agent', 'agent-tooling', 'src', 'main', 'java', 'datadog', 'trace', 'agent', 'tooling', 'csi')
119-
final csiTarget = resolve(javaFolder, 'datadog', 'trace', 'agent', 'tooling', 'csi')
120+
val adviceFile = resolve(adviceFolder, "$adviceClassName.java")
121+
adviceFile.writeText(advice)
122+
123+
val csiSource = resolve(projectFolder, "dd-java-agent", "agent-tooling", "src", "main", "java", "datadog", "trace", "agent", "tooling", "csi")
124+
val csiTarget = resolve(javaFolder, "datadog", "trace", "agent", "tooling", "csi")
120125
csiTarget.mkdirs()
121-
csiSource.listFiles().each { new File(csiTarget, it.name).text = it.text }
126+
csiSource.listFiles()?.forEach {
127+
val targetFile = File(csiTarget, it.name)
128+
targetFile.writeText(it.readText())
129+
}
122130
}
123131

124-
private static BuildResult buildGradleProject(final File buildDir) {
132+
private fun buildGradleProject(buildDir: File): BuildResult {
125133
return GradleRunner.create()
126-
.withTestKitDir(new File(buildDir, '.gradle-test-kit')) // workaround in case the global test-kit cache becomes corrupted
127-
.withDebug(true) // avoids starting daemon which can leave undeleted files post-cleanup
134+
.withTestKitDir(File(buildDir, ".gradle-test-kit")) // workaround in case the global test-kit cache becomes corrupted
135+
.withDebug(true) // avoids starting daemon which can leave undeleted files post-cleanup
128136
.withProjectDir(buildDir)
129-
.withArguments('build', '--info', '--stacktrace')
137+
.withArguments("build", "--info", "--stacktrace")
130138
.withPluginClasspath()
131139
.forwardOutput()
132140
.build()
133141
}
134142

135-
private static String parsePackage(final String advice) {
136-
final advicePackageMatcher = advice =~ /(?s).*package\s+([\w\.]+)\s*;/
137-
return advicePackageMatcher ? advicePackageMatcher[0][1] as String : ''
143+
private fun parsePackage(advice: String): String {
144+
val regex = Regex("package\\s+([\\w.]+)\\s*;", RegexOption.DOT_MATCHES_ALL)
145+
val match = regex.find(advice)
146+
return match?.groupValues?.getOrNull(1) ?: ""
138147
}
139148

140-
private static String parseClassName(final String advice) {
141-
return (advice =~ /(?s).*class\s+(\w+)\s+\{\.*/)[0][1]
149+
private fun parseClassName(advice: String): String {
150+
val regex = Regex("class\\s+(\\w+)\\s+\\{", RegexOption.DOT_MATCHES_ALL)
151+
val match = regex.find(advice)
152+
return match?.groupValues?.getOrNull(1) ?: ""
142153
}
143154

144-
private static File resolve(final File file, final String...path) {
145-
final result = path.inject(file.toPath()) {parent, folder -> parent.resolve(folder)}
146-
return result.toFile()
155+
private fun resolve(parent: File, vararg path: String): File {
156+
return path.fold(parent) { acc, next -> File(acc, next) }
147157
}
148158
}
Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
package datadog.gradle.plugin.instrument
22

33
import net.bytebuddy.utility.OpenedClassReader
4-
import org.gradle.testkit.runner.BuildResult
54
import org.gradle.testkit.runner.GradleRunner
5+
import org.junit.jupiter.api.Assertions.assertTrue
6+
import org.junit.jupiter.api.Test
7+
import org.junit.jupiter.api.io.TempDir
68
import org.objectweb.asm.ClassReader
79
import org.objectweb.asm.ClassVisitor
810
import org.objectweb.asm.FieldVisitor
9-
import spock.lang.Specification
10-
import spock.lang.TempDir
11+
import java.io.File
12+
import java.io.FileInputStream
1113

12-
class InstrumentPluginTest extends Specification {
14+
class InstrumentPluginTest {
1315

14-
def buildGradle = '''
16+
private val buildGradle = """
1517
plugins {
1618
id 'java'
1719
id 'instrument'
@@ -37,9 +39,9 @@ class InstrumentPluginTest extends Specification {
3739
instrument.plugins = [
3840
'TestPlugin'
3941
]
40-
'''
42+
""".trimIndent()
4143

42-
def testPlugin = '''
44+
private val testPlugin = """
4345
import java.io.File;
4446
import java.io.IOException;
4547
import net.bytebuddy.build.Plugin;
@@ -72,56 +74,55 @@ class InstrumentPluginTest extends Specification {
7274
// no-op
7375
}
7476
}
75-
'''
77+
""".trimIndent()
7678

77-
def exampleCode = '''
78-
package example; public class ExampleCode {}
79-
'''
79+
private val exampleCode = """
80+
package example;
81+
public class ExampleCode {}
82+
""".trimIndent()
8083

8184
@TempDir
82-
File buildDir
83-
84-
def 'test instrument plugin'() {
85-
setup:
86-
def tree = new FileTreeBuilder(buildDir)
87-
tree.'build.gradle'(buildGradle)
88-
tree.src {
89-
main {
90-
java {
91-
'TestPlugin.java'(testPlugin)
92-
example {
93-
'ExampleCode.java'(exampleCode)
94-
}
95-
}
96-
}
97-
}
85+
lateinit var buildDir: File
86+
87+
@Test
88+
fun `test instrument plugin`() {
89+
val buildFile = File(buildDir, "build.gradle")
90+
buildFile.writeText(buildGradle)
91+
92+
val srcMainJava = File(buildDir, "src/main/java").apply { mkdirs() }
93+
File(srcMainJava, "TestPlugin.java").writeText(testPlugin)
94+
95+
val examplePackageDir = File(srcMainJava, "example").apply { mkdirs() }
96+
File(examplePackageDir, "ExampleCode.java").writeText(exampleCode)
9897

99-
when:
100-
BuildResult result = GradleRunner.create()
101-
.withTestKitDir(new File(buildDir, '.gradle-test-kit')) // workaround in case the global test-kit cache becomes corrupted
102-
.withDebug(true) // avoids starting daemon which can leave undeleted files post-cleanup
98+
// Run Gradle build with TestKit
99+
val result = GradleRunner.create().withTestKitDir(File(buildDir, ".gradle-test-kit")) // workaround in case the global test-kit cache becomes corrupted
100+
.withDebug(true) // avoids starting daemon which can leave undeleted files post-cleanup
103101
.withProjectDir(buildDir)
104-
.withArguments('build', '--stacktrace')
102+
.withArguments("build", "--stacktrace")
105103
.withPluginClasspath()
106104
.forwardOutput()
107105
.build()
108106

109-
File classFile = new File(buildDir, 'build/classes/java/main/example/ExampleCode.class')
110-
111-
then:
112-
assert classFile.isFile()
113-
114-
boolean foundInsertedField = false
115-
new ClassReader(new FileInputStream(classFile)).accept(new ClassVisitor(OpenedClassReader.ASM_API) {
116-
@Override
117-
FieldVisitor visitField(int access, String fieldName, String descriptor, String signature, Object value) {
118-
if ('__TEST__FIELD__' == fieldName) {
119-
foundInsertedField = true
120-
}
121-
return null
122-
}
123-
}, OpenedClassReader.ASM_API)
107+
val classFile = File(buildDir, "build/classes/java/main/example/ExampleCode.class")
108+
assertTrue(classFile.isFile)
109+
110+
var foundInsertedField = false
111+
FileInputStream(classFile).use { input ->
112+
val classReader = ClassReader(input)
113+
classReader.accept(
114+
object : ClassVisitor(OpenedClassReader.ASM_API) {
115+
override fun visitField(access: Int, fieldName: String?, descriptor: String?, signature: String?, value: Any?): FieldVisitor? {
116+
if ("__TEST__FIELD__" == fieldName) {
117+
foundInsertedField = true
118+
}
119+
return null
120+
}
121+
},
122+
OpenedClassReader.ASM_API
123+
)
124+
}
124125

125-
assert foundInsertedField
126+
assertTrue(foundInsertedField)
126127
}
127128
}

0 commit comments

Comments
 (0)