Path: blob/master/test/jdk/tools/jmod/JmodTest.java
66643 views
/*1* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 8142968 8166568 8166286 8170618 8168149 8240910 8276764 827676626* @summary Basic test for jmod27* @library /test/lib28* @modules jdk.compiler29* jdk.jlink30* @build jdk.test.lib.compiler.CompilerUtils31* jdk.test.lib.util.FileUtils32* jdk.test.lib.Platform33* @run testng/othervm -Djava.io.tmpdir=. JmodTest34*/3536import java.io.*;37import java.lang.module.ModuleDescriptor;38import java.lang.reflect.Method;39import java.nio.file.*;40import java.util.*;41import java.util.function.Consumer;42import java.util.regex.Pattern;43import java.util.spi.ToolProvider;44import java.util.stream.Stream;45import jdk.test.lib.compiler.CompilerUtils;46import jdk.test.lib.util.FileUtils;47import org.testng.annotations.BeforeTest;48import org.testng.annotations.Test;4950import static java.io.File.pathSeparator;51import static java.lang.module.ModuleDescriptor.Version;52import static java.nio.charset.StandardCharsets.UTF_8;53import static java.util.stream.Collectors.toSet;54import static org.testng.Assert.*;5556public class JmodTest {5758static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")59.orElseThrow(() ->60new RuntimeException("jmod tool not found")61);62static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")63.orElseThrow(() ->64new RuntimeException("jar tool not found")65);6667static final String TEST_SRC = System.getProperty("test.src", ".");68static final Path SRC_DIR = Paths.get(TEST_SRC, "src");69static final Path EXPLODED_DIR = Paths.get("build");70static final Path MODS_DIR = Paths.get("jmods");7172static final String CLASSES_PREFIX = "classes/";73static final String CMDS_PREFIX = "bin/";74static final String LIBS_PREFIX = "lib/";75static final String CONFIGS_PREFIX = "conf/";7677@BeforeTest78public void buildExplodedModules() throws IOException {79if (Files.exists(EXPLODED_DIR))80FileUtils.deleteFileTreeWithRetry(EXPLODED_DIR);8182for (String name : new String[] { "foo"/*, "bar", "baz"*/ } ) {83Path dir = EXPLODED_DIR.resolve(name);84assertTrue(compileModule(name, dir.resolve("classes")));85copyResource(SRC_DIR.resolve("foo"),86dir.resolve("classes"),87"jdk/test/foo/resources/foo.properties");88createCmds(dir.resolve("bin"));89createLibs(dir.resolve("lib"));90createConfigs(dir.resolve("conf"));91}9293if (Files.exists(MODS_DIR))94FileUtils.deleteFileTreeWithRetry(MODS_DIR);95Files.createDirectories(MODS_DIR);96}9798// JDK-8166286 - jmod fails on symlink to directory99@Test100public void testDirSymlinks() throws IOException {101Path apaDir = EXPLODED_DIR.resolve("apa");102Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes");103assertTrue(compileModule("apa", classesDir));104Path libDir = apaDir.resolve("lib");105createFiles(libDir, List.of("foo/bar/libfoo.so"));106try {107Path link = Files.createSymbolicLink(108libDir.resolve("baz"), libDir.resolve("foo").toAbsolutePath());109assertTrue(Files.exists(link));110} catch (IOException|UnsupportedOperationException uoe) {111// OS does not support symlinks. Nothing to test!112System.out.println("Creating symlink failed. Test passes vacuously.");113uoe.printStackTrace();114return;115}116117Path jmod = MODS_DIR.resolve("apa.jmod");118jmod("create",119"--libs=" + libDir.toString(),120"--class-path", classesDir.toString(),121jmod.toString())122.assertSuccess();123Files.delete(jmod);124}125126// JDK-8267583 - jmod fails on symlink to class file127@Test128public void testFileSymlinks() throws IOException {129Path apaDir = EXPLODED_DIR.resolve("apa");130Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes");131assertTrue(compileModule("apa", classesDir));132133Files.move(classesDir.resolve("module-info.class"),134classesDir.resolve("module-info.class1"));135Files.move(classesDir.resolve(Paths.get("jdk", "test", "apa", "Apa.class")),136classesDir.resolve("Apa.class1"));137try {138Path link = Files.createSymbolicLink(139classesDir.resolve("module-info.class"),140classesDir.resolve("module-info.class1").toAbsolutePath());141assertTrue(Files.exists(link));142link = Files.createSymbolicLink(143classesDir.resolve(Paths.get("jdk", "test", "apa", "Apa.class")),144classesDir.resolve("Apa.class1").toAbsolutePath());145assertTrue(Files.exists(link));146} catch (IOException|UnsupportedOperationException uoe) {147// OS does not support symlinks. Nothing to test!148System.out.println("Creating symlinks failed. Test passes vacuously.");149uoe.printStackTrace();150return;151}152153Path jmod = MODS_DIR.resolve("apa.jmod");154jmod("create",155"--class-path", classesDir.toString(),156jmod.toString())157.assertSuccess();158Files.delete(jmod);159}160161// JDK-8170618 - jmod should validate if any exported or open package is missing162@Test163public void testMissingPackages() throws IOException {164Path apaDir = EXPLODED_DIR.resolve("apa");165Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes");166if (Files.exists(classesDir))167FileUtils.deleteFileTreeWithRetry(classesDir);168assertTrue(compileModule("apa", classesDir));169FileUtils.deleteFileTreeWithRetry(classesDir.resolve("jdk"));170Path jmod = MODS_DIR.resolve("apa.jmod");171jmod("create",172"--class-path", classesDir.toString(),173jmod.toString())174.assertFailure()175.resultChecker(r -> {176assertContains(r.output, "Packages that are exported or open in apa are not present: [jdk.test.apa]");177});178if (Files.exists(classesDir))179FileUtils.deleteFileTreeWithRetry(classesDir);180}181182@Test183public void testList() throws IOException {184String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();185Path jmod = MODS_DIR.resolve("foo.jmod");186FileUtils.deleteFileIfExistsWithRetry(jmod);187jmod("create",188"--class-path", cp,189jmod.toString())190.assertSuccess();191192jmod("list",193jmod.toString())194.assertSuccess()195.resultChecker(r -> {196// asserts dependent on the exact contents of foo197assertContains(r.output, CLASSES_PREFIX + "module-info.class");198assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/Foo.class");199assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/internal/Message.class");200assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties");201202// JDK-8276764: Ensure the order is sorted for reproducible jmod content203// module-info, followed by <sorted classes>204int mod_info_i = r.output.indexOf(CLASSES_PREFIX + "module-info.class");205int foo_cls_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/Foo.class");206int msg_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/internal/Message.class");207int res_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties");208System.out.println("jmod classes sort order check:\n"+r.output);209assertTrue(mod_info_i < foo_cls_i);210assertTrue(foo_cls_i < msg_i);211assertTrue(msg_i < res_i);212});213}214215@Test216public void testSourceDateReproducible() throws IOException {217String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();218Path jmod1 = MODS_DIR.resolve("foo1.jmod");219Path jmod2 = MODS_DIR.resolve("foo2.jmod");220Path jmod3 = MODS_DIR.resolve("foo3.jmod");221FileUtils.deleteFileIfExistsWithRetry(jmod1);222FileUtils.deleteFileIfExistsWithRetry(jmod2);223FileUtils.deleteFileIfExistsWithRetry(jmod3);224225// Use source date of 15/03/2022226String sourceDate = "2022-03-15T00:00:00+00:00";227228jmod("create",229"--class-path", cp,230"--date", sourceDate,231jmod1.toString())232.assertSuccess();233234try {235// Sleep 5 seconds to ensure zip timestamps might be different if they could be236Thread.sleep(5000);237} catch(InterruptedException ex) {}238239jmod("create",240"--class-path", cp,241"--date", sourceDate,242jmod2.toString())243.assertSuccess();244245// Compare file byte content to see if they are identical246assertSameContent(jmod1, jmod2);247248// Use a date before 1980 and assert failure error249sourceDate = "1976-03-15T00:00:00+00:00";250251jmod("create",252"--class-path", cp,253"--date", sourceDate,254jmod3.toString())255.assertFailure()256.resultChecker(r -> {257assertContains(r.output, "is out of the valid range");258});259260// Use a date after 2099 and assert failure error261sourceDate = "2100-03-15T00:00:00+00:00";262263jmod("create",264"--class-path", cp,265"--date", sourceDate,266jmod3.toString())267.assertFailure()268.resultChecker(r -> {269assertContains(r.output, "is out of the valid range");270});271}272273@Test274public void testExtractCWD() throws IOException {275Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");276jmod("create",277"--class-path", cp.toString(),278MODS_DIR.resolve("fooExtractCWD.jmod").toString())279.assertSuccess();280281jmod("extract",282MODS_DIR.resolve("fooExtractCWD.jmod").toString())283.assertSuccess()284.resultChecker(r -> {285// module-info should exist, but jmod will have added its Packages attr.286assertTrue(Files.exists(Paths.get("classes/module-info.class")));287assertSameContent(cp.resolve("jdk/test/foo/Foo.class"),288Paths.get("classes/jdk/test/foo/Foo.class"));289assertSameContent(cp.resolve("jdk/test/foo/internal/Message.class"),290Paths.get("classes/jdk/test/foo/internal/Message.class"));291assertSameContent(cp.resolve("jdk/test/foo/resources/foo.properties"),292Paths.get("classes/jdk/test/foo/resources/foo.properties"));293});294}295296@Test297public void testExtractDir() throws IOException {298if (Files.exists(Paths.get("extractTestDir")))299FileUtils.deleteFileTreeWithRetry(Paths.get("extractTestDir"));300Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");301Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");302Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");303Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");304305jmod("create",306"--conf", cf.toString(),307"--cmds", bp.toString(),308"--libs", lp.toString(),309"--class-path", cp.toString(),310MODS_DIR.resolve("fooExtractDir.jmod").toString())311.assertSuccess();312313jmod("extract",314"--dir", "extractTestDir",315MODS_DIR.resolve("fooExtractDir.jmod").toString())316.assertSuccess();317318jmod("extract",319"--dir", "extractTestDir",320MODS_DIR.resolve("fooExtractDir.jmod").toString())321.assertSuccess()322.resultChecker(r -> {323// check a sample of the extracted files324Path p = Paths.get("extractTestDir");325assertTrue(Files.exists(p.resolve("classes/module-info.class")));326assertSameContent(cp.resolve("jdk/test/foo/Foo.class"),327p.resolve("classes/jdk/test/foo/Foo.class"));328assertSameContent(bp.resolve("first"),329p.resolve(CMDS_PREFIX).resolve("first"));330assertSameContent(lp.resolve("first.so"),331p.resolve(LIBS_PREFIX).resolve("second.so"));332assertSameContent(cf.resolve("second.cfg"),333p.resolve(CONFIGS_PREFIX).resolve("second.cfg"));334});335}336337@Test338public void testMainClass() throws IOException {339Path jmod = MODS_DIR.resolve("fooMainClass.jmod");340FileUtils.deleteFileIfExistsWithRetry(jmod);341String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();342343jmod("create",344"--class-path", cp,345"--main-class", "jdk.test.foo.Foo",346jmod.toString())347.assertSuccess()348.resultChecker(r -> {349Optional<String> omc = getModuleDescriptor(jmod).mainClass();350assertTrue(omc.isPresent());351assertEquals(omc.get(), "jdk.test.foo.Foo");352});353}354355@Test356public void testModuleVersion() throws IOException {357Path jmod = MODS_DIR.resolve("fooVersion.jmod");358FileUtils.deleteFileIfExistsWithRetry(jmod);359String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();360361jmod("create",362"--class-path", cp,363"--module-version", "5.4.3",364jmod.toString())365.assertSuccess()366.resultChecker(r -> {367Optional<Version> ov = getModuleDescriptor(jmod).version();368assertTrue(ov.isPresent());369assertEquals(ov.get().toString(), "5.4.3");370});371}372373@Test374public void testConfig() throws IOException {375Path jmod = MODS_DIR.resolve("fooConfig.jmod");376FileUtils.deleteFileIfExistsWithRetry(jmod);377Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");378Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");379380jmod("create",381"--class-path", cp.toString(),382"--config", cf.toString(),383jmod.toString())384.assertSuccess()385.resultChecker(r -> {386try (Stream<String> s1 = findFiles(cf).map(p -> CONFIGS_PREFIX + p);387Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {388Set<String> expectedFilenames = Stream.concat(s1, s2)389.collect(toSet());390assertJmodContent(jmod, expectedFilenames);391}392});393}394395@Test396public void testCmds() throws IOException {397Path jmod = MODS_DIR.resolve("fooCmds.jmod");398FileUtils.deleteFileIfExistsWithRetry(jmod);399Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");400Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");401402jmod("create",403"--cmds", bp.toString(),404"--class-path", cp.toString(),405jmod.toString())406.assertSuccess()407.resultChecker(r -> {408try (Stream<String> s1 = findFiles(bp).map(p -> CMDS_PREFIX + p);409Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {410Set<String> expectedFilenames = Stream.concat(s1,s2)411.collect(toSet());412assertJmodContent(jmod, expectedFilenames);413}414});415}416417@Test418public void testLibs() throws IOException {419Path jmod = MODS_DIR.resolve("fooLibs.jmod");420FileUtils.deleteFileIfExistsWithRetry(jmod);421Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");422Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");423424jmod("create",425"--libs=" + lp.toString(),426"--class-path", cp.toString(),427jmod.toString())428.assertSuccess()429.resultChecker(r -> {430try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);431Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {432Set<String> expectedFilenames = Stream.concat(s1,s2)433.collect(toSet());434assertJmodContent(jmod, expectedFilenames);435}436});437}438439@Test440public void testAll() throws IOException {441Path jmod = MODS_DIR.resolve("fooAll.jmod");442FileUtils.deleteFileIfExistsWithRetry(jmod);443Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");444Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");445Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");446Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");447448jmod("create",449"--conf", cf.toString(),450"--cmds=" + bp.toString(),451"--libs=" + lp.toString(),452"--class-path", cp.toString(),453jmod.toString())454.assertSuccess()455.resultChecker(r -> {456try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);457Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p);458Stream<String> s3 = findFiles(bp).map(p -> CMDS_PREFIX + p);459Stream<String> s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) {460Set<String> expectedFilenames = Stream.concat(Stream.concat(s1,s2),461Stream.concat(s3, s4))462.collect(toSet());463assertJmodContent(jmod, expectedFilenames);464}465});466}467468@Test469public void testExcludes() throws IOException {470Path jmod = MODS_DIR.resolve("fooLibs.jmod");471FileUtils.deleteFileIfExistsWithRetry(jmod);472Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");473Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");474475jmod("create",476"--libs=" + lp.toString(),477"--class-path", cp.toString(),478"--exclude", "**internal**",479"--exclude", "first.so",480jmod.toString())481.assertSuccess()482.resultChecker(r -> {483Set<String> expectedFilenames = new HashSet<>();484expectedFilenames.add(CLASSES_PREFIX + "module-info.class");485expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/Foo.class");486expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties");487expectedFilenames.add(LIBS_PREFIX + "second.so");488expectedFilenames.add(LIBS_PREFIX + "third/third.so");489assertJmodContent(jmod, expectedFilenames);490491Set<String> unexpectedFilenames = new HashSet<>();492unexpectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/internal/Message.class");493unexpectedFilenames.add(LIBS_PREFIX + "first.so");494assertJmodDoesNotContain(jmod, unexpectedFilenames);495});496}497498@Test499public void describe() throws IOException {500String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();501jmod("create",502"--class-path", cp,503MODS_DIR.resolve("describeFoo.jmod").toString())504.assertSuccess();505506jmod("describe",507MODS_DIR.resolve("describeFoo.jmod").toString())508.assertSuccess()509.resultChecker(r -> {510// Expect similar output: "foo... exports jdk.test.foo ...511// ... requires java.base mandated... contains jdk.test.foo.internal"512Pattern p = Pattern.compile("foo\\s+exports\\s+jdk.test.foo");513assertTrue(p.matcher(r.output).find(),514"Expecting to find \"foo... exports jdk.test.foo\"" +515"in output, but did not: [" + r.output + "]");516p = Pattern.compile(517"requires\\s+java.base\\s+mandated\\s+contains\\s+jdk.test.foo.internal");518assertTrue(p.matcher(r.output).find(),519"Expecting to find \"requires java.base mandated..., " +520"contains jdk.test.foo.internal ...\"" +521"in output, but did not: [" + r.output + "]");522});523}524525@Test526public void testDuplicateEntries() throws IOException {527Path jmod = MODS_DIR.resolve("testDuplicates.jmod");528FileUtils.deleteFileIfExistsWithRetry(jmod);529String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();530Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");531532jmod("create",533"--class-path", cp + pathSeparator + cp,534jmod.toString())535.assertSuccess()536.resultChecker(r ->537assertContains(r.output, "Warning: ignoring duplicate entry")538);539540FileUtils.deleteFileIfExistsWithRetry(jmod);541jmod("create",542"--class-path", cp,543"--libs", lp.toString() + pathSeparator + lp.toString(),544jmod.toString())545.assertSuccess()546.resultChecker(r ->547assertContains(r.output, "Warning: ignoring duplicate entry")548);549}550551@Test552public void testDuplicateEntriesFromJarFile() throws IOException {553String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();554Path jar = Paths.get("foo.jar");555Path jmod = MODS_DIR.resolve("testDuplicates.jmod");556FileUtils.deleteFileIfExistsWithRetry(jar);557FileUtils.deleteFileIfExistsWithRetry(jmod);558// create JAR file559assertTrue(JAR_TOOL.run(System.out, System.err, "cf", jar.toString(), "-C", cp, ".") == 0);560561jmod("create",562"--class-path", jar.toString() + pathSeparator + jar.toString(),563jmod.toString())564.assertSuccess()565.resultChecker(r ->566assertContains(r.output, "Warning: ignoring duplicate entry")567);568}569570@Test571public void testIgnoreModuleInfoInOtherSections() throws IOException {572Path jmod = MODS_DIR.resolve("testIgnoreModuleInfoInOtherSections.jmod");573FileUtils.deleteFileIfExistsWithRetry(jmod);574String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();575576jmod("create",577"--class-path", cp,578"--libs", cp,579jmod.toString())580.assertSuccess()581.resultChecker(r ->582assertContains(r.output, "Warning: ignoring entry")583);584585FileUtils.deleteFileIfExistsWithRetry(jmod);586jmod("create",587"--class-path", cp,588"--cmds", cp,589jmod.toString())590.assertSuccess()591.resultChecker(r ->592assertContains(r.output, "Warning: ignoring entry")593);594}595596@Test597public void testLastOneWins() throws IOException {598Path workDir = Paths.get("lastOneWins");599if (Files.exists(workDir))600FileUtils.deleteFileTreeWithRetry(workDir);601Files.createDirectory(workDir);602Path jmod = MODS_DIR.resolve("lastOneWins.jmod");603FileUtils.deleteFileIfExistsWithRetry(jmod);604Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");605Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");606Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");607Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");608609Path shouldNotBeAdded = workDir.resolve("shouldNotBeAdded");610Files.createDirectory(shouldNotBeAdded);611Files.write(shouldNotBeAdded.resolve("aFile"), "hello".getBytes(UTF_8));612613// Pairs of options. For options with required arguments the last one614// should win ( first should be effectively ignored, but may still be615// validated ).616jmod("create",617"--conf", shouldNotBeAdded.toString(),618"--conf", cf.toString(),619"--cmds", shouldNotBeAdded.toString(),620"--cmds", bp.toString(),621"--libs", shouldNotBeAdded.toString(),622"--libs", lp.toString(),623"--class-path", shouldNotBeAdded.toString(),624"--class-path", cp.toString(),625"--main-class", "does.NotExist",626"--main-class", "jdk.test.foo.Foo",627"--module-version", "00001",628"--module-version", "5.4.3",629"--do-not-resolve-by-default",630"--do-not-resolve-by-default",631"--warn-if-resolved=incubating",632"--warn-if-resolved=deprecated",633MODS_DIR.resolve("lastOneWins.jmod").toString())634.assertSuccess()635.resultChecker(r -> {636ModuleDescriptor md = getModuleDescriptor(jmod);637Optional<String> omc = md.mainClass();638assertTrue(omc.isPresent());639assertEquals(omc.get(), "jdk.test.foo.Foo");640Optional<Version> ov = md.version();641assertTrue(ov.isPresent());642assertEquals(ov.get().toString(), "5.4.3");643644try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);645Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p);646Stream<String> s3 = findFiles(bp).map(p -> CMDS_PREFIX + p);647Stream<String> s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) {648Set<String> expectedFilenames = Stream.concat(Stream.concat(s1,s2),649Stream.concat(s3, s4))650.collect(toSet());651assertJmodContent(jmod, expectedFilenames);652}653});654655jmod("extract",656"--dir", "blah",657"--dir", "lastOneWinsExtractDir",658jmod.toString())659.assertSuccess()660.resultChecker(r -> {661assertTrue(Files.exists(Paths.get("lastOneWinsExtractDir")));662assertTrue(Files.notExists(Paths.get("blah")));663});664}665666@Test667public void testPackagesAttribute() throws IOException {668Path jmod = MODS_DIR.resolve("foo.jmod");669FileUtils.deleteFileIfExistsWithRetry(jmod);670String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();671672Set<String> expectedPackages = Set.of("jdk.test.foo",673"jdk.test.foo.internal",674"jdk.test.foo.resources");675676jmod("create",677"--class-path", cp,678jmod.toString())679.assertSuccess()680.resultChecker(r -> {681Set<String> pkgs = getModuleDescriptor(jmod).packages();682assertEquals(pkgs, expectedPackages);683});684}685686@Test687public void testVersion() {688jmod("--version")689.assertSuccess()690.resultChecker(r -> {691assertContains(r.output, System.getProperty("java.version"));692});693}694695@Test696public void testHelp() {697jmod("--help")698.assertSuccess()699.resultChecker(r -> {700assertTrue(r.output.startsWith("Usage: jmod"), "Help not printed");701assertFalse(r.output.contains("--do-not-resolve-by-default"));702assertFalse(r.output.contains("--warn-if-resolved"));703});704}705706@Test707public void testHelpExtra() {708jmod("--help-extra")709.assertSuccess()710.resultChecker(r -> {711assertTrue(r.output.startsWith("Usage: jmod"), "Extra help not printed");712assertContains(r.output, "--do-not-resolve-by-default");713assertContains(r.output, "--warn-if-resolved");714});715}716717@Test718public void testTmpFileRemoved() throws IOException {719// Implementation detail: jmod tool creates <jmod-file>.tmp720// Ensure that it is removed in the event of a failure.721// The failure in this case is a class in the unnamed package.722723Path jmod = MODS_DIR.resolve("testTmpFileRemoved.jmod");724Path tmp = MODS_DIR.resolve(".testTmpFileRemoved.jmod.tmp");725FileUtils.deleteFileIfExistsWithRetry(jmod);726FileUtils.deleteFileIfExistsWithRetry(tmp);727String cp = EXPLODED_DIR.resolve("foo").resolve("classes") + File.pathSeparator +728EXPLODED_DIR.resolve("foo").resolve("classes")729.resolve("jdk").resolve("test").resolve("foo").toString();730731jmod("create",732"--class-path", cp,733jmod.toString())734.assertFailure()735.resultChecker(r -> {736assertContains(r.output, "unnamed package");737assertTrue(Files.notExists(tmp), "Unexpected tmp file:" + tmp);738});739}740741// ---742743static boolean compileModule(String name, Path dest) throws IOException {744return CompilerUtils.compile(SRC_DIR.resolve(name), dest);745}746747static void assertContains(String output, String subString) {748if (output.contains(subString))749assertTrue(true);750else751assertTrue(false,"Expected to find [" + subString + "], in output ["752+ output + "]" + "\n");753}754755static ModuleDescriptor getModuleDescriptor(Path jmod) {756ClassLoader cl = ClassLoader.getSystemClassLoader();757try (FileSystem fs = FileSystems.newFileSystem(jmod, cl)) {758String p = "/classes/module-info.class";759try (InputStream is = Files.newInputStream(fs.getPath(p))) {760return ModuleDescriptor.read(is);761}762} catch (IOException ioe) {763throw new UncheckedIOException(ioe);764}765}766767static Stream<String> findFiles(Path dir) {768try {769return Files.find(dir, Integer.MAX_VALUE, (p, a) -> a.isRegularFile())770.map(dir::relativize)771.map(Path::toString)772.map(p -> p.replace(File.separator, "/"));773} catch (IOException x) {774throw new UncheckedIOException(x);775}776}777778static Set<String> getJmodContent(Path jmod) {779JmodResult r = jmod("list", jmod.toString()).assertSuccess();780return Stream.of(r.output.split("\r?\n")).collect(toSet());781}782783static void assertJmodContent(Path jmod, Set<String> expected) {784Set<String> actual = getJmodContent(jmod);785if (!Objects.equals(actual, expected)) {786Set<String> unexpected = new HashSet<>(actual);787unexpected.removeAll(expected);788Set<String> notFound = new HashSet<>(expected);789notFound.removeAll(actual);790StringBuilder sb = new StringBuilder();791sb.append("Unexpected but found:\n");792unexpected.forEach(s -> sb.append("\t" + s + "\n"));793sb.append("Expected but not found:\n");794notFound.forEach(s -> sb.append("\t" + s + "\n"));795assertTrue(false, "Jmod content check failed.\n" + sb.toString());796}797}798799static void assertJmodDoesNotContain(Path jmod, Set<String> unexpectedNames) {800Set<String> actual = getJmodContent(jmod);801Set<String> unexpected = new HashSet<>();802for (String name : unexpectedNames) {803if (actual.contains(name))804unexpected.add(name);805}806if (!unexpected.isEmpty()) {807StringBuilder sb = new StringBuilder();808for (String s : unexpected)809sb.append("Unexpected but found: " + s + "\n");810sb.append("In :");811for (String s : actual)812sb.append("\t" + s + "\n");813assertTrue(false, "Jmod content check failed.\n" + sb.toString());814}815}816817static void assertSameContent(Path p1, Path p2) {818try {819byte[] ba1 = Files.readAllBytes(p1);820byte[] ba2 = Files.readAllBytes(p2);821assertEquals(ba1, ba2);822} catch (IOException x) {823throw new UncheckedIOException(x);824}825}826827static JmodResult jmod(String... args) {828ByteArrayOutputStream baos = new ByteArrayOutputStream();829PrintStream ps = new PrintStream(baos);830System.out.println("jmod " + Arrays.asList(args));831int ec = JMOD_TOOL.run(ps, ps, args);832return new JmodResult(ec, new String(baos.toByteArray(), UTF_8));833}834835static class JmodResult {836final int exitCode;837final String output;838839JmodResult(int exitValue, String output) {840this.exitCode = exitValue;841this.output = output;842}843JmodResult assertSuccess() { assertTrue(exitCode == 0, output); return this; }844JmodResult assertFailure() { assertTrue(exitCode != 0, output); return this; }845JmodResult resultChecker(Consumer<JmodResult> r) { r.accept(this); return this; }846}847848static void createCmds(Path dir) throws IOException {849List<String> files = Arrays.asList(850"first", "second", "third" + File.separator + "third");851createFiles(dir, files);852}853854static void createLibs(Path dir) throws IOException {855List<String> files = Arrays.asList(856"first.so", "second.so", "third" + File.separator + "third.so");857createFiles(dir, files);858}859860static void createConfigs(Path dir) throws IOException {861List<String> files = Arrays.asList(862"first.cfg", "second.cfg", "third" + File.separator + "third.cfg");863createFiles(dir, files);864}865866static void createFiles(Path dir, List<String> filenames) throws IOException {867for (String name : filenames) {868Path file = dir.resolve(name);869Files.createDirectories(file.getParent());870Files.createFile(file);871try (OutputStream os = Files.newOutputStream(file)) {872os.write("blahblahblah".getBytes(UTF_8));873}874}875}876877static void copyResource(Path srcDir, Path dir, String resource) throws IOException {878Path dest = dir.resolve(resource);879Files.deleteIfExists(dest);880881Files.createDirectories(dest.getParent());882Files.copy(srcDir.resolve(resource), dest);883}884885// Standalone entry point.886public static void main(String[] args) throws Throwable {887JmodTest test = new JmodTest();888test.buildExplodedModules();889for (Method m : JmodTest.class.getDeclaredMethods()) {890if (m.getAnnotation(Test.class) != null) {891System.out.println("Invoking " + m.getName());892m.invoke(test);893}894}895}896}897898899