Path: blob/master/test/jdk/tools/launcher/SourceMode.java
66643 views
/*1* Copyright (c) 2017, 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 8192920 8204588 821027526* @summary Test source mode27* @modules jdk.compiler jdk.jlink28* @run main SourceMode29*/303132import java.io.IOException;33import java.io.PrintStream;34import java.nio.file.Files;35import java.nio.file.Path;36import java.nio.file.Paths;37import java.nio.file.attribute.PosixFilePermission;38import java.util.ArrayList;39import java.util.Arrays;40import java.util.HashMap;41import java.util.List;42import java.util.Map;43import java.util.Set;44import java.util.spi.ToolProvider;4546public class SourceMode extends TestHelper {4748public static void main(String... args) throws Exception {49new SourceMode().run(args);50}5152// To reduce the chance of creating shebang lines that are too long,53// use a shorter path for a java command if the standard path is too long.54private final Path shebangJavaCmd;5556// Whether or not to automatically skip the shebang tests57private final boolean skipShebangTest;5859private final PrintStream log;6061private static final String thisVersion = System.getProperty("java.specification.version");6263SourceMode() throws Exception {64log = System.err;6566if (isWindows) {67// Skip shebang tests on Windows, because that requires Cygwin.68skipShebangTest = true;69shebangJavaCmd = null;70} else {71// Try to ensure the path to the Java launcher is reasonably short,72// to work around the mostly undocumented limit of 120 characters73// for a shebang line.74// The value of 120 is the typical kernel compile-time buffer limit.75// The following limit of 80 allows room for arguments to be placed76// after the path to the launcher on the shebang line.77Path cmd = Paths.get(javaCmd);78if (cmd.toString().length() < 80) {79shebangJavaCmd = cmd;80} else {81// Create a small image in the current directory, such that82// the path for the launcher is just "tmpJDK/bin/java".83Path tmpJDK = Paths.get("tmpJDK");84ToolProvider jlink = ToolProvider.findFirst("jlink")85.orElseThrow(() -> new Exception("cannot find jlink"));86jlink.run(System.out, System.err,87"--add-modules", "jdk.compiler,jdk.zipfs", "--output", tmpJDK.toString());88shebangJavaCmd = tmpJDK.resolve("bin").resolve("java");89}90log.println("Using java command: " + shebangJavaCmd);91skipShebangTest = false;92}93}9495// java Simple.java 1 2 396@Test97void testSimpleJava() throws IOException {98starting("testSimpleJava");99Path file = getSimpleFile("Simple.java", false);100TestResult tr = doExec(javaCmd, file.toString(), "1", "2", "3");101if (!tr.isOK())102error(tr, "Bad exit code: " + tr.exitValue);103if (!tr.contains("[1, 2, 3]"))104error(tr, "Expected output not found");105show(tr);106}107108// java --source N simple 1 2 3109@Test110void testSimple() throws IOException {111starting("testSimple");112Path file = getSimpleFile("simple", false);113TestResult tr = doExec(javaCmd, "--source", thisVersion, file.toString(), "1", "2", "3");114if (!tr.isOK())115error(tr, "Bad exit code: " + tr.exitValue);116if (!tr.contains("[1, 2, 3]"))117error(tr, "Expected output not found");118show(tr);119}120121// execSimple 1 2 3122@Test123void testExecSimple() throws IOException {124starting("testExecSimple");125if (skipShebangTest) {126log.println("SKIPPED");127return;128}129Path file = setExecutable(getSimpleFile("execSimple", true));130TestResult tr = doExec(file.toAbsolutePath().toString(), "1", "2", "3");131if (!tr.isOK())132error(tr, "Bad exit code: " + tr.exitValue);133if (!tr.contains("[1, 2, 3]"))134error(tr, "Expected output not found");135show(tr);136}137138// java @simpleJava.at (contains Simple.java 1 2 3)139@Test140void testSimpleJavaAtFile() throws IOException {141starting("testSimpleJavaAtFile");142Path file = getSimpleFile("Simple.java", false);143Path atFile = Paths.get("simpleJava.at");144createFile(atFile, List.of(file + " 1 2 3"));145TestResult tr = doExec(javaCmd, "@" + atFile);146if (!tr.isOK())147error(tr, "Bad exit code: " + tr.exitValue);148if (!tr.contains("[1, 2, 3]"))149error(tr, "Expected output not found");150show(tr);151}152153// java @simple.at (contains --source N simple 1 2 3)154@Test155void testSimpleAtFile() throws IOException {156starting("testSimpleAtFile");157Path file = getSimpleFile("simple", false);158Path atFile = Paths.get("simple.at");159createFile(atFile, List.of("--source " + thisVersion + " " + file + " 1 2 3"));160TestResult tr = doExec(javaCmd, "@" + atFile);161if (!tr.isOK())162error(tr, "Bad exit code: " + tr.exitValue);163if (!tr.contains("[1, 2, 3]"))164error(tr, "Expected output not found");165show(tr);166}167168// java -cp classes Main.java 1 2 3169@Test170void testClasspath() throws IOException {171starting("testClasspath");172Path base = Files.createDirectories(Paths.get("testClasspath"));173Path otherJava = base.resolve("Other.java");174createFile(otherJava, List.of(175"public class Other {",176" public static String join(String[] args) {",177" return String.join(\"-\", args);",178" }",179"}"180));181Path classes = Files.createDirectories(base.resolve("classes"));182Path mainJava = base.resolve("Main.java");183createFile(mainJava, List.of(184"class Main {",185" public static void main(String[] args) {",186" System.out.println(Other.join(args));",187" }}"188));189compile("-d", classes.toString(), otherJava.toString());190TestResult tr = doExec(javaCmd, "-cp", classes.toString(),191mainJava.toString(), "1", "2", "3");192if (!tr.isOK())193error(tr, "Bad exit code: " + tr.exitValue);194if (!tr.contains("1-2-3"))195error(tr, "Expected output not found");196show(tr);197}198199// java --add-exports=... Export.java --help200@Test201void testAddExports() throws IOException {202if (!isEnglishLocale()) {203return;204}205206starting("testAddExports");207Path exportJava = Paths.get("Export.java");208createFile(exportJava, List.of(209"public class Export {",210" public static void main(String[] args) {",211" new com.sun.tools.javac.main.Main(\"demo\").compile(args);",212" }",213"}"214));215// verify access fails without --add-exports216TestResult tr1 = doExec(javaCmd, exportJava.toString(), "--help");217if (tr1.isOK())218error(tr1, "Compilation succeeded unexpectedly");219show(tr1);220// verify access succeeds with --add-exports221TestResult tr2 = doExec(javaCmd,222"--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",223exportJava.toString(), "--help");224if (!tr2.isOK())225error(tr2, "Bad exit code: " + tr2.exitValue);226if (!(tr2.contains("demo") && tr2.contains("Usage")))227error(tr2, "Expected output not found");228show(tr2);229}230231// java -cp ... HelloWorld.java (for a class "java" in package "HelloWorld")232@Test233void testClassNamedJava() throws IOException {234starting("testClassNamedJava");235Path base = Files.createDirectories(Paths.get("testClassNamedJava"));236Path src = Files.createDirectories(base.resolve("src"));237Path srcfile = src.resolve("java.java");238createFile(srcfile, List.of(239"package HelloWorld;",240"class java {",241" public static void main(String... args) {",242" System.out.println(HelloWorld.java.class.getName());",243" }",244"}"245));246Path classes = base.resolve("classes");247compile("-d", classes.toString(), srcfile.toString());248TestResult tr =249doExec(javaCmd, "-cp", classes.toString(), "HelloWorld.java");250if (!tr.isOK())251error(tr, "Command failed");252if (!tr.contains("HelloWorld.java"))253error(tr, "Expected output not found");254show(tr);255}256257// java --source N -cp ... HelloWorld258@Test259void testSourceClasspath() throws IOException {260if (!isEnglishLocale()) {261return;262}263264starting("testSourceClasspath");265Path base = Files.createDirectories(Paths.get("testSourceClasspath"));266Path src = Files.createDirectories(base.resolve("src"));267Path srcfile = src.resolve("java.java");268createFile(srcfile, List.of(269"class HelloWorld {",270" public static void main(String... args) {",271" System.out.println(\"Hello World\");",272" }",273"}"274));275Path classes = base.resolve("classes");276compile("-d", classes.toString(), srcfile.toString());277TestResult tr =278doExec(javaCmd, "--source", thisVersion, "-cp", classes.toString(), "HelloWorld");279if (tr.isOK())280error(tr, "Command succeeded unexpectedly");281if (!tr.contains("file not found: HelloWorld"))282error(tr, "Expected output not found");283show(tr);284}285286// java --source287@Test288void testSourceNoArg() throws IOException {289starting("testSourceNoArg");290TestResult tr = doExec(javaCmd, "--source");291if (tr.isOK())292error(tr, "Command succeeded unexpectedly");293if (!tr.contains("--source requires source version"))294error(tr, "Expected output not found");295show(tr);296}297298// java --source N -jar simple.jar299@Test300void testSourceJarConflict() throws IOException {301starting("testSourceJarConflict");302Path base = Files.createDirectories(Paths.get("testSourceJarConflict"));303Path file = getSimpleFile("Simple.java", false);304Path classes = Files.createDirectories(base.resolve("classes"));305compile("-d", classes.toString(), file.toString());306Path simpleJar = base.resolve("simple.jar");307createJar("cf", simpleJar.toString(), "-C", classes.toString(), ".");308TestResult tr =309doExec(javaCmd, "--source", thisVersion, "-jar", simpleJar.toString());310if (tr.isOK())311error(tr, "Command succeeded unexpectedly");312if (!tr.contains("Option -jar is not allowed with --source"))313error(tr, "Expected output not found");314show(tr);315}316317// java --source N -m jdk.compiler318@Test319void testSourceModuleConflict() throws IOException {320starting("testSourceModuleConflict");321TestResult tr = doExec(javaCmd, "--source", thisVersion, "-m", "jdk.compiler");322if (tr.isOK())323error(tr, "Command succeeded unexpectedly");324if (!tr.contains("Option -m is not allowed with --source"))325error(tr, "Expected output not found");326show(tr);327}328329// #!.../java --source N -version330@Test331void testTerminalOptionInShebang() throws IOException {332starting("testTerminalOptionInShebang");333if (skipShebangTest || isAIX || isMacOSX) {334// On MacOSX, we cannot distinguish between terminal options on the335// shebang line and those on the command line.336// On Solaris, all options after the first on the shebang line are337// ignored. Similar on AIX.338log.println("SKIPPED");339return;340}341Path base = Files.createDirectories(342Paths.get("testTerminalOptionInShebang"));343Path bad = base.resolve("bad");344createFile(bad, List.of(345"#!" + shebangJavaCmd + " --source " + thisVersion + " -version"));346setExecutable(bad);347TestResult tr = doExec(bad.toString());348if (!tr.contains("Option -version is not allowed in this context"))349error(tr, "Expected output not found");350show(tr);351}352353// #!.../java --source N @bad.at (contains -version)354@Test355void testTerminalOptionInShebangAtFile() throws IOException {356starting("testTerminalOptionInShebangAtFile");357if (skipShebangTest || isAIX || isMacOSX) {358// On MacOSX, we cannot distinguish between terminal options in a359// shebang @-file and those on the command line.360// On Solaris, all options after the first on the shebang line are361// ignored. Similar on AIX.362log.println("SKIPPED");363return;364}365// Use a short directory name, to avoid line length limitations366Path base = Files.createDirectories(Paths.get("testBadAtFile"));367Path bad_at = base.resolve("bad.at");368createFile(bad_at, List.of("-version"));369Path bad = base.resolve("bad");370createFile(bad, List.of(371"#!" + shebangJavaCmd + " --source " + thisVersion + " @" + bad_at));372setExecutable(bad);373TestResult tr = doExec(bad.toString());374if (!tr.contains("Option -version in @testBadAtFile/bad.at is "375+ "not allowed in this context"))376error(tr, "Expected output not found");377show(tr);378}379380// #!.../java --source N HelloWorld381@Test382void testMainClassInShebang() throws IOException {383starting("testMainClassInShebang");384if (skipShebangTest || isAIX || isMacOSX) {385// On MacOSX, we cannot distinguish between a main class on the386// shebang line and one on the command line.387// On Solaris, all options after the first on the shebang line are388// ignored. Similar on AIX.389log.println("SKIPPED");390return;391}392Path base = Files.createDirectories(Paths.get("testMainClassInShebang"));393Path bad = base.resolve("bad");394createFile(bad, List.of(395"#!" + shebangJavaCmd + " --source " + thisVersion + " HelloWorld"));396setExecutable(bad);397TestResult tr = doExec(bad.toString());398if (!tr.contains("Cannot specify main class in this context"))399error(tr, "Expected output not found");400show(tr);401}402403//--------------------------------------------------------------------------404405private void starting(String label) {406System.out.println();407System.out.println("*** Starting: " + label + " (stdout)");408409System.err.println();410System.err.println("*** Starting: " + label + " (stderr)");411}412413private void show(TestResult tr) {414log.println("*** Test Output:");415for (String line: tr.testOutput) {416log.println(line);417}418log.println("*** End Of Test Output:");419}420421private Map<String,String> getLauncherDebugEnv() {422return Map.of("_JAVA_LAUNCHER_DEBUG", "1");423}424425private Path getSimpleFile(String name, boolean shebang) throws IOException {426Path file = Paths.get(name);427if (!Files.exists(file)) {428createFile(file, List.of(429(shebang ? "#!" + shebangJavaCmd + " --source=" + thisVersion: ""),430"public class Simple {",431" public static void main(String[] args) {",432" System.out.println(java.util.Arrays.toString(args));",433" }}"));434}435return file;436}437438private void createFile(Path file, List<String> lines) throws IOException {439lines.stream()440.filter(line -> line.length() > 128)441.forEach(line -> {442log.println("*** Warning: long line ("443+ line.length()444+ " chars) in file " + file);445log.println("*** " + line);446});447log.println("*** File: " + file);448lines.stream().forEach(log::println);449log.println("*** End Of File");450createFile(file.toFile(), lines);451}452453private Path setExecutable(Path file) throws IOException {454Set<PosixFilePermission> perms = Files.getPosixFilePermissions(file);455perms.add(PosixFilePermission.OWNER_EXECUTE);456Files.setPosixFilePermissions(file, perms);457return file;458}459460private void error(TestResult tr, String message) {461show(tr);462throw new RuntimeException(message);463}464}465466467