mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8365467: Issues with jrtfs implementation for exploded run-time images
Reviewed-by: rriggs, sundar
This commit is contained in:
parent
80873a09bf
commit
e190355777
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,17 +27,15 @@ package jdk.internal.jrtfs;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystemException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.jimage.ImageReader.Node;
|
||||
@ -56,16 +54,15 @@ class ExplodedImage extends SystemImage {
|
||||
|
||||
private static final String MODULES = "/modules/";
|
||||
private static final String PACKAGES = "/packages/";
|
||||
private static final int PACKAGES_LEN = PACKAGES.length();
|
||||
|
||||
private final FileSystem defaultFS;
|
||||
private final Path modulesDir;
|
||||
private final String separator;
|
||||
private final Map<String, PathNode> nodes = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Map<String, PathNode> nodes = new HashMap<>();
|
||||
private final BasicFileAttributes modulesDirAttrs;
|
||||
|
||||
ExplodedImage(Path modulesDir) throws IOException {
|
||||
defaultFS = FileSystems.getDefault();
|
||||
String str = defaultFS.getSeparator();
|
||||
this.modulesDir = modulesDir;
|
||||
String str = modulesDir.getFileSystem().getSeparator();
|
||||
separator = str.equals("/") ? null : str;
|
||||
modulesDirAttrs = Files.readAttributes(modulesDir, BasicFileAttributes.class);
|
||||
initNodes();
|
||||
@ -79,21 +76,26 @@ class ExplodedImage extends SystemImage {
|
||||
private PathNode link;
|
||||
private List<Node> children;
|
||||
|
||||
PathNode(String name, Path path, BasicFileAttributes attrs) { // path
|
||||
private PathNode(String name, Path path, BasicFileAttributes attrs) { // path
|
||||
super(name, attrs);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
PathNode(String name, Node link) { // link
|
||||
private PathNode(String name, Node link) { // link
|
||||
super(name, link.getFileAttributes());
|
||||
this.link = (PathNode)link;
|
||||
}
|
||||
|
||||
PathNode(String name, List<Node> children) { // dir
|
||||
private PathNode(String name, List<Node> children) { // dir
|
||||
super(name, modulesDirAttrs);
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResource() {
|
||||
return link == null && !getFileAttributes().isDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return children != null ||
|
||||
@ -112,7 +114,7 @@ class ExplodedImage extends SystemImage {
|
||||
return recursive && link.isLink() ? link.resolveLink(true) : link;
|
||||
}
|
||||
|
||||
byte[] getContent() throws IOException {
|
||||
private byte[] getContent() throws IOException {
|
||||
if (!getFileAttributes().isRegularFile())
|
||||
throw new FileSystemException(getName() + " is not file");
|
||||
return Files.readAllBytes(path);
|
||||
@ -126,7 +128,7 @@ class ExplodedImage extends SystemImage {
|
||||
List<Node> list = new ArrayList<>();
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
|
||||
for (Path p : stream) {
|
||||
p = explodedModulesDir.relativize(p);
|
||||
p = modulesDir.relativize(p);
|
||||
String pName = MODULES + nativeSlashToFrontSlash(p.toString());
|
||||
Node node = findNode(pName);
|
||||
if (node != null) { // findNode may choose to hide certain files!
|
||||
@ -152,7 +154,7 @@ class ExplodedImage extends SystemImage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
public synchronized void close() throws IOException {
|
||||
nodes.clear();
|
||||
}
|
||||
|
||||
@ -161,72 +163,76 @@ class ExplodedImage extends SystemImage {
|
||||
return ((PathNode)node).getContent();
|
||||
}
|
||||
|
||||
// find Node for the given Path
|
||||
@Override
|
||||
public synchronized Node findNode(String str) {
|
||||
Node node = findModulesNode(str);
|
||||
public synchronized Node findNode(String name) {
|
||||
PathNode node = nodes.get(name);
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
// lazily created for paths like /packages/<package>/<module>/xyz
|
||||
// For example /packages/java.lang/java.base/java/lang/
|
||||
if (str.startsWith(PACKAGES)) {
|
||||
// pkgEndIdx marks end of <package> part
|
||||
int pkgEndIdx = str.indexOf('/', PACKAGES_LEN);
|
||||
if (pkgEndIdx != -1) {
|
||||
// modEndIdx marks end of <module> part
|
||||
int modEndIdx = str.indexOf('/', pkgEndIdx + 1);
|
||||
if (modEndIdx != -1) {
|
||||
// make sure we have such module link!
|
||||
// ie., /packages/<package>/<module> is valid
|
||||
Node linkNode = nodes.get(str.substring(0, modEndIdx));
|
||||
if (linkNode == null || !linkNode.isLink()) {
|
||||
return null;
|
||||
}
|
||||
// map to "/modules/zyz" path and return that node
|
||||
// For example, "/modules/java.base/java/lang" for
|
||||
// "/packages/java.lang/java.base/java/lang".
|
||||
String mod = MODULES + str.substring(pkgEndIdx + 1);
|
||||
return findModulesNode(mod);
|
||||
// If null, this was not the name of "/modules/..." node, and since all
|
||||
// "/packages/..." nodes were created and cached in advance, the name
|
||||
// cannot reference a valid node.
|
||||
Path path = underlyingModulesPath(name);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
// This can still return null for hidden files.
|
||||
return createModulesNode(name, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily creates and caches a {@code Node} for the given "/modules/..." name
|
||||
* and corresponding path to a file or directory.
|
||||
*
|
||||
* @param name a resource or directory node name, of the form "/modules/...".
|
||||
* @param path the path of a file for a resource or directory.
|
||||
* @return the newly created and cached node, or {@code null} if the given
|
||||
* path references a file which must be hidden in the node hierarchy.
|
||||
*/
|
||||
private Node createModulesNode(String name, Path path) {
|
||||
assert !nodes.containsKey(name) : "Node must not already exist: " + name;
|
||||
assert isNonEmptyModulesPath(name) : "Invalid modules name: " + name;
|
||||
|
||||
try {
|
||||
// We only know if we're creating a resource of directory when we
|
||||
// look up file attributes, and we only do that once. Thus, we can
|
||||
// only reject "marker files" here, rather than by inspecting the
|
||||
// given name string, since it doesn't apply to directories.
|
||||
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
|
||||
if (attrs.isRegularFile()) {
|
||||
Path f = path.getFileName();
|
||||
if (f.toString().startsWith("_the.")) {
|
||||
return null;
|
||||
}
|
||||
} else if (!attrs.isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
PathNode node = new PathNode(name, path, attrs);
|
||||
nodes.put(name, node);
|
||||
return node;
|
||||
} catch (IOException x) {
|
||||
// Since the path reference a file, any errors should not be ignored.
|
||||
throw new UncheckedIOException(x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expected file path for name in the "/modules/..." namespace,
|
||||
* or {@code null} if the name is not in the "/modules/..." namespace or the
|
||||
* path does not reference a file.
|
||||
*/
|
||||
private Path underlyingModulesPath(String name) {
|
||||
if (isNonEmptyModulesPath(name)) {
|
||||
Path path = modulesDir.resolve(frontSlashToNativeSlash(name.substring(MODULES.length())));
|
||||
return Files.exists(path) ? path : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// find a Node for a path that starts like "/modules/..."
|
||||
Node findModulesNode(String str) {
|
||||
PathNode node = nodes.get(str);
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
// lazily created "/modules/xyz/abc/" Node
|
||||
// This is mapped to default file system path "<JDK_MODULES_DIR>/xyz/abc"
|
||||
Path p = underlyingPath(str);
|
||||
if (p != null) {
|
||||
try {
|
||||
BasicFileAttributes attrs = Files.readAttributes(p, BasicFileAttributes.class);
|
||||
if (attrs.isRegularFile()) {
|
||||
Path f = p.getFileName();
|
||||
if (f.toString().startsWith("_the."))
|
||||
return null;
|
||||
}
|
||||
node = new PathNode(str, p, attrs);
|
||||
nodes.put(str, node);
|
||||
return node;
|
||||
} catch (IOException x) {
|
||||
// does not exists or unable to determine
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Path underlyingPath(String str) {
|
||||
if (str.startsWith(MODULES)) {
|
||||
str = frontSlashToNativeSlash(str.substring("/modules".length()));
|
||||
return defaultFS.getPath(explodedModulesDir.toString(), str);
|
||||
}
|
||||
return null;
|
||||
private static boolean isNonEmptyModulesPath(String name) {
|
||||
// Don't just check the prefix, there must be something after it too
|
||||
// (otherwise you end up with an empty string after trimming).
|
||||
return name.startsWith(MODULES) && name.length() > MODULES.length();
|
||||
}
|
||||
|
||||
// convert "/" to platform path separator
|
||||
@ -249,24 +255,21 @@ class ExplodedImage extends SystemImage {
|
||||
// same package prefix may exist in multiple modules. This Map
|
||||
// is filled by walking "jdk modules" directory recursively!
|
||||
Map<String, List<String>> packageToModules = new HashMap<>();
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(explodedModulesDir)) {
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(modulesDir)) {
|
||||
for (Path module : stream) {
|
||||
if (Files.isDirectory(module)) {
|
||||
String moduleName = module.getFileName().toString();
|
||||
// make sure "/modules/<moduleName>" is created
|
||||
findModulesNode(MODULES + moduleName);
|
||||
Objects.requireNonNull(createModulesNode(MODULES + moduleName, module));
|
||||
try (Stream<Path> contentsStream = Files.walk(module)) {
|
||||
contentsStream.filter(Files::isDirectory).forEach((p) -> {
|
||||
p = module.relativize(p);
|
||||
String pkgName = slashesToDots(p.toString());
|
||||
// skip META-INF and empty strings
|
||||
if (!pkgName.isEmpty() && !pkgName.startsWith("META-INF")) {
|
||||
List<String> moduleNames = packageToModules.get(pkgName);
|
||||
if (moduleNames == null) {
|
||||
moduleNames = new ArrayList<>();
|
||||
packageToModules.put(pkgName, moduleNames);
|
||||
}
|
||||
moduleNames.add(moduleName);
|
||||
packageToModules
|
||||
.computeIfAbsent(pkgName, k -> new ArrayList<>())
|
||||
.add(moduleName);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -275,8 +278,8 @@ class ExplodedImage extends SystemImage {
|
||||
}
|
||||
// create "/modules" directory
|
||||
// "nodes" map contains only /modules/<foo> nodes only so far and so add all as children of /modules
|
||||
PathNode modulesDir = new PathNode("/modules", new ArrayList<>(nodes.values()));
|
||||
nodes.put(modulesDir.getName(), modulesDir);
|
||||
PathNode modulesRootNode = new PathNode("/modules", new ArrayList<>(nodes.values()));
|
||||
nodes.put(modulesRootNode.getName(), modulesRootNode);
|
||||
|
||||
// create children under "/packages"
|
||||
List<Node> packagesChildren = new ArrayList<>(packageToModules.size());
|
||||
@ -285,7 +288,7 @@ class ExplodedImage extends SystemImage {
|
||||
List<String> moduleNameList = entry.getValue();
|
||||
List<Node> moduleLinkNodes = new ArrayList<>(moduleNameList.size());
|
||||
for (String moduleName : moduleNameList) {
|
||||
Node moduleNode = findModulesNode(MODULES + moduleName);
|
||||
Node moduleNode = Objects.requireNonNull(nodes.get(MODULES + moduleName));
|
||||
PathNode linkNode = new PathNode(PACKAGES + pkgName + "/" + moduleName, moduleNode);
|
||||
nodes.put(linkNode.getName(), linkNode);
|
||||
moduleLinkNodes.add(linkNode);
|
||||
@ -295,13 +298,13 @@ class ExplodedImage extends SystemImage {
|
||||
packagesChildren.add(pkgDir);
|
||||
}
|
||||
// "/packages" dir
|
||||
PathNode packagesDir = new PathNode("/packages", packagesChildren);
|
||||
nodes.put(packagesDir.getName(), packagesDir);
|
||||
PathNode packagesRootNode = new PathNode("/packages", packagesChildren);
|
||||
nodes.put(packagesRootNode.getName(), packagesRootNode);
|
||||
|
||||
// finally "/" dir!
|
||||
List<Node> rootChildren = new ArrayList<>();
|
||||
rootChildren.add(packagesDir);
|
||||
rootChildren.add(modulesDir);
|
||||
rootChildren.add(packagesRootNode);
|
||||
rootChildren.add(modulesRootNode);
|
||||
PathNode root = new PathNode("/", rootChildren);
|
||||
nodes.put(root.getName(), root);
|
||||
}
|
||||
|
||||
@ -78,13 +78,13 @@ abstract class SystemImage {
|
||||
return new ExplodedImage(explodedModulesDir);
|
||||
}
|
||||
|
||||
static final String RUNTIME_HOME;
|
||||
private static final String RUNTIME_HOME;
|
||||
// "modules" jimage file Path
|
||||
static final Path moduleImageFile;
|
||||
private static final Path moduleImageFile;
|
||||
// "modules" jimage exists or not?
|
||||
static final boolean modulesImageExists;
|
||||
private static final boolean modulesImageExists;
|
||||
// <JAVA_HOME>/modules directory Path
|
||||
static final Path explodedModulesDir;
|
||||
private static final Path explodedModulesDir;
|
||||
|
||||
static {
|
||||
PrivilegedAction<String> pa = SystemImage::findHome;
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Whitebox tests for ExplodedImage to ensure compatibility with ImageReader.
|
||||
* @modules java.base/jdk.internal.jrtfs java.base/jdk.internal.jimage
|
||||
* @run junit/othervm java.base/jdk.internal.jrtfs.ExplodedImageTest
|
||||
*/
|
||||
public class ExplodedImageTestDriver {}
|
||||
4
test/jdk/jdk/internal/jrtfs/whitebox/TEST.properties
Normal file
4
test/jdk/jdk/internal/jrtfs/whitebox/TEST.properties
Normal file
@ -0,0 +1,4 @@
|
||||
modules = \
|
||||
java.base/jdk.internal.jimage \
|
||||
java.base/jdk.internal.jrtfs
|
||||
bootclasspath.dirs=.
|
||||
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.jrtfs;
|
||||
|
||||
import jdk.internal.jimage.ImageReader;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests an {@link ExplodedImage} view of a class-file hierarchy.
|
||||
*
|
||||
* <p>For simplicity and performance, only a subset of the JRT files are copied
|
||||
* to disk for testing.
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class ExplodedImageTest {
|
||||
|
||||
private Path modulesRoot;
|
||||
private SystemImage explodedImage;
|
||||
private String pathSeparator;
|
||||
|
||||
@BeforeAll
|
||||
public void createTestDirectory(@TempDir Path modulesRoot) throws IOException {
|
||||
this.modulesRoot = modulesRoot;
|
||||
this.pathSeparator = modulesRoot.getFileSystem().getSeparator();
|
||||
// Copy only a useful subset of files for testing. Use at least two
|
||||
// modules with "overlapping" packages to test /package links better.
|
||||
unpackModulesDirectoriesFromJrtFileSystem(modulesRoot,
|
||||
"java.base/java/util",
|
||||
"java.base/java/util/zip",
|
||||
"java.logging/java/util/logging");
|
||||
this.explodedImage = new ExplodedImage(modulesRoot);
|
||||
}
|
||||
|
||||
/** Unpacks a list of "/modules/..." directories non-recursively into the specified root directory. */
|
||||
private static void unpackModulesDirectoriesFromJrtFileSystem(Path modulesRoot, String... dirNames)
|
||||
throws IOException {
|
||||
FileSystem jrtfs = FileSystems.getFileSystem(URI.create("jrt:/"));
|
||||
List<Path> srcDirs = Arrays.stream(dirNames).map(s -> "/modules/" + s).map(jrtfs::getPath).toList();
|
||||
for (Path srcDir : srcDirs) {
|
||||
// Skip-1 to remove "modules" segment (not part of the file system path).
|
||||
Path dstDir = StreamSupport.stream(srcDir.spliterator(), false)
|
||||
.skip(1)
|
||||
.reduce(modulesRoot, (path, segment) -> path.resolve(segment.toString()));
|
||||
Files.createDirectories(dstDir);
|
||||
try (DirectoryStream<Path> files = Files.newDirectoryStream(srcDir)) {
|
||||
for (Path srcFile : files) {
|
||||
Files.copy(srcFile, dstDir.resolve(srcFile.getFileName().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void topLevelNodes() throws IOException {
|
||||
ImageReader.Node root = explodedImage.findNode("/");
|
||||
ImageReader.Node modules = explodedImage.findNode("/modules");
|
||||
ImageReader.Node packages = explodedImage.findNode("/packages");
|
||||
assertEquals(
|
||||
Set.of(modules.getName(), packages.getName()),
|
||||
root.getChildNames().collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"/modules/java.base/java/util/List.class",
|
||||
"/modules/java.base/java/util/zip/ZipEntry.class",
|
||||
"/modules/java.logging/java/util/logging/Logger.class"})
|
||||
public void basicLookupResource(String expectedResourceName) throws IOException {
|
||||
ImageReader.Node node = assertResourceNode(expectedResourceName);
|
||||
|
||||
Path fsRelPath = getRelativePath(expectedResourceName);
|
||||
assertArrayEquals(
|
||||
Files.readAllBytes(modulesRoot.resolve(fsRelPath)),
|
||||
explodedImage.getResource(node));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"/modules/java.base",
|
||||
"/modules/java.logging",
|
||||
"/modules/java.base/java",
|
||||
"/modules/java.base/java/util",
|
||||
"/modules/java.logging/java/util",
|
||||
})
|
||||
public void basicLookupDirectory(String expectedDirectoryName) throws IOException {
|
||||
ImageReader.Node node = assertDirectoryNode(expectedDirectoryName);
|
||||
|
||||
Path fsRelPath = getRelativePath(expectedDirectoryName);
|
||||
List<String> fsChildBaseNames;
|
||||
try (DirectoryStream<Path> paths = Files.newDirectoryStream(modulesRoot.resolve(fsRelPath))) {
|
||||
fsChildBaseNames = StreamSupport.stream(paths.spliterator(), false)
|
||||
.map(Path::getFileName)
|
||||
.map(Path::toString)
|
||||
.toList();
|
||||
}
|
||||
List<String> nodeChildBaseNames = node.getChildNames()
|
||||
.map(s -> s.substring(node.getName().length() + 1))
|
||||
.toList();
|
||||
assertEquals(fsChildBaseNames, nodeChildBaseNames, "expected same child names");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"/packages/java/java.base",
|
||||
"/packages/java/java.logging",
|
||||
"/packages/java.util/java.base",
|
||||
"/packages/java.util/java.logging",
|
||||
"/packages/java.util.zip/java.base"})
|
||||
public void basicLookupPackageLinks(String expectedLinkName) throws IOException {
|
||||
ImageReader.Node node = assertLinkNode(expectedLinkName);
|
||||
ImageReader.Node resolved = node.resolveLink();
|
||||
assertSame(explodedImage.findNode(resolved.getName()), resolved);
|
||||
String moduleName = expectedLinkName.substring(expectedLinkName.lastIndexOf('/') + 1);
|
||||
assertEquals("/modules/" + moduleName, resolved.getName());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"/packages/java",
|
||||
"/packages/java.util",
|
||||
"/packages/java.util.zip"})
|
||||
public void packageDirectories(String expectedDirectoryName) throws IOException {
|
||||
ImageReader.Node node = assertDirectoryNode(expectedDirectoryName);
|
||||
assertTrue(node.getChildNames().findFirst().isPresent(),
|
||||
"Package directories should not be empty: " + node);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"",
|
||||
".",
|
||||
"/.",
|
||||
"modules",
|
||||
"packages",
|
||||
"/modules/",
|
||||
"/modules/xxxx",
|
||||
"/modules/java.base/java/lang/Xxxx.class",
|
||||
"/packages/",
|
||||
"/packages/xxxx",
|
||||
"/packages/java.xxxx",
|
||||
"/packages/java.util.",
|
||||
// Mismatched module.
|
||||
"/packages/java.util.logging/java.base",
|
||||
"/packages/java.util.zip/java.logging",
|
||||
// Links are not resolved as they are fetched (old/broken behaviour).
|
||||
"/packages/java.util/java.base/java/util/Vector.class",
|
||||
})
|
||||
public void invalidNames(String invalidName) throws IOException {
|
||||
assertNull(explodedImage.findNode(invalidName), "No node expected for: " + invalidName);
|
||||
}
|
||||
|
||||
private ImageReader.Node assertResourceNode(String name) throws IOException {
|
||||
ImageReader.Node node = explodedImage.findNode(name);
|
||||
assertNotNull(node);
|
||||
assertEquals(name, node.getName(), "expected node name: " + name);
|
||||
assertTrue(node.isResource(), "expected a resource: " + node);
|
||||
assertFalse(node.isDirectory(), "resources are not directories: " + node);
|
||||
assertFalse(node.isLink(), "resources are not links: " + node);
|
||||
return node;
|
||||
}
|
||||
|
||||
private ImageReader.Node assertDirectoryNode(String name) throws IOException {
|
||||
ImageReader.Node node = explodedImage.findNode(name);
|
||||
assertNotNull(node);
|
||||
assertEquals(name, node.getName(), "expected node name: " + name);
|
||||
assertTrue(node.isDirectory(), "expected a directory: " + node);
|
||||
assertFalse(node.isResource(), "directories are not resources: " + node);
|
||||
assertFalse(node.isLink(), "directories are not links: " + node);
|
||||
return node;
|
||||
}
|
||||
|
||||
private ImageReader.Node assertLinkNode(String name) throws IOException {
|
||||
ImageReader.Node node = explodedImage.findNode(name);
|
||||
assertNotNull(node);
|
||||
assertEquals(name, node.getName(), "expected node name: " + name);
|
||||
assertTrue(node.isLink(), "expected a link: " + node);
|
||||
assertFalse(node.isResource(), "links are not resources: " + node);
|
||||
assertFalse(node.isDirectory(), "links are not directories: " + node);
|
||||
return node;
|
||||
}
|
||||
|
||||
private Path getRelativePath(String name) {
|
||||
return Path.of(name.substring("/modules/".length()).replace("/", pathSeparator));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user