blob: 867927685d453ad942050cc5bfa5542b375e41e4 [file] [log] [blame]
Alan Bateman36e08202016-05-03 09:09:57 +01001/**
Mandy Chung6b74b5d2017-01-16 12:15:44 -08002 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
Alan Bateman36e08202016-05-03 09:09:57 +01003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
Mandy Chung6b74b5d2017-01-16 12:15:44 -080026 * @bug 8160286
Alan Bateman36e08202016-05-03 09:09:57 +010027 * @summary Test the recording and checking of module hashes
Igor Ignatyev97a53602017-05-24 14:16:09 -070028 * @library /test/lib
Alan Bateman83df0932016-10-28 10:18:07 +010029 * @modules java.base/jdk.internal.misc
30 * java.base/jdk.internal.module
Alan Bateman36e08202016-05-03 09:09:57 +010031 * jdk.compiler
Mandy Chung6b74b5d2017-01-16 12:15:44 -080032 * jdk.jartool
33 * jdk.jlink
Igor Ignatyevf84b5212017-06-12 12:43:26 -070034 * @build jdk.test.lib.compiler.ModuleInfoMaker
35 * jdk.test.lib.compiler.CompilerUtils
Alan Bateman36e08202016-05-03 09:09:57 +010036 * @run testng HashesTest
37 */
38
Mandy Chung6b74b5d2017-01-16 12:15:44 -080039import java.io.File;
Alan Bateman36e08202016-05-03 09:09:57 +010040import java.io.IOException;
41import java.io.InputStream;
Mandy Chung6b74b5d2017-01-16 12:15:44 -080042import java.io.UncheckedIOException;
Alan Bateman36e08202016-05-03 09:09:57 +010043import java.lang.module.ModuleDescriptor;
44import java.lang.module.ModuleFinder;
45import java.lang.module.ModuleReader;
46import java.lang.module.ModuleReference;
Alan Bateman36e08202016-05-03 09:09:57 +010047import java.nio.file.FileVisitResult;
48import java.nio.file.Files;
49import java.nio.file.Path;
50import java.nio.file.Paths;
51import java.nio.file.SimpleFileVisitor;
52import java.nio.file.attribute.BasicFileAttributes;
53import java.util.ArrayList;
54import java.util.Arrays;
55import java.util.Collections;
56import java.util.List;
Alan Bateman36e08202016-05-03 09:09:57 +010057import java.util.Set;
Mandy Chung44a7c1b2016-10-12 15:41:00 -070058import java.util.spi.ToolProvider;
Alan Bateman36e08202016-05-03 09:09:57 +010059import java.util.stream.Collectors;
Mandy Chung6b74b5d2017-01-16 12:15:44 -080060import java.util.stream.Stream;
Alan Bateman36e08202016-05-03 09:09:57 +010061
Alan Bateman1e82db62016-12-16 06:19:16 +000062import jdk.internal.module.ModuleInfo;
Alan Bateman36e08202016-05-03 09:09:57 +010063import jdk.internal.module.ModuleHashes;
Alan Bateman1e82db62016-12-16 06:19:16 +000064import jdk.internal.module.ModulePath;
Alan Bateman83df0932016-10-28 10:18:07 +010065
Igor Ignatyev97a53602017-05-24 14:16:09 -070066import jdk.test.lib.compiler.ModuleInfoMaker;
67
Alan Bateman36e08202016-05-03 09:09:57 +010068import org.testng.annotations.Test;
69
70import static org.testng.Assert.*;
Mandy Chung6b74b5d2017-01-16 12:15:44 -080071import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
Alan Bateman36e08202016-05-03 09:09:57 +010072
73public class HashesTest {
Mandy Chung44a7c1b2016-10-12 15:41:00 -070074 static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
75 .orElseThrow(() ->
76 new RuntimeException("jmod tool not found")
77 );
Mandy Chung6b74b5d2017-01-16 12:15:44 -080078 static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
79 .orElseThrow(() ->
80 new RuntimeException("jar tool not found")
81 );
Alan Bateman36e08202016-05-03 09:09:57 +010082
Mandy Chung6b74b5d2017-01-16 12:15:44 -080083 private final Path mods;
84 private final Path srcDir;
85 private final Path lib;
86 private final ModuleInfoMaker builder;
87 HashesTest(Path dest) throws IOException {
88 if (Files.exists(dest)) {
89 deleteDirectory(dest);
Alan Bateman36e08202016-05-03 09:09:57 +010090 }
Mandy Chung6b74b5d2017-01-16 12:15:44 -080091 this.mods = dest.resolve("mods");
92 this.srcDir = dest.resolve("src");
93 this.lib = dest.resolve("lib");
94 this.builder = new ModuleInfoMaker(srcDir);
Alan Bateman36e08202016-05-03 09:09:57 +010095
Mandy Chung6b74b5d2017-01-16 12:15:44 -080096 Files.createDirectories(lib);
97 Files.createDirectories(mods);
Alan Bateman36e08202016-05-03 09:09:57 +010098 }
99
100 @Test
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800101 public static void test() throws IOException {
102 Path dest = Paths.get("test");
103 HashesTest ht = new HashesTest(dest);
104
105 // create modules for test cases
106 ht.makeModule("m2");
107 ht.makeModule("m3");
108 ht.makeModule("m1", "m2", "m3");
109
110 ht.makeModule("org.bar", TRANSITIVE, "m1");
111 ht.makeModule("org.foo", TRANSITIVE, "org.bar");
112
113 // create JMOD for m1, m2, m3
114 ht.makeJmod("m2");
115 ht.makeJmod("m3");
116
117 // no hash is recorded since m1 has outgoing edges
118 ht.jmodHashModules("m1", ".*");
119
120 // no hash is recorded in m1, m2, m3
121 assertTrue(ht.hashes("m1") == null);
122 assertTrue(ht.hashes("m2") == null);
123 assertTrue(ht.hashes("m3") == null);
Alan Bateman36e08202016-05-03 09:09:57 +0100124
125 // hash m1 in m2
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800126 ht.jmodHashModules("m2", "m1");
127 ht.checkHashes("m2", "m1");
Alan Bateman36e08202016-05-03 09:09:57 +0100128
129 // hash m1 in m2
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800130 ht.jmodHashModules("m2", ".*");
131 ht.checkHashes("m2", "m1");
Alan Bateman36e08202016-05-03 09:09:57 +0100132
133 // create m2.jmod with no hash
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800134 ht.makeJmod("m2");
Alan Bateman36e08202016-05-03 09:09:57 +0100135 // run jmod hash command to hash m1 in m2 and m3
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800136 runJmod(List.of("hash", "--module-path", ht.lib.toString(),
137 "--hash-modules", ".*"));
138 ht.checkHashes("m2", "m1");
139 ht.checkHashes("m3", "m1");
Alan Bateman36e08202016-05-03 09:09:57 +0100140
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800141 // check transitive requires
142 ht.makeJmod("org.bar");
143 ht.makeJmod("org.foo");
Alan Bateman36e08202016-05-03 09:09:57 +0100144
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800145 ht.jmodHashModules("org.bar", "org.*");
146 ht.checkHashes("org.bar", "org.foo");
Alan Bateman36e08202016-05-03 09:09:57 +0100147
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800148 ht.jmodHashModules( "m3", ".*");
149 ht.checkHashes("m3", "org.foo", "org.bar", "m1");
Alan Bateman36e08202016-05-03 09:09:57 +0100150 }
151
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800152 @Test
153 public static void multiBaseModules() throws IOException {
154 Path dest = Paths.get("test2");
155 HashesTest ht = new HashesTest(dest);
156
157 /*
158 * y2 -----------> y1
159 * |______
160 * | |
161 * V V
162 * z3 -> z2
163 * | |
164 * | V
165 * |---> z1
166 */
167
168 ht.makeModule("z1");
169 ht.makeModule("z2", "z1");
170 ht.makeModule("z3", "z1", "z2");
171
172 ht.makeModule("y1");
173 ht.makeModule("y2", "y1", "z2", "z3");
174
175 Set<String> ys = Set.of("y1", "y2");
176 Set<String> zs = Set.of("z1", "z2", "z3");
177
178 // create JMOD files
179 Stream.concat(ys.stream(), zs.stream()).forEach(ht::makeJmod);
180
181 // run jmod hash command
182 runJmod(List.of("hash", "--module-path", ht.lib.toString(),
183 "--hash-modules", ".*"));
184
185 /*
186 * z1 and y1 are the modules with hashes recorded.
187 */
188 ht.checkHashes("y1", "y2");
189 ht.checkHashes("z1", "z2", "z3", "y2");
190 Stream.concat(ys.stream(), zs.stream())
191 .filter(mn -> !mn.equals("y1") && !mn.equals("z1"))
192 .forEach(mn -> assertTrue(ht.hashes(mn) == null));
193 }
194
195 @Test
196 public static void mixJmodAndJarFile() throws IOException {
197 Path dest = Paths.get("test3");
198 HashesTest ht = new HashesTest(dest);
199
200 /*
201 * j3 -----------> j2
202 * |______
203 * | |
204 * V V
205 * m3 -> m2
206 * | |
207 * | V
208 * |---> m1 -> j1 -> jdk.jlink
209 */
210
211 ht.makeModule("j1");
212 ht.makeModule("j2");
213 ht.makeModule("m1", "j1");
214 ht.makeModule("m2", "m1");
215 ht.makeModule("m3", "m1", "m2");
216
217 ht.makeModule("j3", "j2", "m2", "m3");
218
219 Set<String> jars = Set.of("j1", "j2", "j3");
220 Set<String> jmods = Set.of("m1", "m2", "m3");
221
222 // create JMOD and JAR files
223 jars.forEach(ht::makeJar);
224 jmods.forEach(ht::makeJmod);
225
226 // run jmod hash command
227 runJmod(List.of("hash", "--module-path", ht.lib.toString(),
228 "--hash-modules", "^j.*|^m.*"));
229
230 /*
231 * j1 and j2 are the modules with hashes recorded.
232 */
233 ht.checkHashes("j2", "j3");
234 ht.checkHashes("j1", "m1", "m2", "m3", "j3");
235 Stream.concat(jars.stream(), jmods.stream())
236 .filter(mn -> !mn.equals("j1") && !mn.equals("j2"))
237 .forEach(mn -> assertTrue(ht.hashes(mn) == null));
238 }
239
240 @Test
241 public static void upgradeableModule() throws IOException {
242 Path mpath = Paths.get(System.getProperty("java.home"), "jmods");
243 if (!Files.exists(mpath)) {
244 return;
245 }
246
247 Path dest = Paths.get("test4");
248 HashesTest ht = new HashesTest(dest);
249 ht.makeModule("m1");
250 ht.makeModule("java.xml.bind", "m1");
251 ht.makeModule("java.xml.ws", "java.xml.bind");
252 ht.makeModule("m2", "java.xml.ws");
253
254 ht.makeJmod("m1");
255 ht.makeJmod("m2");
256 ht.makeJmod("java.xml.ws");
257 ht.makeJmod("java.xml.bind",
258 "--module-path",
259 ht.lib.toString() + File.pathSeparator + mpath,
260 "--hash-modules", "^java.xml.*|^m.*");
261
262 ht.checkHashes("java.xml.bind", "java.xml.ws", "m2");
263 }
264
265 @Test
266 public static void testImageJmods() throws IOException {
267 Path mpath = Paths.get(System.getProperty("java.home"), "jmods");
268 if (!Files.exists(mpath)) {
269 return;
270 }
271
272 Path dest = Paths.get("test5");
273 HashesTest ht = new HashesTest(dest);
274 ht.makeModule("m1", "jdk.compiler", "jdk.attach");
275 ht.makeModule("m2", "m1");
276 ht.makeModule("m3", "java.compiler");
277
278 ht.makeJmod("m1");
279 ht.makeJmod("m2");
280
281 runJmod(List.of("hash",
282 "--module-path",
283 mpath.toString() + File.pathSeparator + ht.lib.toString(),
284 "--hash-modules", ".*"));
285
286 validateImageJmodsTest(ht, mpath);
287 }
288
289 @Test
290 public static void testImageJmods1() throws IOException {
291 Path mpath = Paths.get(System.getProperty("java.home"), "jmods");
292 if (!Files.exists(mpath)) {
293 return;
294 }
295
296 Path dest = Paths.get("test6");
297 HashesTest ht = new HashesTest(dest);
298 ht.makeModule("m1", "jdk.compiler", "jdk.attach");
299 ht.makeModule("m2", "m1");
300 ht.makeModule("m3", "java.compiler");
301
302 ht.makeJar("m2");
303 ht.makeJar("m1",
304 "--module-path",
305 mpath.toString() + File.pathSeparator + ht.lib.toString(),
306 "--hash-modules", ".*");
307 validateImageJmodsTest(ht, mpath);
308 }
309
310 private static void validateImageJmodsTest(HashesTest ht, Path mpath)
311 throws IOException
312 {
313 // hash is recorded in m1 and not any other packaged modules on module path
314 ht.checkHashes("m1", "m2");
315 assertTrue(ht.hashes("m2") == null);
316
317 // should not override any JDK packaged modules
Alan Batemana4693ee2017-02-10 09:04:39 +0000318 ModuleFinder finder = ModulePath.of(Runtime.version(), true, mpath);
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800319 assertTrue(ht.hashes(finder,"jdk.compiler") == null);
320 assertTrue(ht.hashes(finder,"jdk.attach") == null);
321 }
322
323 private void checkHashes(String mn, String... hashModules) throws IOException {
324 ModuleHashes hashes = hashes(mn);
Alan Bateman36e08202016-05-03 09:09:57 +0100325 assertTrue(hashes.names().equals(Set.of(hashModules)));
326 }
327
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800328 private ModuleHashes hashes(String name) {
Alan Batemana4693ee2017-02-10 09:04:39 +0000329 ModuleFinder finder = ModulePath.of(Runtime.version(), true, lib);
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800330 return hashes(finder, name);
331 }
332
333 private ModuleHashes hashes(ModuleFinder finder, String name) {
Alan Bateman36e08202016-05-03 09:09:57 +0100334 ModuleReference mref = finder.find(name).orElseThrow(RuntimeException::new);
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800335 try {
336 ModuleReader reader = mref.open();
337 try (InputStream in = reader.open("module-info.class").get()) {
338 ModuleHashes hashes = ModuleInfo.read(in, null).recordedHashes();
339 System.out.format("hashes in module %s %s%n", name,
Alan Bateman1e82db62016-12-16 06:19:16 +0000340 (hashes != null) ? "present" : "absent");
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800341 if (hashes != null) {
342 hashes.names().stream().sorted().forEach(n ->
343 System.out.format(" %s %s%n", n, toHex(hashes.hashFor(n)))
344 );
345 }
346 return hashes;
347 } finally {
348 reader.close();
Alan Bateman36e08202016-05-03 09:09:57 +0100349 }
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800350 } catch (IOException e) {
351 throw new UncheckedIOException(e);
Alan Bateman36e08202016-05-03 09:09:57 +0100352 }
353 }
354
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800355 private String toHex(byte[] ba) {
356 StringBuilder sb = new StringBuilder(ba.length);
357 for (byte b: ba) {
358 sb.append(String.format("%02x", b & 0xff));
359 }
360 return sb.toString();
361 }
362
Alan Bateman36e08202016-05-03 09:09:57 +0100363 private void deleteDirectory(Path dir) throws IOException {
364 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
365 @Override
366 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
367 throws IOException
368 {
369 Files.delete(file);
370 return FileVisitResult.CONTINUE;
371 }
372
373 @Override
374 public FileVisitResult postVisitDirectory(Path dir, IOException exc)
375 throws IOException
376 {
377 Files.delete(dir);
378 return FileVisitResult.CONTINUE;
379 }
380 });
381 }
382
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800383
384 private void makeModule(String mn, String... deps) throws IOException {
385 makeModule(mn, null, deps);
386 }
387
Igor Ignatyev97a53602017-05-24 14:16:09 -0700388 private void makeModule(String mn, ModuleDescriptor.Requires.Modifier mod, String... deps)
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800389 throws IOException
390 {
391 if (mod != null && mod != TRANSITIVE && mod != STATIC) {
392 throw new IllegalArgumentException(mod.toString());
393 }
394
395 StringBuilder sb = new StringBuilder();
Igor Ignatyev97a53602017-05-24 14:16:09 -0700396 sb.append("module ")
397 .append(mn)
398 .append(" {")
399 .append("\n");
400 Arrays.stream(deps)
401 .forEach(req -> {
402 sb.append(" requires ");
403 if (mod != null) {
404 sb.append(mod.toString().toLowerCase())
405 .append(" ");
406 }
407 sb.append(req)
408 .append(";\n");
409 });
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800410 sb.append("}\n");
411 builder.writeJavaFiles(mn, sb.toString());
Igor Ignatyev97a53602017-05-24 14:16:09 -0700412 builder.compile(mn, mods);
Alan Bateman36e08202016-05-03 09:09:57 +0100413 }
414
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800415 private void jmodHashModules(String moduleName, String hashModulesPattern) {
416 makeJmod(moduleName, "--module-path", lib.toString(),
417 "--hash-modules", hashModulesPattern);
418 }
419
420 private void makeJmod(String moduleName, String... options) {
Alan Bateman36e08202016-05-03 09:09:57 +0100421 Path mclasses = mods.resolve(moduleName);
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800422 Path outfile = lib.resolve(moduleName + ".jmod");
Alan Bateman36e08202016-05-03 09:09:57 +0100423 List<String> args = new ArrayList<>();
424 args.add("create");
425 Collections.addAll(args, options);
426 Collections.addAll(args, "--class-path", mclasses.toString(),
427 outfile.toString());
428
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800429 if (Files.exists(outfile)) {
430 try {
431 Files.delete(outfile);
432 } catch (IOException e) {
433 throw new UncheckedIOException(e);
434 }
435 }
Alan Bateman36e08202016-05-03 09:09:57 +0100436 runJmod(args);
437 }
438
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800439 private static void runJmod(List<String> args) {
Mandy Chung44a7c1b2016-10-12 15:41:00 -0700440 int rc = JMOD_TOOL.run(System.out, System.out, args.toArray(new String[args.size()]));
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800441 System.out.println("jmod " + args.stream().collect(Collectors.joining(" ")));
Alan Bateman36e08202016-05-03 09:09:57 +0100442 if (rc != 0) {
Mandy Chung6b74b5d2017-01-16 12:15:44 -0800443 throw new AssertionError("jmod failed: rc = " + rc);
444 }
445 }
446
447 private void makeJar(String moduleName, String... options) {
448 Path mclasses = mods.resolve(moduleName);
449 Path outfile = lib.resolve(moduleName + ".jar");
450 List<String> args = new ArrayList<>();
451 Stream.concat(Stream.of("--create",
452 "--file=" + outfile.toString()),
453 Arrays.stream(options))
454 .forEach(args::add);
455 args.add("-C");
456 args.add(mclasses.toString());
457 args.add(".");
458
459 if (Files.exists(outfile)) {
460 try {
461 Files.delete(outfile);
462 } catch (IOException e) {
463 throw new UncheckedIOException(e);
464 }
465 }
466
467 int rc = JAR_TOOL.run(System.out, System.out, args.toArray(new String[args.size()]));
468 System.out.println("jar " + args.stream().collect(Collectors.joining(" ")));
469 if (rc != 0) {
470 throw new AssertionError("jar failed: rc = " + rc);
Alan Bateman36e08202016-05-03 09:09:57 +0100471 }
472 }
473}