43f7f47ae0
Reviewed-by: alanb, mchung, jpai, redestad
993 lines
39 KiB
Java
993 lines
39 KiB
Java
/*
|
|
* Copyright (c) 2015, 2022, 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
|
|
* @bug 8142968 8166568 8166286 8170618 8168149 8240910 8276764 8276766
|
|
* @summary Basic test for jmod
|
|
* @library /test/lib
|
|
* @modules jdk.compiler
|
|
* jdk.jlink
|
|
* @build jdk.test.lib.compiler.CompilerUtils
|
|
* jdk.test.lib.util.FileUtils
|
|
* jdk.test.lib.Platform
|
|
* @run testng/othervm -Djava.io.tmpdir=. JmodTest
|
|
*/
|
|
|
|
import java.io.*;
|
|
import java.lang.module.ModuleDescriptor;
|
|
import java.lang.reflect.Method;
|
|
import java.nio.file.*;
|
|
import java.util.*;
|
|
import java.util.function.Consumer;
|
|
import java.util.regex.Pattern;
|
|
import java.util.spi.ToolProvider;
|
|
import java.util.stream.Stream;
|
|
import jdk.test.lib.compiler.CompilerUtils;
|
|
import jdk.test.lib.util.FileUtils;
|
|
import org.testng.annotations.BeforeTest;
|
|
import org.testng.annotations.Test;
|
|
|
|
import static java.io.File.pathSeparator;
|
|
import static java.lang.module.ModuleDescriptor.Version;
|
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
import static java.util.stream.Collectors.toSet;
|
|
import static org.testng.Assert.*;
|
|
|
|
public class JmodTest {
|
|
|
|
static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
|
|
.orElseThrow(() ->
|
|
new RuntimeException("jmod tool not found")
|
|
);
|
|
static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
|
|
.orElseThrow(() ->
|
|
new RuntimeException("jar tool not found")
|
|
);
|
|
|
|
static final String TEST_SRC = System.getProperty("test.src", ".");
|
|
static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
|
|
static final Path EXPLODED_DIR = Paths.get("build");
|
|
static final Path MODS_DIR = Paths.get("jmods");
|
|
|
|
static final String CLASSES_PREFIX = "classes/";
|
|
static final String CMDS_PREFIX = "bin/";
|
|
static final String LIBS_PREFIX = "lib/";
|
|
static final String CONFIGS_PREFIX = "conf/";
|
|
|
|
@BeforeTest
|
|
public void buildExplodedModules() throws IOException {
|
|
if (Files.exists(EXPLODED_DIR))
|
|
FileUtils.deleteFileTreeWithRetry(EXPLODED_DIR);
|
|
|
|
for (String name : new String[] { "foo"/*, "bar", "baz"*/ } ) {
|
|
Path dir = EXPLODED_DIR.resolve(name);
|
|
assertTrue(compileModule(name, dir.resolve("classes")));
|
|
copyResource(SRC_DIR.resolve("foo"),
|
|
dir.resolve("classes"),
|
|
"jdk/test/foo/resources/foo.properties");
|
|
createCmds(dir.resolve("bin"));
|
|
createLibs(dir.resolve("lib"));
|
|
createConfigs(dir.resolve("conf"));
|
|
}
|
|
|
|
if (Files.exists(MODS_DIR))
|
|
FileUtils.deleteFileTreeWithRetry(MODS_DIR);
|
|
Files.createDirectories(MODS_DIR);
|
|
}
|
|
|
|
// JDK-8166286 - jmod fails on symlink to directory
|
|
@Test
|
|
public void testDirSymlinks() throws IOException {
|
|
Path apaDir = EXPLODED_DIR.resolve("apa");
|
|
Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes");
|
|
assertTrue(compileModule("apa", classesDir));
|
|
Path libDir = apaDir.resolve("lib");
|
|
createFiles(libDir, List.of("foo/bar/libfoo.so"));
|
|
try {
|
|
Path link = Files.createSymbolicLink(
|
|
libDir.resolve("baz"), libDir.resolve("foo").toAbsolutePath());
|
|
assertTrue(Files.exists(link));
|
|
} catch (IOException|UnsupportedOperationException uoe) {
|
|
// OS does not support symlinks. Nothing to test!
|
|
System.out.println("Creating symlink failed. Test passes vacuously.");
|
|
uoe.printStackTrace();
|
|
return;
|
|
}
|
|
|
|
Path jmod = MODS_DIR.resolve("apa.jmod");
|
|
jmod("create",
|
|
"--libs=" + libDir.toString(),
|
|
"--class-path", classesDir.toString(),
|
|
jmod.toString())
|
|
.assertSuccess();
|
|
Files.delete(jmod);
|
|
}
|
|
|
|
// JDK-8267583 - jmod fails on symlink to class file
|
|
@Test
|
|
public void testFileSymlinks() throws IOException {
|
|
Path apaDir = EXPLODED_DIR.resolve("apa");
|
|
Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes");
|
|
assertTrue(compileModule("apa", classesDir));
|
|
|
|
Files.move(classesDir.resolve("module-info.class"),
|
|
classesDir.resolve("module-info.class1"));
|
|
Files.move(classesDir.resolve(Paths.get("jdk", "test", "apa", "Apa.class")),
|
|
classesDir.resolve("Apa.class1"));
|
|
try {
|
|
Path link = Files.createSymbolicLink(
|
|
classesDir.resolve("module-info.class"),
|
|
classesDir.resolve("module-info.class1").toAbsolutePath());
|
|
assertTrue(Files.exists(link));
|
|
link = Files.createSymbolicLink(
|
|
classesDir.resolve(Paths.get("jdk", "test", "apa", "Apa.class")),
|
|
classesDir.resolve("Apa.class1").toAbsolutePath());
|
|
assertTrue(Files.exists(link));
|
|
} catch (IOException|UnsupportedOperationException uoe) {
|
|
// OS does not support symlinks. Nothing to test!
|
|
System.out.println("Creating symlinks failed. Test passes vacuously.");
|
|
uoe.printStackTrace();
|
|
return;
|
|
}
|
|
|
|
Path jmod = MODS_DIR.resolve("apa.jmod");
|
|
jmod("create",
|
|
"--class-path", classesDir.toString(),
|
|
jmod.toString())
|
|
.assertSuccess();
|
|
Files.delete(jmod);
|
|
}
|
|
|
|
// JDK-8170618 - jmod should validate if any exported or open package is missing
|
|
@Test
|
|
public void testMissingPackages() throws IOException {
|
|
Path apaDir = EXPLODED_DIR.resolve("apa");
|
|
Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes");
|
|
if (Files.exists(classesDir))
|
|
FileUtils.deleteFileTreeWithRetry(classesDir);
|
|
assertTrue(compileModule("apa", classesDir));
|
|
FileUtils.deleteFileTreeWithRetry(classesDir.resolve("jdk"));
|
|
Path jmod = MODS_DIR.resolve("apa.jmod");
|
|
jmod("create",
|
|
"--class-path", classesDir.toString(),
|
|
jmod.toString())
|
|
.assertFailure()
|
|
.resultChecker(r -> {
|
|
assertContains(r.output, "Packages that are exported or open in apa are not present: [jdk.test.apa]");
|
|
});
|
|
if (Files.exists(classesDir))
|
|
FileUtils.deleteFileTreeWithRetry(classesDir);
|
|
}
|
|
|
|
@Test
|
|
public void testList() throws IOException {
|
|
String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
|
|
Path jmod = MODS_DIR.resolve("foo.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
jmod.toString())
|
|
.assertSuccess();
|
|
|
|
jmod("list",
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
// asserts dependent on the exact contents of foo
|
|
assertContains(r.output, CLASSES_PREFIX + "module-info.class");
|
|
assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/Foo.class");
|
|
assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/internal/Message.class");
|
|
assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties");
|
|
|
|
// JDK-8276764: Ensure the order is sorted for reproducible jmod content
|
|
// module-info, followed by <sorted classes>
|
|
int mod_info_i = r.output.indexOf(CLASSES_PREFIX + "module-info.class");
|
|
int foo_cls_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/Foo.class");
|
|
int msg_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/internal/Message.class");
|
|
int res_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties");
|
|
System.out.println("jmod classes sort order check:\n"+r.output);
|
|
assertTrue(mod_info_i < foo_cls_i);
|
|
assertTrue(foo_cls_i < msg_i);
|
|
assertTrue(msg_i < res_i);
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testSourceDateReproducible() throws IOException {
|
|
String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
|
|
Path jmod1 = MODS_DIR.resolve("foo1.jmod");
|
|
Path jmod2 = MODS_DIR.resolve("foo2.jmod");
|
|
Path jmod3 = MODS_DIR.resolve("foo3.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod1);
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod2);
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod3);
|
|
|
|
// Use source date of 15/03/2022
|
|
String sourceDate = "2022-03-15T00:00:00+00:00";
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--date", sourceDate,
|
|
jmod1.toString())
|
|
.assertSuccess();
|
|
|
|
try {
|
|
// Sleep 5 seconds to ensure zip timestamps might be different if they could be
|
|
Thread.sleep(5000);
|
|
} catch(InterruptedException ex) {}
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--date", sourceDate,
|
|
jmod2.toString())
|
|
.assertSuccess();
|
|
|
|
// Compare file byte content to see if they are identical
|
|
assertSameContent(jmod1, jmod2);
|
|
|
|
// Use a date before 1980 and assert failure error
|
|
sourceDate = "1976-03-15T00:00:00+00:00";
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--date", sourceDate,
|
|
jmod3.toString())
|
|
.assertFailure()
|
|
.resultChecker(r -> {
|
|
assertContains(r.output, "is out of the valid range");
|
|
});
|
|
|
|
// Use a date after 2099 and assert failure error
|
|
sourceDate = "2100-03-15T00:00:00+00:00";
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--date", sourceDate,
|
|
jmod3.toString())
|
|
.assertFailure()
|
|
.resultChecker(r -> {
|
|
assertContains(r.output, "is out of the valid range");
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testExtractCWD() throws IOException {
|
|
Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
|
|
jmod("create",
|
|
"--class-path", cp.toString(),
|
|
MODS_DIR.resolve("fooExtractCWD.jmod").toString())
|
|
.assertSuccess();
|
|
|
|
jmod("extract",
|
|
MODS_DIR.resolve("fooExtractCWD.jmod").toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
// module-info should exist, but jmod will have added its Packages attr.
|
|
assertTrue(Files.exists(Paths.get("classes/module-info.class")));
|
|
assertSameContent(cp.resolve("jdk/test/foo/Foo.class"),
|
|
Paths.get("classes/jdk/test/foo/Foo.class"));
|
|
assertSameContent(cp.resolve("jdk/test/foo/internal/Message.class"),
|
|
Paths.get("classes/jdk/test/foo/internal/Message.class"));
|
|
assertSameContent(cp.resolve("jdk/test/foo/resources/foo.properties"),
|
|
Paths.get("classes/jdk/test/foo/resources/foo.properties"));
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testExtractDir() throws IOException {
|
|
if (Files.exists(Paths.get("extractTestDir")))
|
|
FileUtils.deleteFileTreeWithRetry(Paths.get("extractTestDir"));
|
|
Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
|
|
Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");
|
|
Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
|
|
Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");
|
|
|
|
jmod("create",
|
|
"--conf", cf.toString(),
|
|
"--cmds", bp.toString(),
|
|
"--libs", lp.toString(),
|
|
"--class-path", cp.toString(),
|
|
MODS_DIR.resolve("fooExtractDir.jmod").toString())
|
|
.assertSuccess();
|
|
|
|
jmod("extract",
|
|
"--dir", "extractTestDir",
|
|
MODS_DIR.resolve("fooExtractDir.jmod").toString())
|
|
.assertSuccess();
|
|
|
|
jmod("extract",
|
|
"--dir", "extractTestDir",
|
|
MODS_DIR.resolve("fooExtractDir.jmod").toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
// check a sample of the extracted files
|
|
Path p = Paths.get("extractTestDir");
|
|
assertTrue(Files.exists(p.resolve("classes/module-info.class")));
|
|
assertSameContent(cp.resolve("jdk/test/foo/Foo.class"),
|
|
p.resolve("classes/jdk/test/foo/Foo.class"));
|
|
assertSameContent(bp.resolve("first"),
|
|
p.resolve(CMDS_PREFIX).resolve("first"));
|
|
assertSameContent(lp.resolve("first.so"),
|
|
p.resolve(LIBS_PREFIX).resolve("second.so"));
|
|
assertSameContent(cf.resolve("second.cfg"),
|
|
p.resolve(CONFIGS_PREFIX).resolve("second.cfg"));
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testMainClass() throws IOException {
|
|
Path jmod = MODS_DIR.resolve("fooMainClass.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--main-class", "jdk.test.foo.Foo",
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
Optional<String> omc = getModuleDescriptor(jmod).mainClass();
|
|
assertTrue(omc.isPresent());
|
|
assertEquals(omc.get(), "jdk.test.foo.Foo");
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testModuleVersion() throws IOException {
|
|
Path jmod = MODS_DIR.resolve("fooVersion.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--module-version", "5.4.3",
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
Optional<Version> ov = getModuleDescriptor(jmod).version();
|
|
assertTrue(ov.isPresent());
|
|
assertEquals(ov.get().toString(), "5.4.3");
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testConfig() throws IOException {
|
|
Path jmod = MODS_DIR.resolve("fooConfig.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
|
|
Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");
|
|
|
|
jmod("create",
|
|
"--class-path", cp.toString(),
|
|
"--config", cf.toString(),
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
try (Stream<String> s1 = findFiles(cf).map(p -> CONFIGS_PREFIX + p);
|
|
Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {
|
|
Set<String> expectedFilenames = Stream.concat(s1, s2)
|
|
.collect(toSet());
|
|
assertJmodContent(jmod, expectedFilenames);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testCmds() throws IOException {
|
|
Path jmod = MODS_DIR.resolve("fooCmds.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
|
|
Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");
|
|
|
|
jmod("create",
|
|
"--cmds", bp.toString(),
|
|
"--class-path", cp.toString(),
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
try (Stream<String> s1 = findFiles(bp).map(p -> CMDS_PREFIX + p);
|
|
Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {
|
|
Set<String> expectedFilenames = Stream.concat(s1,s2)
|
|
.collect(toSet());
|
|
assertJmodContent(jmod, expectedFilenames);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testLibs() throws IOException {
|
|
Path jmod = MODS_DIR.resolve("fooLibs.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
|
|
Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
|
|
|
|
jmod("create",
|
|
"--libs=" + lp.toString(),
|
|
"--class-path", cp.toString(),
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);
|
|
Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {
|
|
Set<String> expectedFilenames = Stream.concat(s1,s2)
|
|
.collect(toSet());
|
|
assertJmodContent(jmod, expectedFilenames);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testAll() throws IOException {
|
|
Path jmod = MODS_DIR.resolve("fooAll.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
|
|
Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");
|
|
Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
|
|
Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");
|
|
|
|
jmod("create",
|
|
"--conf", cf.toString(),
|
|
"--cmds=" + bp.toString(),
|
|
"--libs=" + lp.toString(),
|
|
"--class-path", cp.toString(),
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);
|
|
Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p);
|
|
Stream<String> s3 = findFiles(bp).map(p -> CMDS_PREFIX + p);
|
|
Stream<String> s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) {
|
|
Set<String> expectedFilenames = Stream.concat(Stream.concat(s1,s2),
|
|
Stream.concat(s3, s4))
|
|
.collect(toSet());
|
|
assertJmodContent(jmod, expectedFilenames);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testExcludes() throws IOException {
|
|
Path jmod = MODS_DIR.resolve("fooLibs.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
|
|
Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
|
|
|
|
jmod("create",
|
|
"--libs=" + lp.toString(),
|
|
"--class-path", cp.toString(),
|
|
"--exclude", "**internal**",
|
|
"--exclude", "first.so",
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
Set<String> expectedFilenames = new HashSet<>();
|
|
expectedFilenames.add(CLASSES_PREFIX + "module-info.class");
|
|
expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/Foo.class");
|
|
expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties");
|
|
expectedFilenames.add(LIBS_PREFIX + "second.so");
|
|
expectedFilenames.add(LIBS_PREFIX + "third/third.so");
|
|
assertJmodContent(jmod, expectedFilenames);
|
|
|
|
Set<String> unexpectedFilenames = new HashSet<>();
|
|
unexpectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/internal/Message.class");
|
|
unexpectedFilenames.add(LIBS_PREFIX + "first.so");
|
|
assertJmodDoesNotContain(jmod, unexpectedFilenames);
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void describe() throws IOException {
|
|
String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
MODS_DIR.resolve("describeFoo.jmod").toString())
|
|
.assertSuccess();
|
|
|
|
jmod("describe",
|
|
MODS_DIR.resolve("describeFoo.jmod").toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
// Expect similar output: "foo... exports jdk.test.foo ...
|
|
// ... requires java.base mandated... contains jdk.test.foo.internal"
|
|
Pattern p = Pattern.compile("foo\\s+exports\\s+jdk.test.foo");
|
|
assertTrue(p.matcher(r.output).find(),
|
|
"Expecting to find \"foo... exports jdk.test.foo\"" +
|
|
"in output, but did not: [" + r.output + "]");
|
|
p = Pattern.compile(
|
|
"requires\\s+java.base\\s+mandated\\s+contains\\s+jdk.test.foo.internal");
|
|
assertTrue(p.matcher(r.output).find(),
|
|
"Expecting to find \"requires java.base mandated..., " +
|
|
"contains jdk.test.foo.internal ...\"" +
|
|
"in output, but did not: [" + r.output + "]");
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testDuplicateEntries() throws IOException {
|
|
Path jmod = MODS_DIR.resolve("testDuplicates.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
|
|
Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
|
|
|
|
jmod("create",
|
|
"--class-path", cp + pathSeparator + cp,
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r ->
|
|
assertContains(r.output, "Warning: ignoring duplicate entry")
|
|
);
|
|
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--libs", lp.toString() + pathSeparator + lp.toString(),
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r ->
|
|
assertContains(r.output, "Warning: ignoring duplicate entry")
|
|
);
|
|
}
|
|
|
|
@Test
|
|
public void testDuplicateEntriesFromJarFile() throws IOException {
|
|
String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
|
|
Path jar = Paths.get("foo.jar");
|
|
Path jmod = MODS_DIR.resolve("testDuplicates.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jar);
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
// create JAR file
|
|
assertTrue(JAR_TOOL.run(System.out, System.err, "cf", jar.toString(), "-C", cp, ".") == 0);
|
|
|
|
jmod("create",
|
|
"--class-path", jar.toString() + pathSeparator + jar.toString(),
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r ->
|
|
assertContains(r.output, "Warning: ignoring duplicate entry")
|
|
);
|
|
}
|
|
|
|
@Test
|
|
public void testIgnoreModuleInfoInOtherSections() throws IOException {
|
|
Path jmod = MODS_DIR.resolve("testIgnoreModuleInfoInOtherSections.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--libs", cp,
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r ->
|
|
assertContains(r.output, "Warning: ignoring entry")
|
|
);
|
|
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--cmds", cp,
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r ->
|
|
assertContains(r.output, "Warning: ignoring entry")
|
|
);
|
|
}
|
|
|
|
@Test
|
|
public void testLastOneWins() throws IOException {
|
|
Path workDir = Paths.get("lastOneWins");
|
|
if (Files.exists(workDir))
|
|
FileUtils.deleteFileTreeWithRetry(workDir);
|
|
Files.createDirectory(workDir);
|
|
Path jmod = MODS_DIR.resolve("lastOneWins.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
|
|
Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");
|
|
Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
|
|
Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");
|
|
|
|
Path shouldNotBeAdded = workDir.resolve("shouldNotBeAdded");
|
|
Files.createDirectory(shouldNotBeAdded);
|
|
Files.write(shouldNotBeAdded.resolve("aFile"), "hello".getBytes(UTF_8));
|
|
|
|
// Pairs of options. For options with required arguments the last one
|
|
// should win ( first should be effectively ignored, but may still be
|
|
// validated ).
|
|
jmod("create",
|
|
"--conf", shouldNotBeAdded.toString(),
|
|
"--conf", cf.toString(),
|
|
"--cmds", shouldNotBeAdded.toString(),
|
|
"--cmds", bp.toString(),
|
|
"--libs", shouldNotBeAdded.toString(),
|
|
"--libs", lp.toString(),
|
|
"--class-path", shouldNotBeAdded.toString(),
|
|
"--class-path", cp.toString(),
|
|
"--main-class", "does.NotExist",
|
|
"--main-class", "jdk.test.foo.Foo",
|
|
"--module-version", "00001",
|
|
"--module-version", "5.4.3",
|
|
"--do-not-resolve-by-default",
|
|
"--do-not-resolve-by-default",
|
|
"--warn-if-resolved=incubating",
|
|
"--warn-if-resolved=deprecated",
|
|
MODS_DIR.resolve("lastOneWins.jmod").toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
ModuleDescriptor md = getModuleDescriptor(jmod);
|
|
Optional<String> omc = md.mainClass();
|
|
assertTrue(omc.isPresent());
|
|
assertEquals(omc.get(), "jdk.test.foo.Foo");
|
|
Optional<Version> ov = md.version();
|
|
assertTrue(ov.isPresent());
|
|
assertEquals(ov.get().toString(), "5.4.3");
|
|
|
|
try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);
|
|
Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p);
|
|
Stream<String> s3 = findFiles(bp).map(p -> CMDS_PREFIX + p);
|
|
Stream<String> s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) {
|
|
Set<String> expectedFilenames = Stream.concat(Stream.concat(s1,s2),
|
|
Stream.concat(s3, s4))
|
|
.collect(toSet());
|
|
assertJmodContent(jmod, expectedFilenames);
|
|
}
|
|
});
|
|
|
|
jmod("extract",
|
|
"--dir", "blah",
|
|
"--dir", "lastOneWinsExtractDir",
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
assertTrue(Files.exists(Paths.get("lastOneWinsExtractDir")));
|
|
assertTrue(Files.notExists(Paths.get("blah")));
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testPackagesAttribute() throws IOException {
|
|
Path jmod = MODS_DIR.resolve("foo.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
|
|
|
|
Set<String> expectedPackages = Set.of("jdk.test.foo",
|
|
"jdk.test.foo.internal",
|
|
"jdk.test.foo.resources");
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
jmod.toString())
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
Set<String> pkgs = getModuleDescriptor(jmod).packages();
|
|
assertEquals(pkgs, expectedPackages);
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testVersion() {
|
|
jmod("--version")
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
assertContains(r.output, System.getProperty("java.version"));
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testHelp() {
|
|
jmod("--help")
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
assertTrue(r.output.startsWith("Usage: jmod"), "Help not printed");
|
|
assertFalse(r.output.contains("--do-not-resolve-by-default"));
|
|
assertFalse(r.output.contains("--warn-if-resolved"));
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testHelpExtra() {
|
|
jmod("--help-extra")
|
|
.assertSuccess()
|
|
.resultChecker(r -> {
|
|
assertTrue(r.output.startsWith("Usage: jmod"), "Extra help not printed");
|
|
assertContains(r.output, "--do-not-resolve-by-default");
|
|
assertContains(r.output, "--warn-if-resolved");
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testTmpFileRemoved() throws IOException {
|
|
// Implementation detail: jmod tool creates <jmod-file>.tmp
|
|
// Ensure that it is removed in the event of a failure.
|
|
// The failure in this case is a class in the unnamed package.
|
|
|
|
Path jmod = MODS_DIR.resolve("testTmpFileRemoved.jmod");
|
|
Path tmp = MODS_DIR.resolve(".testTmpFileRemoved.jmod.tmp");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
FileUtils.deleteFileIfExistsWithRetry(tmp);
|
|
String cp = EXPLODED_DIR.resolve("foo").resolve("classes") + File.pathSeparator +
|
|
EXPLODED_DIR.resolve("foo").resolve("classes")
|
|
.resolve("jdk").resolve("test").resolve("foo").toString();
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
jmod.toString())
|
|
.assertFailure()
|
|
.resultChecker(r -> {
|
|
assertContains(r.output, "unnamed package");
|
|
assertTrue(Files.notExists(tmp), "Unexpected tmp file:" + tmp);
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testCompressionLevel() throws IOException {
|
|
String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
|
|
Path jmod = MODS_DIR.resolve("foo.jmod");
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--compress", "zip-0",
|
|
jmod.toString())
|
|
.assertSuccess();
|
|
|
|
jmod("list",
|
|
"--compress", "zip-0",
|
|
jmod.toString())
|
|
.assertFailure()
|
|
.resultChecker(r -> {
|
|
assertTrue(r.output.contains("--compress is only accepted with create mode"), "Error message printed");
|
|
});
|
|
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--compress", "zip-9",
|
|
jmod.toString())
|
|
.assertSuccess();
|
|
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--compress", "zip--1",
|
|
jmod.toString())
|
|
.assertFailure()
|
|
.resultChecker(r -> {
|
|
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
|
|
});
|
|
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--compress", "zip-1-something",
|
|
jmod.toString())
|
|
.assertFailure()
|
|
.resultChecker(r -> {
|
|
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
|
|
});
|
|
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--compress", "zip-10",
|
|
jmod.toString())
|
|
.assertFailure()
|
|
.resultChecker(r -> {
|
|
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
|
|
});
|
|
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--compress", "zip-",
|
|
jmod.toString())
|
|
.assertFailure()
|
|
.resultChecker(r -> {
|
|
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
|
|
});
|
|
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--compress", "test",
|
|
jmod.toString())
|
|
.assertFailure()
|
|
.resultChecker(r -> {
|
|
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
|
|
});
|
|
|
|
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
|
|
|
jmod("create",
|
|
"--class-path", cp,
|
|
"--compress", "test-0",
|
|
jmod.toString())
|
|
.assertFailure()
|
|
.resultChecker(r -> {
|
|
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
|
|
});
|
|
}
|
|
|
|
// ---
|
|
|
|
static boolean compileModule(String name, Path dest) throws IOException {
|
|
return CompilerUtils.compile(SRC_DIR.resolve(name), dest);
|
|
}
|
|
|
|
static void assertContains(String output, String subString) {
|
|
if (output.contains(subString))
|
|
assertTrue(true);
|
|
else
|
|
assertTrue(false,"Expected to find [" + subString + "], in output ["
|
|
+ output + "]" + "\n");
|
|
}
|
|
|
|
static ModuleDescriptor getModuleDescriptor(Path jmod) {
|
|
ClassLoader cl = ClassLoader.getSystemClassLoader();
|
|
try (FileSystem fs = FileSystems.newFileSystem(jmod, cl)) {
|
|
String p = "/classes/module-info.class";
|
|
try (InputStream is = Files.newInputStream(fs.getPath(p))) {
|
|
return ModuleDescriptor.read(is);
|
|
}
|
|
} catch (IOException ioe) {
|
|
throw new UncheckedIOException(ioe);
|
|
}
|
|
}
|
|
|
|
static Stream<String> findFiles(Path dir) {
|
|
try {
|
|
return Files.find(dir, Integer.MAX_VALUE, (p, a) -> a.isRegularFile())
|
|
.map(dir::relativize)
|
|
.map(Path::toString)
|
|
.map(p -> p.replace(File.separator, "/"));
|
|
} catch (IOException x) {
|
|
throw new UncheckedIOException(x);
|
|
}
|
|
}
|
|
|
|
static Set<String> getJmodContent(Path jmod) {
|
|
JmodResult r = jmod("list", jmod.toString()).assertSuccess();
|
|
return Stream.of(r.output.split("\r?\n")).collect(toSet());
|
|
}
|
|
|
|
static void assertJmodContent(Path jmod, Set<String> expected) {
|
|
Set<String> actual = getJmodContent(jmod);
|
|
if (!Objects.equals(actual, expected)) {
|
|
Set<String> unexpected = new HashSet<>(actual);
|
|
unexpected.removeAll(expected);
|
|
Set<String> notFound = new HashSet<>(expected);
|
|
notFound.removeAll(actual);
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("Unexpected but found:\n");
|
|
unexpected.forEach(s -> sb.append("\t" + s + "\n"));
|
|
sb.append("Expected but not found:\n");
|
|
notFound.forEach(s -> sb.append("\t" + s + "\n"));
|
|
assertTrue(false, "Jmod content check failed.\n" + sb.toString());
|
|
}
|
|
}
|
|
|
|
static void assertJmodDoesNotContain(Path jmod, Set<String> unexpectedNames) {
|
|
Set<String> actual = getJmodContent(jmod);
|
|
Set<String> unexpected = new HashSet<>();
|
|
for (String name : unexpectedNames) {
|
|
if (actual.contains(name))
|
|
unexpected.add(name);
|
|
}
|
|
if (!unexpected.isEmpty()) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (String s : unexpected)
|
|
sb.append("Unexpected but found: " + s + "\n");
|
|
sb.append("In :");
|
|
for (String s : actual)
|
|
sb.append("\t" + s + "\n");
|
|
assertTrue(false, "Jmod content check failed.\n" + sb.toString());
|
|
}
|
|
}
|
|
|
|
static void assertSameContent(Path p1, Path p2) {
|
|
try {
|
|
byte[] ba1 = Files.readAllBytes(p1);
|
|
byte[] ba2 = Files.readAllBytes(p2);
|
|
assertEquals(ba1, ba2);
|
|
} catch (IOException x) {
|
|
throw new UncheckedIOException(x);
|
|
}
|
|
}
|
|
|
|
static JmodResult jmod(String... args) {
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
PrintStream ps = new PrintStream(baos);
|
|
System.out.println("jmod " + Arrays.asList(args));
|
|
int ec = JMOD_TOOL.run(ps, ps, args);
|
|
return new JmodResult(ec, new String(baos.toByteArray(), UTF_8));
|
|
}
|
|
|
|
static class JmodResult {
|
|
final int exitCode;
|
|
final String output;
|
|
|
|
JmodResult(int exitValue, String output) {
|
|
this.exitCode = exitValue;
|
|
this.output = output;
|
|
}
|
|
JmodResult assertSuccess() { assertTrue(exitCode == 0, output); return this; }
|
|
JmodResult assertFailure() { assertTrue(exitCode != 0, output); return this; }
|
|
JmodResult resultChecker(Consumer<JmodResult> r) { r.accept(this); return this; }
|
|
}
|
|
|
|
static void createCmds(Path dir) throws IOException {
|
|
List<String> files = Arrays.asList(
|
|
"first", "second", "third" + File.separator + "third");
|
|
createFiles(dir, files);
|
|
}
|
|
|
|
static void createLibs(Path dir) throws IOException {
|
|
List<String> files = Arrays.asList(
|
|
"first.so", "second.so", "third" + File.separator + "third.so");
|
|
createFiles(dir, files);
|
|
}
|
|
|
|
static void createConfigs(Path dir) throws IOException {
|
|
List<String> files = Arrays.asList(
|
|
"first.cfg", "second.cfg", "third" + File.separator + "third.cfg");
|
|
createFiles(dir, files);
|
|
}
|
|
|
|
static void createFiles(Path dir, List<String> filenames) throws IOException {
|
|
for (String name : filenames) {
|
|
Path file = dir.resolve(name);
|
|
Files.createDirectories(file.getParent());
|
|
Files.createFile(file);
|
|
try (OutputStream os = Files.newOutputStream(file)) {
|
|
os.write("blahblahblah".getBytes(UTF_8));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void copyResource(Path srcDir, Path dir, String resource) throws IOException {
|
|
Path dest = dir.resolve(resource);
|
|
Files.deleteIfExists(dest);
|
|
|
|
Files.createDirectories(dest.getParent());
|
|
Files.copy(srcDir.resolve(resource), dest);
|
|
}
|
|
|
|
// Standalone entry point.
|
|
public static void main(String[] args) throws Throwable {
|
|
JmodTest test = new JmodTest();
|
|
test.buildExplodedModules();
|
|
for (Method m : JmodTest.class.getDeclaredMethods()) {
|
|
if (m.getAnnotation(Test.class) != null) {
|
|
System.out.println("Invoking " + m.getName());
|
|
m.invoke(test);
|
|
}
|
|
}
|
|
}
|
|
}
|