/** * Copyright (c) 2015, 2017, 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 * @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.Utils * jdk.test.lib.Asserts * jdk.test.lib.JDKToolFinder * jdk.test.lib.JDKToolLauncher * jdk.test.lib.Platform * jdk.test.lib.process.* * @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 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 testSymlinks() 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 (UnsupportedOperationException uoe) { // OS does not support symlinks. Nothing to test! return; } Path jmod = MODS_DIR.resolve("apa.jmod"); jmod("create", "--libs=", libDir.toString(), "--class-path", classesDir.toString(), jmod.toString()) .assertSuccess(); } // 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(); jmod("create", "--class-path", cp, MODS_DIR.resolve("foo.jmod").toString()) .assertSuccess(); jmod("list", MODS_DIR.resolve("foo.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"); }); } @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 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 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 s1 = findFiles(cf).map(p -> CONFIGS_PREFIX + p); Stream s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) { Set 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 s1 = findFiles(bp).map(p -> CMDS_PREFIX + p); Stream s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) { Set 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 s1 = findFiles(lp).map(p -> LIBS_PREFIX + p); Stream s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) { Set 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 s1 = findFiles(lp).map(p -> LIBS_PREFIX + p); Stream s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p); Stream s3 = findFiles(bp).map(p -> CMDS_PREFIX + p); Stream s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) { Set 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 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 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 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 omc = md.mainClass(); assertTrue(omc.isPresent()); assertEquals(omc.get(), "jdk.test.foo.Foo"); Optional ov = md.version(); assertTrue(ov.isPresent()); assertEquals(ov.get().toString(), "5.4.3"); try (Stream s1 = findFiles(lp).map(p -> LIBS_PREFIX + p); Stream s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p); Stream s3 = findFiles(bp).map(p -> CMDS_PREFIX + p); Stream s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) { Set 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 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 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 .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); }); } // --- 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 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 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 expected) { Set actual = getJmodContent(jmod); if (!Objects.equals(actual, expected)) { Set unexpected = new HashSet<>(actual); unexpected.removeAll(expected); Set 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 unexpectedNames) { Set actual = getJmodContent(jmod); Set 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 r) { r.accept(this); return this; } } static void createCmds(Path dir) throws IOException { List files = Arrays.asList( "first", "second", "third" + File.separator + "third"); createFiles(dir, files); } static void createLibs(Path dir) throws IOException { List files = Arrays.asList( "first.so", "second.so", "third" + File.separator + "third.so"); createFiles(dir, files); } static void createConfigs(Path dir) throws IOException { List files = Arrays.asList( "first.cfg", "second.cfg", "third" + File.separator + "third.cfg"); createFiles(dir, files); } static void createFiles(Path dir, List 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); } } } }