1 /* 2 * Copyright (c) 2024, Red Hat, Inc. 3 * 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 import java.io.IOException; 25 import java.nio.file.FileVisitResult; 26 import java.nio.file.Files; 27 import java.nio.file.Path; 28 import java.nio.file.SimpleFileVisitor; 29 import java.nio.file.attribute.BasicFileAttributes; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.Collections; 33 import java.util.List; 34 35 import tests.Helper; 36 import tests.JImageGenerator; 37 38 39 /* 40 * @test 41 * @summary Compare packaged-modules jlink with a run-time image based jlink to 42 * produce the same result 43 * @requires (jlink.packagedModules & vm.compMode != "Xcomp" & os.maxMemory >= 2g) 44 * @library ../../lib /test/lib 45 * @enablePreview 46 * @modules java.base/jdk.internal.jimage 47 * jdk.jlink/jdk.tools.jlink.internal 48 * jdk.jlink/jdk.tools.jlink.plugin 49 * jdk.jlink/jdk.tools.jimage 50 * @build tests.* jdk.test.lib.process.OutputAnalyzer 51 * jdk.test.lib.process.ProcessTools 52 * @run main/othervm/timeout=1200 -Xmx1g PackagedModulesVsRuntimeImageLinkTest 53 */ 54 public class PackagedModulesVsRuntimeImageLinkTest extends AbstractLinkableRuntimeTest { 55 56 public static void main(String[] args) throws Exception { 57 PackagedModulesVsRuntimeImageLinkTest test = new PackagedModulesVsRuntimeImageLinkTest(); 58 test.run(); 59 } 60 61 @Override 62 void runTest(Helper helper, boolean isLinkableRuntime) throws Exception { 63 // create a java.se using jmod-less approach 64 BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder() 65 .helper(helper) 66 .name("java-se-jmodless") 67 .addModule("java.se") 68 .validatingModule("java.se"); 69 if (isLinkableRuntime) { 70 builder.setLinkableRuntime(); 71 } 72 Path javaSEruntimeLink = createJavaImageRuntimeLink(builder.build()); 73 74 // create a java.se using packaged modules (jmod-full) 75 Path javaSEJmodFull = JImageGenerator.getJLinkTask() 76 .output(helper.createNewImageDir("java-se-jmodfull")) 77 .addMods("java.se").call().assertSuccess(); 78 79 compareRecursively(javaSEruntimeLink, javaSEJmodFull); 80 } 81 82 // Visit all files in the given directories checking that they're byte-by-byte identical 83 private static void compareRecursively(Path javaSEJmodLess, 84 Path javaSEJmodFull) throws IOException, AssertionError { 85 FilesCapturingVisitor jmodFullVisitor = new FilesCapturingVisitor(javaSEJmodFull); 86 FilesCapturingVisitor jmodLessVisitor = new FilesCapturingVisitor(javaSEJmodLess); 87 Files.walkFileTree(javaSEJmodFull, jmodFullVisitor); 88 Files.walkFileTree(javaSEJmodLess, jmodLessVisitor); 89 List<String> jmodFullFiles = jmodFullVisitor.filesVisited(); 90 List<String> jmodLessFiles = jmodLessVisitor.filesVisited(); 91 Collections.sort(jmodFullFiles); 92 Collections.sort(jmodLessFiles); 93 94 if (jmodFullFiles.size() != jmodLessFiles.size()) { 95 throw new AssertionError(String.format("Size of files different for jmod-less (%d) vs jmod-full (%d) java.se jlink", jmodLessFiles.size(), jmodFullFiles.size())); 96 } 97 String jimageFile = Path.of("lib").resolve("modules").toString(); 98 // Compare all files except the modules image 99 for (int i = 0; i < jmodFullFiles.size(); i++) { 100 String jmodFullPath = jmodFullFiles.get(i); 101 String jmodLessPath = jmodLessFiles.get(i); 102 if (!jmodFullPath.equals(jmodLessPath)) { 103 throw new AssertionError(String.format("jmod-full path (%s) != jmod-less path (%s)", jmodFullPath, jmodLessPath)); 104 } 105 if (jmodFullPath.equals(jimageFile)) { 106 continue; 107 } 108 Path a = javaSEJmodFull.resolve(Path.of(jmodFullPath)); 109 Path b = javaSEJmodLess.resolve(Path.of(jmodLessPath)); 110 if (Files.mismatch(a, b) != -1L) { 111 handleFileMismatch(a, b); 112 } 113 } 114 // Compare jimage contents by iterating its entries and comparing their 115 // paths and content bytes 116 // 117 // Note: The files aren't byte-by-byte comparable (probably due to string hashing 118 // and offset differences in container bytes) 119 Path jimageJmodLess = javaSEJmodLess.resolve(Path.of("lib")).resolve(Path.of("modules")); 120 Path jimageJmodFull = javaSEJmodFull.resolve(Path.of("lib")).resolve(Path.of("modules")); 121 List<String> jimageContentJmodLess = JImageHelper.listContents(jimageJmodLess); 122 List<String> jimageContentJmodFull = JImageHelper.listContents(jimageJmodFull); 123 if (jimageContentJmodLess.size() != jimageContentJmodFull.size()) { 124 throw new AssertionError(String.format("Size of jimage content differs for jmod-less (%d) v. jmod-full (%d)", jimageContentJmodLess.size(), jimageContentJmodFull.size())); 125 } 126 for (int i = 0; i < jimageContentJmodFull.size(); i++) { 127 if (!jimageContentJmodFull.get(i).equals(jimageContentJmodLess.get(i))) { 128 throw new AssertionError(String.format("Jimage content differs at index %d: jmod-full was: '%s' jmod-less was: '%s'", 129 i, 130 jimageContentJmodFull.get(i), 131 jimageContentJmodLess.get(i) 132 )); 133 } 134 String loc = jimageContentJmodFull.get(i); 135 if (isTreeInfoResource(loc)) { 136 // Skip container bytes as those are offsets to the content 137 // of the container which might be different between jlink runs. 138 continue; 139 } 140 byte[] resBytesFull = JImageHelper.getLocationBytes(loc, jimageJmodFull); 141 byte[] resBytesLess = JImageHelper.getLocationBytes(loc, jimageJmodLess); 142 if (resBytesFull.length != resBytesLess.length || Arrays.mismatch(resBytesFull, resBytesLess) != -1) { 143 throw new AssertionError("Content bytes mismatch for " + loc); 144 } 145 } 146 } 147 148 private static boolean isTreeInfoResource(String path) { 149 return path.startsWith("/packages") || path.startsWith("/modules"); 150 } 151 152 private static void handleFileMismatch(Path a, Path b) { 153 throw new AssertionError("Files mismatch: " + a + " vs. " + b); 154 } 155 156 static class FilesCapturingVisitor extends SimpleFileVisitor<Path> { 157 private final Path basePath; 158 private final List<String> filePaths = new ArrayList<>(); 159 public FilesCapturingVisitor(Path basePath) { 160 this.basePath = basePath; 161 } 162 163 @Override 164 public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { 165 Path relative = basePath.relativize(path); 166 filePaths.add(relative.toString()); 167 return FileVisitResult.CONTINUE; 168 } 169 170 List<String> filesVisited() { 171 return filePaths; 172 } 173 } 174 175 } --- EOF ---