Path: blob/master/test/jdk/tools/jmod/hashes/HashesTest.java
41149 views
/*1* Copyright (c) 2015, 2021, 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 8160286 8243666 821752726* @summary Test the recording and checking of module hashes27* @library /test/lib28* @modules java.base/jdk.internal.misc29* java.base/jdk.internal.module30* jdk.compiler31* jdk.jartool32* jdk.jlink33* @build jdk.test.lib.compiler.ModuleInfoMaker34* jdk.test.lib.compiler.CompilerUtils35* @run testng HashesTest36*/3738import java.io.File;39import java.io.IOException;40import java.io.InputStream;41import java.io.UncheckedIOException;42import java.lang.module.ModuleDescriptor;43import java.lang.module.ModuleFinder;44import java.lang.module.ModuleReader;45import java.lang.module.ModuleReference;46import java.nio.file.FileVisitResult;47import java.nio.file.Files;48import java.nio.file.Path;49import java.nio.file.Paths;50import java.nio.file.SimpleFileVisitor;51import java.nio.file.attribute.BasicFileAttributes;52import java.util.ArrayList;53import java.util.Arrays;54import java.util.Collections;55import java.util.List;56import java.util.Map;57import java.util.Set;58import java.util.concurrent.atomic.AtomicInteger;59import java.util.spi.ToolProvider;60import java.util.stream.Collectors;61import java.util.stream.Stream;6263import jdk.internal.module.ModuleInfo;64import jdk.internal.module.ModuleHashes;65import jdk.internal.module.ModulePath;6667import jdk.test.lib.compiler.ModuleInfoMaker;6869import org.testng.annotations.BeforeMethod;70import org.testng.annotations.Test;7172import static org.testng.Assert.*;73import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;7475public class HashesTest {76static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")77.orElseThrow(() ->78new RuntimeException("jmod tool not found")79);80static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")81.orElseThrow(() ->82new RuntimeException("jar tool not found")83);8485static final AtomicInteger counter = new AtomicInteger(0);8687private Path mods;88private Path lib;89private ModuleInfoMaker builder;9091@BeforeMethod92public void setTestPath() throws IOException {93Path dest = Path.of("test" + counter.addAndGet(1));94if (Files.exists(dest)) {95deleteDirectory(dest);96}97this.mods = dest.resolve("mods");98this.lib = dest.resolve("lib");99this.builder = new ModuleInfoMaker(dest.resolve("src"));100101Files.createDirectories(lib);102Files.createDirectories(mods);103}104105@Test106public void test() throws IOException {107// create modules for test cases108makeModule("m2");109makeModule("m3");110makeModule("m1", "m2", "m3");111112makeModule("org.bar", TRANSITIVE, "m1");113makeModule("org.foo", TRANSITIVE, "org.bar");114115// create JMOD for m1, m2, m3116makeJmod("m2");117makeJmod("m3");118119// no hash is recorded since m1 has outgoing edges120jmodHashModules("m1", ".*");121122// no hash is recorded in m1, m2, m3123assertNull(hashes("m1"));124assertNull(hashes("m2"));125assertNull(hashes("m3"));126127// hash m1 in m2128jmodHashModules("m2", "m1");129checkHashes("m2", Set.of("m1"));130131// hash m1 in m2132jmodHashModules("m2", ".*");133checkHashes("m2", Set.of("m1"));134135// create m2.jmod with no hash136makeJmod("m2");137// run jmod hash command to hash m1 in m2 and m3138runJmod(List.of("hash", "--module-path", lib.toString(),139"--hash-modules", ".*"));140checkHashes("m2", Set.of("m1"));141checkHashes("m3", Set.of("m1"));142143// check transitive requires144makeJmod("org.bar");145makeJmod("org.foo");146147jmodHashModules("org.bar", "org.*");148checkHashes("org.bar", Set.of("org.foo"));149150jmodHashModules( "m3", ".*");151checkHashes("m3", Set.of("org.foo", "org.bar", "m1"));152}153154@Test155public void multiBaseModules() throws IOException {156/*157* y2 -----------> y1158* |______159* | |160* V V161* z3 -> z2162* | |163* | V164* |---> z1165*/166167makeModule("z1");168makeModule("z2", "z1");169makeModule("z3", "z1", "z2");170171makeModule("y1");172makeModule("y2", "y1", "z2", "z3");173174Set<String> ys = Set.of("y1", "y2");175Set<String> zs = Set.of("z1", "z2", "z3");176177// create JMOD files178Stream.concat(ys.stream(), zs.stream()).forEach(this::makeJmod);179180// run jmod hash command181runJmod(List.of("hash", "--module-path", lib.toString(),182"--hash-modules", ".*"));183184/*185* z1 and y1 are the modules with hashes recorded.186*/187checkHashes("y1", Set.of("y2"));188checkHashes("z1", Set.of("z2", "z3", "y2"));189Stream.concat(ys.stream(), zs.stream())190.filter(mn -> !mn.equals("y1") && !mn.equals("z1"))191.forEach(mn -> assertNull(hashes(mn)));192}193194@Test195public void mixJmodAndJarFile() throws IOException {196/*197* j3 -----------> j2198* |______199* | |200* V V201* m3 -> m2202* | |203* | V204* |---> m1 -> j1 -> jdk.jlink205*/206207makeModule("j1");208makeModule("j2");209makeModule("m1", "j1");210makeModule("m2", "m1");211makeModule("m3", "m1", "m2");212213makeModule("j3", "j2", "m2", "m3");214215Set<String> jars = Set.of("j1", "j2", "j3");216Set<String> jmods = Set.of("m1", "m2", "m3");217218// create JMOD and JAR files219jars.forEach(this::makeJar);220jmods.forEach(this::makeJmod);221222// run jmod hash command223runJmod(List.of("hash", "--module-path", lib.toString(),224"--hash-modules", "^j.*|^m.*"));225226/*227* j1 and j2 are the modules with hashes recorded.228*/229checkHashes("j2", Set.of("j3"));230checkHashes("j1", Set.of("m1", "m2", "m3", "j3"));231Stream.concat(jars.stream(), jmods.stream())232.filter(mn -> !mn.equals("j1") && !mn.equals("j2"))233.forEach(mn -> assertNull(hashes(mn)));234}235236@Test237public void upgradeableModule() throws IOException {238Path mpath = Paths.get(System.getProperty("java.home"), "jmods");239if (!Files.exists(mpath)) {240return;241}242243makeModule("m1");244makeModule("java.compiler", "m1");245makeModule("m2", "java.compiler");246247makeJmod("m1");248makeJmod("m2");249makeJmod("java.compiler",250"--module-path",251lib.toString() + File.pathSeparator + mpath,252"--hash-modules", "java\\.(?!se)|^m.*");253254checkHashes("java.compiler", Set.of("m2"));255}256257@Test258public void testImageJmods() throws IOException {259Path mpath = Paths.get(System.getProperty("java.home"), "jmods");260if (!Files.exists(mpath)) {261return;262}263264makeModule("m1", "jdk.compiler", "jdk.attach");265makeModule("m2", "m1");266makeModule("m3", "java.compiler");267268makeJmod("m1");269makeJmod("m2");270271runJmod(List.of("hash",272"--module-path",273mpath.toString() + File.pathSeparator + lib.toString(),274"--hash-modules", ".*"));275276validateImageJmodsTest(mpath);277}278279@Test280public void testImageJmods1() throws IOException {281Path mpath = Paths.get(System.getProperty("java.home"), "jmods");282if (!Files.exists(mpath)) {283return;284}285286makeModule("m1", "jdk.compiler", "jdk.attach");287makeModule("m2", "m1");288makeModule("m3", "java.compiler");289290makeJar("m2");291makeJar("m1",292"--module-path",293mpath.toString() + File.pathSeparator + lib.toString(),294"--hash-modules", ".*");295validateImageJmodsTest(mpath);296}297298@Test299public void testReproducibibleHash() throws Exception {300makeModule("m4");301makeModule("m3", "m4");302makeModule("m2");303makeModule("m1", "m2", "m3");304305// create JMOD files and run jmod hash306List.of("m1", "m2", "m3", "m4").forEach(this::makeJmod);307Map<String, ModuleHashes> hashes1 = runJmodHash();308309// sleep a bit to be confident that the hashes aren't dependent on timestamps310Thread.sleep(2000);311312// (re)create JMOD files and run jmod hash313List.of("m1", "m2", "m3", "m4").forEach(this::makeJmod);314Map<String, ModuleHashes> hashes2 = runJmodHash();315316// hashes should be equal317assertEquals(hashes1, hashes2);318}319320@Test321public void testHashModulesPattern() throws IOException {322// create modules for test cases323makeModule("m1");324makeModule("m2", "m1");325makeModule("m3");326makeModule("m4", "m1", "m3");327List.of("m1", "m2", "m3", "m4").forEach(this::makeJmod);328329// compute hash for the target jmod (m1.jmod) with different regex330// 1) --hash-module "m2"331Path jmod = lib.resolve("m1.jmod");332runJmod("hash",333"--module-path", lib.toString(),334"--hash-modules", "m2", jmod.toString());335assertEquals(moduleHashes().keySet(), Set.of("m1"));336checkHashes("m1", Set.of("m2"));337338// 2) --hash-module "m2|m4"339runJmod("hash",340"--module-path", lib.toString(),341"--hash-modules", "m2|m4", jmod.toString());342assertEquals(moduleHashes().keySet(), Set.of("m1"));343checkHashes("m1", Set.of("m2", "m4"));344345// 3) --hash-module ".*"346runJmod("hash",347"--module-path", lib.toString(),348"--hash-modules", ".*", jmod.toString());349assertEquals(moduleHashes().keySet(), Set.of("m1"));350checkHashes("m1", Set.of("m2", "m4"));351352// target jmod is not specified353// compute hash for all modules in the library354runJmod("hash",355"--module-path", lib.toString(),356"--hash-modules", ".*");357assertEquals(moduleHashes().keySet(), Set.of("m1", "m3"));358checkHashes("m1", Set.of("m2", "m4"));359checkHashes("m3", Set.of("m4"));360}361362private void validateImageJmodsTest(Path mpath)363throws IOException364{365// hash is recorded in m1 and not any other packaged modules on module path366checkHashes("m1", Set.of("m2"));367assertNull(hashes("m2"));368369// should not override any JDK packaged modules370ModuleFinder finder = ModulePath.of(Runtime.version(), true, mpath);371assertNull(hashes(finder, "jdk.compiler"));372assertNull(hashes(finder, "jdk.attach"));373}374375private void checkHashes(String mn, Set<String> hashModules) {376ModuleHashes hashes = hashes(mn);377assertEquals(hashModules, hashes.names());378}379380private ModuleHashes hashes(String name) {381ModuleFinder finder = ModulePath.of(Runtime.version(), true, lib);382return hashes(finder, name);383}384385private ModuleHashes hashes(ModuleFinder finder, String name) {386ModuleReference mref = finder.find(name).orElseThrow(RuntimeException::new);387try {388try (ModuleReader reader = mref.open();389InputStream in = reader.open("module-info.class").get()) {390ModuleHashes hashes = ModuleInfo.read(in, null).recordedHashes();391System.out.format("hashes in module %s %s%n", name,392(hashes != null) ? "present" : "absent");393if (hashes != null) {394hashes.names().stream().sorted().forEach(n ->395System.out.format(" %s %s%n", n, toHex(hashes.hashFor(n)))396);397}398return hashes;399}400} catch (IOException e) {401throw new UncheckedIOException(e);402}403}404405private String toHex(byte[] ba) {406StringBuilder sb = new StringBuilder(ba.length);407for (byte b: ba) {408sb.append(String.format("%02x", b & 0xff));409}410return sb.toString();411}412413private void deleteDirectory(Path dir) throws IOException {414Files.walkFileTree(dir, new SimpleFileVisitor<>() {415@Override416public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)417throws IOException418{419Files.delete(file);420return FileVisitResult.CONTINUE;421}422423@Override424public FileVisitResult postVisitDirectory(Path dir, IOException exc)425throws IOException426{427Files.delete(dir);428return FileVisitResult.CONTINUE;429}430});431}432433434private void makeModule(String mn, String... deps) throws IOException {435makeModule(mn, null, deps);436}437438private void makeModule(String mn, ModuleDescriptor.Requires.Modifier mod, String... deps)439throws IOException440{441if (mod != null && mod != TRANSITIVE && mod != STATIC) {442throw new IllegalArgumentException(mod.toString());443}444445StringBuilder sb = new StringBuilder();446sb.append("module ")447.append(mn)448.append(" {")449.append("\n");450Arrays.stream(deps)451.forEach(req -> {452sb.append(" requires ");453if (mod != null) {454sb.append(mod.toString().toLowerCase())455.append(" ");456}457sb.append(req)458.append(";\n");459});460sb.append("}\n");461builder.writeJavaFiles(mn, sb.toString());462builder.compile(mn, mods);463}464465private void jmodHashModules(String moduleName, String hashModulesPattern) {466makeJmod(moduleName, "--module-path", lib.toString(),467"--hash-modules", hashModulesPattern);468}469470private void makeJmod(String moduleName, String... options) {471Path mclasses = mods.resolve(moduleName);472Path outfile = lib.resolve(moduleName + ".jmod");473List<String> args = new ArrayList<>();474args.add("create");475Collections.addAll(args, options);476Collections.addAll(args, "--class-path", mclasses.toString(),477outfile.toString());478479if (Files.exists(outfile)) {480try {481Files.delete(outfile);482} catch (IOException e) {483throw new UncheckedIOException(e);484}485}486runJmod(args);487}488489/**490* Execute jmod hash on the modules in the lib directory. Returns a map of491* the modules, with the module name as the key, for the modules that have492* a ModuleHashes class file attribute.493*/494private Map<String, ModuleHashes> runJmodHash() {495runJmod("hash",496"--module-path", lib.toString(),497"--hash-modules", ".*");498return moduleHashes();499}500501private Map<String, ModuleHashes> moduleHashes() {502return ModulePath.of(Runtime.version(), true, lib)503.findAll()504.stream()505.map(ModuleReference::descriptor)506.map(ModuleDescriptor::name)507.filter(mn -> hashes(mn) != null)508.collect(Collectors.toMap(mn -> mn, this::hashes));509}510511private static void runJmod(List<String> args) {512runJmod(args.toArray(new String[args.size()]));513}514515private static void runJmod(String... args) {516int rc = JMOD_TOOL.run(System.out, System.out, args);517System.out.println("jmod " + Arrays.stream(args).collect(Collectors.joining(" ")));518if (rc != 0) {519throw new AssertionError("jmod failed: rc = " + rc);520}521}522523private void makeJar(String moduleName, String... options) {524Path mclasses = mods.resolve(moduleName);525Path outfile = lib.resolve(moduleName + ".jar");526List<String> args = new ArrayList<>();527Stream.concat(Stream.of("--create",528"--file=" + outfile.toString()),529Arrays.stream(options))530.forEach(args::add);531args.add("-C");532args.add(mclasses.toString());533args.add(".");534535if (Files.exists(outfile)) {536try {537Files.delete(outfile);538} catch (IOException e) {539throw new UncheckedIOException(e);540}541}542543int rc = JAR_TOOL.run(System.out, System.out, args.toArray(new String[args.size()]));544System.out.println("jar " + args.stream().collect(Collectors.joining(" ")));545if (rc != 0) {546throw new AssertionError("jar failed: rc = " + rc);547}548}549}550551552