|
1 | 1 | package datadog.gradle.plugin.csi |
2 | 2 |
|
3 | | -import java.nio.file.Files |
4 | 3 | import org.gradle.testkit.runner.BuildResult |
5 | 4 | import org.gradle.testkit.runner.GradleRunner |
6 | 5 | 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 |
11 | 13 |
|
12 | | - def buildGradle = ''' |
| 14 | +class CallSiteInstrumentationPluginTest { |
| 15 | + private val buildGradle = """ |
13 | 16 | plugins { |
14 | 17 | id 'java' |
15 | 18 | id 'call-site-instrumentation' |
16 | 19 | } |
17 | | - |
| 20 | +
|
18 | 21 | sourceCompatibility = JavaVersion.VERSION_1_8 |
19 | 22 | targetCompatibility = JavaVersion.VERSION_1_8 |
20 | 23 |
|
21 | 24 | csi { |
22 | 25 | suffix = 'CallSite' |
23 | 26 | targetFolder = project.layout.buildDirectory.dir('csi') |
24 | | - rootFolder = file('$$ROOT_FOLDER$$') |
| 27 | + rootFolder = file('__ROOT_FOLDER__') |
25 | 28 | } |
26 | | - |
| 29 | + |
27 | 30 | repositories { |
28 | 31 | mavenCentral() |
29 | 32 | } |
30 | | - |
| 33 | + |
31 | 34 | dependencies { |
32 | 35 | implementation group: 'net.bytebuddy', name: 'byte-buddy', version: '1.18.1' |
33 | 36 | implementation group: 'com.google.auto.service', name: 'auto-service-annotations', version: '1.1.1' |
34 | 37 | } |
35 | | - ''' |
| 38 | + """.trimIndent() |
36 | 39 |
|
37 | 40 | @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")) |
63 | 67 | } |
64 | 68 |
|
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")) |
90 | 95 | } |
91 | 96 |
|
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") |
96 | 101 | testCallSiteJarDir.mkdirs() |
97 | 102 |
|
98 | 103 | Files.copy( |
99 | | - callSiteJar.toPath(), |
100 | | - testCallSiteJarDir.toPath().resolve(callSiteJar.name) |
| 104 | + callSiteJar.toPath(), |
| 105 | + testCallSiteJarDir.toPath().resolve(callSiteJar.name) |
101 | 106 | ) |
102 | 107 |
|
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) |
107 | 111 |
|
108 | | - final javaFolder = resolve(buildDir, 'src', 'main', 'java') |
| 112 | + val javaFolder = resolve(buildDir, "src", "main", "java") |
109 | 113 | javaFolder.mkdirs() |
110 | 114 |
|
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()) |
114 | 118 | adviceFolder.mkdirs() |
115 | | - final adviceFile = resolve(adviceFolder, "${adviceClassName}.java") |
116 | | - adviceFile.text = advice |
117 | 119 |
|
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") |
120 | 125 | 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 | + } |
122 | 130 | } |
123 | 131 |
|
124 | | - private static BuildResult buildGradleProject(final File buildDir) { |
| 132 | + private fun buildGradleProject(buildDir: File): BuildResult { |
125 | 133 | 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 |
128 | 136 | .withProjectDir(buildDir) |
129 | | - .withArguments('build', '--info', '--stacktrace') |
| 137 | + .withArguments("build", "--info", "--stacktrace") |
130 | 138 | .withPluginClasspath() |
131 | 139 | .forwardOutput() |
132 | 140 | .build() |
133 | 141 | } |
134 | 142 |
|
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) ?: "" |
138 | 147 | } |
139 | 148 |
|
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) ?: "" |
142 | 153 | } |
143 | 154 |
|
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) } |
147 | 157 | } |
148 | 158 | } |
0 commit comments