|
| 1 | +/* |
| 2 | + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. |
| 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.nio.file.Files; |
| 25 | +import java.nio.file.Path; |
| 26 | +import java.time.Instant; |
| 27 | +import java.util.ArrayList; |
| 28 | +import java.util.List; |
| 29 | +import java.util.Optional; |
| 30 | + |
| 31 | +import javax.lang.model.SourceVersion; |
| 32 | + |
| 33 | +import jdk.test.lib.process.OutputAnalyzer; |
| 34 | +import jdk.test.lib.process.ProcessTools; |
| 35 | +import org.junit.jupiter.api.BeforeAll; |
| 36 | +import org.junit.jupiter.api.Test; |
| 37 | + |
| 38 | +/* |
| 39 | + * @test |
| 40 | + * @bug 8327466 |
| 41 | + * @summary verifies that the ct.sym file created by build.tools.symbolgenerator.CreateSymbols |
| 42 | + * is reproducible |
| 43 | + * @library /test/lib |
| 44 | + * @modules java.compiler |
| 45 | + * jdk.compiler/com.sun.tools.javac.api |
| 46 | + * jdk.compiler/com.sun.tools.javac.jvm |
| 47 | + * jdk.compiler/com.sun.tools.javac.main |
| 48 | + * jdk.compiler/com.sun.tools.javac.util |
| 49 | + * |
| 50 | + * @compile ${test.root}/../../make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java |
| 51 | + * |
| 52 | + * @run junit CreateSymbolsReproducibleTest |
| 53 | + */ |
| 54 | +public class CreateSymbolsReproducibleTest { |
| 55 | + |
| 56 | + // the fully qualified class name of the tool that we launch to generate the ct.sym file |
| 57 | + private static final String CREATE_SYMBOLS_CLASS_FQN = "build.tools.symbolgenerator.CreateSymbols"; |
| 58 | + // a reproducible timestamp (in seconds) that we pass to "CreateSymbols build-ctsym" as input |
| 59 | + // when generating the ct.sym file |
| 60 | + private static final long SOURCE_EPOCH_DATE = Instant.now().getEpochSecond(); |
| 61 | + // arbitrary set of packages that will be included in a include list file |
| 62 | + // that will be given as input to "CreateSymbols build-description-incremental" command |
| 63 | + // for generating the symbol text file |
| 64 | + private static final String INCLUDE_PKGS = """ |
| 65 | + +java/io/ |
| 66 | + +java/lang/ |
| 67 | + +java/lang/annotation/ |
| 68 | + +java/lang/instrument/ |
| 69 | + +java/lang/invoke/ |
| 70 | + """; |
| 71 | + |
| 72 | + private static Path symTxtFile; |
| 73 | + |
| 74 | + @BeforeAll |
| 75 | + static void beforeAll() throws Exception { |
| 76 | + symTxtFile = createSymTxtFile(); |
| 77 | + System.out.println("created sym.txt file at " + symTxtFile); |
| 78 | + } |
| 79 | + |
| 80 | + /* |
| 81 | + * Launches the "CreateSymbols build-ctsym" tool multiple times to generate ct.sym files. |
| 82 | + * Each time with the same inputs and the same timestamp. For each of these attempts, we use |
| 83 | + * a different timezone when launching the tool. The test verifies that irrespective of |
| 84 | + * what timezone gets used, the generated ct.sym files don't differ. |
| 85 | + */ |
| 86 | + @Test |
| 87 | + void testDifferentTimezone() throws Exception { |
| 88 | + final Path destDir = Files.createTempDirectory(Path.of("."), "").toAbsolutePath(); |
| 89 | + final List<Path> ctSymFiles = new ArrayList<>(); |
| 90 | + final List<Optional<String>> timezones = List.of( |
| 91 | + Optional.empty(), // no explicit timezone |
| 92 | + Optional.of("UTC"), |
| 93 | + Optional.of("America/Los_Angeles"), |
| 94 | + Optional.of("Asia/Tokyo") |
| 95 | + ); |
| 96 | + int num = 0; |
| 97 | + // create several ct.sym files by launching the tool with different timezones |
| 98 | + // but the same timestamp value as input |
| 99 | + for (final Optional<String> timezone : timezones) { |
| 100 | + num++; |
| 101 | + final String destCtSymFileName = "ct-" + num + ".sym"; |
| 102 | + final Path destCtSym = destDir.resolve(destCtSymFileName); |
| 103 | + System.out.println("using timezone " + timezone + " to create ct.sym file at " |
| 104 | + + destCtSym); |
| 105 | + createCtSym(destCtSym, symTxtFile, timezone); |
| 106 | + ctSymFiles.add(destCtSym); |
| 107 | + } |
| 108 | + // verify that each of these generated ct.sym files are exactly the same in content |
| 109 | + for (int i = 0; i < ctSymFiles.size() - 1; i++) { |
| 110 | + final Path ctSym1 = ctSymFiles.get(i); |
| 111 | + final Path ctSym2 = ctSymFiles.get(i + 1); |
| 112 | + final long mismatchOffset = Files.mismatch(ctSym1, ctSym2); |
| 113 | + if (mismatchOffset != -1) { |
| 114 | + throw new AssertionError("contents of files " + ctSym1 + " and " + ctSym2 |
| 115 | + + " unexpectedly differ" + " (at " + mismatchOffset + " offset)"); |
| 116 | + } |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + private static Path createSymTxtFile() throws Exception { |
| 121 | + final Path tmpDir = Files.createTempDirectory(Path.of("."), "").toAbsolutePath(); |
| 122 | + final Path destSymTxtFile = tmpDir.resolve("sym.txt"); |
| 123 | + Files.writeString(destSymTxtFile, ""); |
| 124 | + final Path includeList = tmpDir.resolve("include.list"); |
| 125 | + Files.writeString(includeList, INCLUDE_PKGS); |
| 126 | + final String[] cmd = new String[]{ |
| 127 | + "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", |
| 128 | + "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", |
| 129 | + CREATE_SYMBOLS_CLASS_FQN, |
| 130 | + "build-description-incremental", |
| 131 | + destSymTxtFile.toString(), |
| 132 | + includeList.toString() |
| 133 | + }; |
| 134 | + final OutputAnalyzer oa = ProcessTools.executeTestJava(cmd); |
| 135 | + oa.shouldHaveExitValue(0); |
| 136 | + // verify the file was created |
| 137 | + if (Files.notExists(destSymTxtFile)) { |
| 138 | + oa.reportDiagnosticSummary(); |
| 139 | + throw new AssertionError(CREATE_SYMBOLS_CLASS_FQN |
| 140 | + + " build-description-incremental failed to create " + destSymTxtFile); |
| 141 | + } |
| 142 | + return destSymTxtFile; |
| 143 | + } |
| 144 | + |
| 145 | + private static void createCtSym(final Path destCtSymFile, final Path symTxtFile, |
| 146 | + final Optional<String> timezone) throws Exception { |
| 147 | + final Path modulesDir = Path.of(".").resolve("modules"); |
| 148 | + Files.createDirectories(modulesDir); |
| 149 | + final Path modulesList = Path.of(".").resolve("modules-list"); |
| 150 | + // an empty file |
| 151 | + Files.writeString(modulesList, ""); |
| 152 | + |
| 153 | + final List<String> cmd = new ArrayList<>(); |
| 154 | + timezone.ifPresent((tz) -> { |
| 155 | + // launch the tool with a specific timezone (if any) |
| 156 | + cmd.add("-Duser.timezone=" + tz); |
| 157 | + }); |
| 158 | + cmd.add(CREATE_SYMBOLS_CLASS_FQN); |
| 159 | + cmd.add("build-ctsym"); // command to CreateSymbols tool |
| 160 | + cmd.add("non-existent-ct-desc-file"); |
| 161 | + cmd.add(symTxtFile.toString()); // a previously generated a sym.txt file |
| 162 | + cmd.add(destCtSymFile.toString()); // target ct.sym file to generate |
| 163 | + cmd.add(Long.toString(SOURCE_EPOCH_DATE)); // reproducible timestamp (in seconds) |
| 164 | + cmd.add(Integer.toString(SourceVersion.latest().ordinal())); |
| 165 | + cmd.add("does-not-matter-pre-release-tag"); |
| 166 | + cmd.add(modulesDir.toString()); |
| 167 | + cmd.add(modulesList.toString()); |
| 168 | + final OutputAnalyzer oa = ProcessTools.executeTestJava(cmd); |
| 169 | + oa.shouldHaveExitValue(0); |
| 170 | + // verify the ct.sym file was generated |
| 171 | + if (Files.notExists(destCtSymFile)) { |
| 172 | + oa.reportDiagnosticSummary(); |
| 173 | + throw new AssertionError("ct.sym file missing at " + destCtSymFile); |
| 174 | + } |
| 175 | + } |
| 176 | +} |
0 commit comments