Skip to content

Commit 12d19ee

Browse files
authored
Normalize platform-dependent resource and test paths (#981)
* Normalize platform-dependent paths * Handle equivalent temporary path aliases in Maven tests * Fix remaining Windows functional test assertions * Use non-asserting output check for native image arguments * Make console color assertion deterministic across platforms
1 parent 444eeee commit 12d19ee

9 files changed

Lines changed: 86 additions & 22 deletions

File tree

common/utils/src/main/java/org/graalvm/buildtools/model/resources/ClassPathDirectoryAnalyzer.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
106106
hasNativeImageResourceFile = true;
107107
return FileVisitResult.TERMINATE;
108108
}
109-
maybeAddResource(root.relativize(file).toString(), resources);
109+
// Native Image resource names use portable separators. §FS-common-libraries.2.
110+
maybeAddResource(relativePathOf(file), resources);
110111
return FileVisitResult.CONTINUE;
111112
}
112113
}

common/utils/src/test/java/org/graalvm/buildtools/utils/ClassPathEntryAnalyzerTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,20 @@
4343
import org.graalvm.buildtools.model.resources.ClassPathEntryAnalyzer;
4444
import org.junit.jupiter.api.DisplayName;
4545
import org.junit.jupiter.api.Test;
46+
import org.junit.jupiter.api.io.TempDir;
4647

4748
import java.io.File;
4849
import java.io.IOException;
50+
import java.nio.file.Files;
51+
import java.nio.file.Path;
4952
import java.util.Collections;
5053

5154
import static org.junit.jupiter.api.Assertions.assertEquals;
5255

5356
public class ClassPathEntryAnalyzerTest {
57+
@TempDir
58+
Path temporaryDirectory;
59+
5460
@Test
5561
@DisplayName("A non existent jar shouldn't cause an exception")
5662
public void testShouldAllowNonExistentJar() throws IOException {
@@ -72,4 +78,21 @@ public void testShouldAllowNonExistentDir() throws IOException {
7278
);
7379
assertEquals(Collections.emptyList(), analyzer.getResources());
7480
}
81+
82+
@Test
83+
@DisplayName("Directory resources should use portable path separators")
84+
public void testShouldNormalizeDirectoryResourcePathSeparators() throws IOException {
85+
Path resource = temporaryDirectory.resolve("org/graalvm/demo/expected.txt");
86+
Files.createDirectories(resource.getParent());
87+
Files.writeString(resource, "resource");
88+
89+
ClassPathEntryAnalyzer analyzer = ClassPathEntryAnalyzer.of(
90+
temporaryDirectory.toFile(),
91+
path -> true,
92+
false
93+
);
94+
95+
// Directory scanning emits portable Native Image resource names. §FS-common-libraries.2.
96+
assertEquals(Collections.singletonList("org/graalvm/demo/expected.txt"), analyzer.getResources());
97+
}
7598
}

native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/JavaApplicationFunctionalTest.groovy

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ class JavaApplicationFunctionalTest extends AbstractFunctionalTest {
191191
withSample("java-application")
192192
buildFile << """
193193
graalvmNative {
194+
useArgFile = true
194195
binaries.all {
195196
richOutput = true
196197
verbose = true
@@ -205,7 +206,7 @@ class JavaApplicationFunctionalTest extends AbstractFunctionalTest {
205206
tasks {
206207
succeeded ':nativeCompile'
207208
}
208-
outputContains expectedColorArgument
209+
nativeImageInvocationContains('nativeCompile', expectedColorArgument)
209210

210211
where:
211212
console | expectedColorArgument
@@ -392,4 +393,17 @@ class JavaApplicationFunctionalTest extends AbstractFunctionalTest {
392393
outputContains "PGO: user-provided"
393394
}
394395

396+
// This scenario uses an argument file so console-color assertions are deterministic on every OS. §FS-native-invocation.3.
397+
private boolean nativeImageInvocationContains(String taskName, String expectedArgument) {
398+
if (result.output.contains(expectedArgument)) {
399+
return true
400+
}
401+
def matcher = result.output =~ /\[native-image-plugin\] Args are: \[@(.+?\.args), /
402+
if (!matcher.find()) {
403+
return false
404+
}
405+
def workingDirectory = path('build', 'native', taskName)
406+
Files.readAllLines(workingDirectory.resolve(matcher.group(1)).normalize()).contains(expectedArgument)
407+
}
408+
395409
}

native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/JavaApplicationWithAgentFunctionalTest.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ class JavaApplicationWithAgentFunctionalTest extends AbstractFunctionalTest {
205205

206206
and:
207207
// Instrumented task output reports the Gradle-managed agent output directory. §FS-tracing-agent.4.
208-
outputContains "Instrumenting task with the native-image-agent: test. Agent output: ${file('build/native/agent-output/test').path}"
208+
outputContains "Instrumenting task with the native-image-agent: test. Agent output: ${file('build/native/agent-output/test').canonicalPath}"
209209
assert metadataExistsAt("build/native/agent-output/test")
210210

211211
when:
@@ -260,7 +260,7 @@ class JavaApplicationWithAgentFunctionalTest extends AbstractFunctionalTest {
260260

261261
and:
262262
// Instrumented task output reports the Gradle-managed agent output directory. §FS-tracing-agent.4.
263-
outputContains "Instrumenting task with the native-image-agent: run. Agent output: ${file('build/native/agent-output/run').path}"
263+
outputContains "Instrumenting task with the native-image-agent: run. Agent output: ${file('build/native/agent-output/run').canonicalPath}"
264264
assert metadataExistsAt("build/native/agent-output/run")
265265

266266
when:

native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/NativeConfigRepoFunctionalTest.groovy

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class NativeConfigRepoFunctionalTest extends AbstractFunctionalTest {
8282
and: "identifies the selected metadata repository source"
8383
// Gradle output identifies the selected metadata repository source. §FS-resources-and-metadata.3.
8484
outputContains "Using GraalVM reachability metadata repository from " +
85-
file("config-directory${extension ? '.' + extension : ''}").toURI().toASCIIString()
85+
file("config-directory${extension ? '.' + extension : ''}").canonicalFile.toURI().toASCIIString()
8686

8787
and: "doesn't find a configuration directory for the current version"
8888
outputContains "[graalvm reachability metadata repository for org.graalvm.internal:library-with-reflection:1.5]: Configuration directory not found. Trying latest version."
@@ -98,16 +98,16 @@ class NativeConfigRepoFunctionalTest extends AbstractFunctionalTest {
9898
'tar.bz2' | 'tar.bz2 file'
9999
}
100100

101-
// Protects custom binary runtime classpath and metadata exclusions. §FS-plugin-model.4
102-
// §FS-native-tasks.1 §FS-resources-and-metadata.6
101+
// Protects custom binary runtime classpath and metadata exclusions through argument files.
102+
// §FS-plugin-model.4 §FS-native-tasks.1 §FS-native-invocation.4 §FS-resources-and-metadata.6
103103
@Issue("https://github.com/graalvm/native-build-tools/issues/478")
104104
def "custom binary uses runtime classpath and metadata exclusions"() {
105105
given:
106106
withSample("native-config-integration")
107107

108108
buildFile << """
109109
graalvmNative {
110-
useArgFile = false
110+
useArgFile = true
111111
binaries {
112112
qa {
113113
imageName = 'native-config-integration-qa'
@@ -128,8 +128,9 @@ graalvmNative {
128128

129129
and:
130130
outputContains "Hello, from reflection!"
131-
outputContains "--exclude-config"
132-
outputContains "library-with-reflection-1.5.jar"
131+
def nativeImageArgs = nativeImageArgsFor("nativeQaCompile")
132+
nativeImageArgs.contains("--exclude-config")
133+
nativeImageArgs.any { it.contains("library-with-reflection-1.5.jar") }
133134
}
134135

135136
def "can exclude a dependency from native configuration"() {
@@ -248,6 +249,13 @@ project(':second') {
248249
repository.resolve("org.graalvm.internal/library-with-reflection/1/reflect-config.json").toFile()
249250
}
250251

252+
private List<String> nativeImageArgsFor(String taskName) {
253+
def matcher = result.output =~ /\[native-image-plugin\] Args are: \[@(.+?\.args), /
254+
assert matcher.find(): "Expected native-image argument file in output"
255+
Path workingDirectory = path("build", "native", taskName)
256+
Files.readAllLines(workingDirectory.resolve(matcher.group(1)).normalize())
257+
}
258+
251259
private File metadataOutput(String projectName) {
252260
file(projectName, "build/native-reachability-metadata/META-INF/native-image/org.graalvm.internal/library-with-reflection/1.5/reflect-config.json")
253261
}

native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationWithAgentFunctionalTest.groovy

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@ class JavaApplicationWithAgentFunctionalTest extends AbstractGraalVMMavenFunctio
8282

8383
and:
8484
// Instrumented execution output reports the Maven-managed agent output directory. §FS-tracing-agent.3.
85-
outputContains "Instrumenting Maven test execution with the native-image-agent. Agent output: " +
86-
file('target/native/agent-output/test').absolutePath
85+
outputContainsAbsoluteOrCanonicalPath(
86+
"Instrumenting Maven test execution with the native-image-agent. Agent output: ",
87+
file('target/native/agent-output/test'))
8788

8889
and:
8990
// Agent generates files
@@ -128,8 +129,9 @@ class JavaApplicationWithAgentFunctionalTest extends AbstractGraalVMMavenFunctio
128129

129130
and:
130131
// Instrumented execution output reports the Maven-managed agent output directory. §FS-tracing-agent.3.
131-
outputContains "Instrumenting Maven application execution with the native-image-agent. Agent output: " +
132-
file('target/native/agent-output/main').absolutePath
132+
outputContainsAbsoluteOrCanonicalPath(
133+
"Instrumenting Maven application execution with the native-image-agent. Agent output: ",
134+
file('target/native/agent-output/main'))
133135

134136
when:
135137
mvn'-Pnative', '-DquickBuild', '-DskipNativeTests', '-Dagent=true', 'native:metadata-copy'

native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationWithResourcesFunctionalTest.groovy

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,9 @@ class JavaApplicationWithResourcesFunctionalTest extends AbstractGraalVMMavenFun
205205
</execution>
206206
</executions>
207207
</plugin>'''
208-
assert pom.text.contains(buildStart)
209-
pom.text = pom.text.replace(buildStart, configuredBuildStart)
208+
def pomText = pom.text.replace('\r\n', '\n')
209+
assert pomText.contains(buildStart)
210+
pom.text = pomText.replace(buildStart, configuredBuildStart)
210211
}
211212

212213
private static void assertResourcePatterns(File configFile, List<String> expectedPatterns) {

native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/MetadataRepositoryFunctionalTest.groovy

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@ class MetadataRepositoryFunctionalTest extends AbstractGraalVMMavenFunctionalTes
6868
buildSucceeded
6969
outputContains "Hello, from reflection!"
7070
// Maven output identifies the selected metadata repository source. §FS-resources-and-metadata.2.
71-
outputContains "Using GraalVM reachability metadata repository from " +
72-
file("config-directory").toURI().toASCIIString()
71+
outputContainsAbsoluteOrCanonicalUri(
72+
"Using GraalVM reachability metadata repository from ",
73+
file("config-directory"))
7374

7475
and: "it doesn't find a configuration directory for the current version"
7576
outputContains "[graalvm reachability metadata repository for org.graalvm.internal:library-with-reflection:1.5]: Configuration directory not found. Trying latest version."
@@ -125,8 +126,9 @@ class MetadataRepositoryFunctionalTest extends AbstractGraalVMMavenFunctionalTes
125126
buildSucceeded
126127
outputContains "[graalvm reachability metadata repository for org.graalvm.internal:library-with-reflection:1.5]: Configuration is forced to version 2"
127128
// Maven output identifies the selected metadata repository source. §FS-resources-and-metadata.2.
128-
outputContains "Using GraalVM reachability metadata repository from " +
129-
file("config-directory").toURI().toASCIIString()
129+
outputContainsAbsoluteOrCanonicalUri(
130+
"Using GraalVM reachability metadata repository from ",
131+
file("config-directory"))
130132
outputContains "Reflection failed"
131133
}
132134

@@ -141,8 +143,9 @@ class MetadataRepositoryFunctionalTest extends AbstractGraalVMMavenFunctionalTes
141143
buildSucceeded
142144
outputContains "Hello, from reflection!"
143145
// Maven output identifies the selected metadata repository source. §FS-resources-and-metadata.2.
144-
outputContains "Using GraalVM reachability metadata repository from " +
145-
file("target/repo.zip").toURI().toASCIIString()
146+
outputContainsAbsoluteOrCanonicalUri(
147+
"Using GraalVM reachability metadata repository from ",
148+
file("target/repo.zip"))
146149

147150
and: "it doesn't find a configuration directory for the current version"
148151
outputContains "[graalvm reachability metadata repository for org.graalvm.internal:library-with-reflection:1.5]: Configuration directory not found. Trying latest version."

native-maven-plugin/src/testFixtures/groovy/org/graalvm/buildtools/maven/AbstractGraalVMMavenFunctionalTest.groovy

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,18 @@ abstract class AbstractGraalVMMavenFunctionalTest extends Specification {
218218
normalizeString(result.stdOut).contains(normalizeString(text))
219219
}
220220

221+
// Temp directories can be reported through equivalent aliases (macOS symlinks or Windows 8.3 paths).
222+
// §E2E-functional-tests.3.5 §E2E-functional-tests.3.6
223+
boolean outputContainsAbsoluteOrCanonicalPath(String prefix, File location) {
224+
[location.absolutePath, location.canonicalPath].any { path -> outputContains(prefix + path) }
225+
}
226+
227+
boolean outputContainsAbsoluteOrCanonicalUri(String prefix, File location) {
228+
[location.absoluteFile, location.canonicalFile]
229+
.collect { file -> file.toURI().toASCIIString() }
230+
.any { uri -> outputContains(prefix + uri) }
231+
}
232+
221233
boolean outputContainsPattern(String pattern) {
222234
def normalizedOutput = normalizeString(result.stdOut)
223235
def lines = normalizedOutput.split('\n')

0 commit comments

Comments
 (0)