8154956: Module system implementation refresh (4/2016)

Co-authored-by: Mandy Chung <mandy.chung@oracle.com>
Co-authored-by: Erik Joelsson <erik.joelsson@oracle.com>
Co-authored-by: Chris Hegarty <chris.hegarty@oracle.com>
Co-authored-by: Peter Levart <peter.levart@gmail.com>
Co-authored-by: Sundararajan Athijegannathan <sundararajan.athijegannathan@oracle.com>
Reviewed-by: alanb, mchung, chegar, redestad
diff --git a/jdk/test/tools/jmod/hashes/HashesTest.java b/jdk/test/tools/jmod/hashes/HashesTest.java
new file mode 100644
index 0000000..243ecbd
--- /dev/null
+++ b/jdk/test/tools/jmod/hashes/HashesTest.java
@@ -0,0 +1,213 @@
+/**
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Test the recording and checking of module hashes
+ * @author Andrei Eremeev
+ * @library /lib/testlibrary
+ * @modules java.base/jdk.internal.module
+ *          jdk.jlink/jdk.tools.jlink.internal
+ *          jdk.jlink/jdk.tools.jmod
+ *          jdk.compiler
+ * @build CompilerUtils
+ * @run testng HashesTest
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.lang.reflect.Method;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import jdk.internal.module.ConfigurableModuleFinder;
+import jdk.internal.module.ModuleHashes;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.*;
+
+public class HashesTest {
+
+    private final Path testSrc = Paths.get(System.getProperty("test.src"));
+    private final Path modSrc = testSrc.resolve("src");
+    private final Path mods = Paths.get("mods");
+    private final Path jmods = Paths.get("jmods");
+    private final String[] modules = new String[] { "m1", "m2", "m3"};
+
+    private static Method hashesMethod;
+    @BeforeTest
+    private void setup() throws Exception {
+        if (Files.exists(jmods)) {
+            deleteDirectory(jmods);
+        }
+        Files.createDirectories(jmods);
+
+        // build m2, m3 required by m1
+        compileModule("m2", modSrc);
+        jmod("m2");
+
+        compileModule("m3", modSrc);
+        jmod("m3");
+
+        // build m1
+        compileModule("m1", modSrc);
+        // no hash is recorded since m1 has outgoing edges
+        jmod("m1", "--modulepath", jmods.toString(), "--hash-modules", ".*");
+
+        // compile org.bar and org.foo
+        compileModule("org.bar", modSrc);
+        compileModule("org.foo", modSrc);
+
+        try {
+            hashesMethod = ModuleDescriptor.class.getDeclaredMethod("hashes");
+            hashesMethod.setAccessible(true);
+        } catch (ReflectiveOperationException x) {
+            throw new InternalError(x);
+        }
+    }
+
+    @Test
+    public void test() throws Exception {
+        for (String mn : modules) {
+            assertFalse(hashes(mn).isPresent());
+        }
+
+        // hash m1 in m2
+        jmod("m2", "--modulepath", jmods.toString(), "--hash-modules", "m1");
+        checkHashes(hashes("m2").get(), "m1");
+
+        // hash m1 in m2
+        jmod("m2", "--modulepath", jmods.toString(), "--hash-modules", ".*");
+        checkHashes(hashes("m2").get(), "m1");
+
+        // create m2.jmod with no hash
+        jmod("m2");
+        // run jmod hash command to hash m1 in m2 and m3
+        runJmod(Arrays.asList("hash", "--modulepath", jmods.toString(),
+                "--hash-modules", ".*"));
+        checkHashes(hashes("m2").get(), "m1");
+        checkHashes(hashes("m3").get(), "m1");
+
+        jmod("org.bar");
+        jmod("org.foo");
+
+        jmod("org.bar", "--modulepath", jmods.toString(), "--hash-modules", "org.*");
+        checkHashes(hashes("org.bar").get(), "org.foo");
+
+        jmod("m3", "--modulepath", jmods.toString(), "--hash-modules", ".*");
+        checkHashes(hashes("m3").get(), "org.foo", "org.bar", "m1");
+    }
+
+    private void checkHashes(ModuleHashes hashes, String... hashModules) {
+        assertTrue(hashes.names().equals(Set.of(hashModules)));
+    }
+
+    private Optional<ModuleHashes> hashes(String name) throws Exception {
+        ModuleFinder finder = ModuleFinder.of(jmods.resolve(name + ".jmod"));
+        if (finder instanceof ConfigurableModuleFinder) {
+            ((ConfigurableModuleFinder) finder)
+                .configurePhase(ConfigurableModuleFinder.Phase.LINK_TIME);
+        }
+        ModuleReference mref = finder.find(name).orElseThrow(RuntimeException::new);
+        ModuleReader reader = mref.open();
+        try (InputStream in = reader.open("module-info.class").get()) {
+            ModuleDescriptor md = ModuleDescriptor.read(in);
+            Optional<ModuleHashes> hashes =
+                (Optional<ModuleHashes>) hashesMethod.invoke(md);
+            System.out.format("hashes in module %s %s%n", name,
+                              hashes.isPresent() ? "present" : "absent");
+            if (hashes.isPresent()) {
+                hashes.get().names().stream()
+                    .sorted()
+                    .forEach(n -> System.out.format("  %s %s%n", n, hashes.get().hashFor(n)));
+            }
+            return hashes;
+        } finally {
+            reader.close();
+        }
+    }
+
+    private void deleteDirectory(Path dir) throws IOException {
+        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                throws IOException
+            {
+                Files.delete(file);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc)
+                throws IOException
+            {
+                Files.delete(dir);
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    private void compileModule(String moduleName, Path src) throws IOException {
+        Path msrc = src.resolve(moduleName);
+        assertTrue(CompilerUtils.compile(msrc, mods, "-modulesourcepath", src.toString()));
+    }
+
+    private void jmod(String moduleName, String... options) throws IOException {
+        Path mclasses = mods.resolve(moduleName);
+        Path outfile = jmods.resolve(moduleName + ".jmod");
+        List<String> args = new ArrayList<>();
+        args.add("create");
+        Collections.addAll(args, options);
+        Collections.addAll(args, "--class-path", mclasses.toString(),
+                           outfile.toString());
+
+        if (Files.exists(outfile))
+            Files.delete(outfile);
+
+        runJmod(args);
+    }
+
+    private void runJmod(List<String> args) {
+        int rc = jdk.tools.jmod.Main.run(args.toArray(new String[args.size()]), System.out);
+        System.out.println("jmod options: " + args.stream().collect(Collectors.joining(" ")));
+        if (rc != 0) {
+            throw new AssertionError("Jmod failed: rc = " + rc);
+        }
+    }
+}