1 /*
   2  * Copyright (c) 2017, 2018, 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 /*
  25  * @test
  26  * @bug 8192920
  27  * @summary Test source mode
  28  * @modules jdk.compiler
  29  * @run main SourceMode
  30  */
  31 
  32 
  33 import java.io.IOException;
  34 import java.nio.file.Files;
  35 import java.nio.file.Path;
  36 import java.nio.file.Paths;
  37 import java.nio.file.attribute.PosixFilePermission;
  38 import java.util.ArrayList;
  39 import java.util.Arrays;
  40 import java.util.HashMap;
  41 import java.util.List;
  42 import java.util.Map;
  43 import java.util.Set;
  44 
  45 public class SourceMode extends TestHelper {
  46 
  47     public static void main(String... args) throws Exception {
  48         new SourceMode().run(args);
  49     }
  50 
  51     // java Simple.java 1 2 3
  52     @Test
  53     void testSimpleJava() throws IOException {
  54         Path file = getSimpleFile("Simple.java", false);
  55         TestResult tr = doExec(javaCmd, file.toString(), "1", "2", "3");
  56         if (!tr.isOK())
  57             error(tr, "Bad exit code: " + tr.exitValue);
  58         if (!tr.contains("[1, 2, 3]"))
  59             error(tr, "Expected output not found");
  60         System.out.println(tr.testOutput);
  61     }
  62 
  63     // java --source 10 simple 1 2 3
  64     @Test
  65     void testSimple() throws IOException {
  66         Path file = getSimpleFile("simple", false);
  67         TestResult tr = doExec(javaCmd, "--source", "10", file.toString(), "1", "2", "3");
  68         if (!tr.isOK())
  69             error(tr, "Bad exit code: " + tr.exitValue);
  70         if (!tr.contains("[1, 2, 3]"))
  71             error(tr, "Expected output not found");
  72         System.out.println(tr.testOutput);
  73     }
  74 
  75     // execSimple 1 2 3
  76     @Test
  77     void testExecSimple() throws IOException {
  78         if (isWindows) // Will not work without cygwin, pass silently
  79             return;
  80         Path file = setExecutable(getSimpleFile("execSimple", true));
  81         TestResult tr = doExec(file.toAbsolutePath().toString(), "1", "2", "3");
  82         if (!tr.isOK())
  83             error(tr, "Bad exit code: " + tr.exitValue);
  84         if (!tr.contains("[1, 2, 3]"))
  85             error(tr, "Expected output not found");
  86         System.out.println(tr.testOutput);
  87     }
  88 
  89     // java @simpleJava.at  (contains Simple.java 1 2 3)
  90     @Test
  91     void testSimpleJavaAtFile() throws IOException {
  92         Path file = getSimpleFile("Simple.java", false);
  93         Path atFile = Paths.get("simpleJava.at");
  94         createFile(atFile.toFile(), List.of(file + " 1 2 3"));
  95         TestResult tr = doExec(javaCmd, "@" + atFile);
  96         if (!tr.isOK())
  97             error(tr, "Bad exit code: " + tr.exitValue);
  98         if (!tr.contains("[1, 2, 3]"))
  99             error(tr, "Expected output not found");
 100         System.out.println(tr.testOutput);
 101     }
 102 
 103     // java @simple.at  (contains --source 10 simple 1 2 3)
 104     @Test
 105     void testSimpleAtFile() throws IOException {
 106         Path file = getSimpleFile("simple", false);
 107         Path atFile = Paths.get("simple.at");
 108         createFile(atFile.toFile(), List.of("--source 10 " + file + " 1 2 3"));
 109         TestResult tr = doExec(javaCmd, "@" + atFile);
 110         if (!tr.isOK())
 111             error(tr, "Bad exit code: " + tr.exitValue);
 112         if (!tr.contains("[1, 2, 3]"))
 113             error(tr, "Expected output not found");
 114         System.out.println(tr.testOutput);
 115     }
 116 
 117     // java -cp classes Main.java 1 2 3
 118     @Test
 119     void testClasspath() throws IOException {
 120         Path base = Files.createDirectories(Paths.get("testClasspath"));
 121         Path otherJava = base.resolve("Other.java");
 122         createFile(otherJava.toFile(), List.of(
 123             "public class Other {",
 124             "  public static String join(String[] args) {",
 125             "    return String.join(\"-\", args);",
 126             "  }",
 127             "}"
 128         ));
 129         Path classes = Files.createDirectories(base.resolve("classes"));
 130         Path mainJava = base.resolve("Main.java");
 131         createFile(mainJava.toFile(), List.of(
 132             "class Main {",
 133             "  public static void main(String[] args) {",
 134             "    System.out.println(Other.join(args));",
 135             "  }}"
 136         ));
 137         compile("-d", classes.toString(), otherJava.toString());
 138         TestResult tr = doExec(javaCmd, "-cp", classes.toString(),
 139                 mainJava.toString(), "1", "2", "3");
 140         if (!tr.isOK())
 141             error(tr, "Bad exit code: " + tr.exitValue);
 142         if (!tr.contains("1-2-3"))
 143             error(tr, "Expected output not found");
 144         System.out.println(tr.testOutput);
 145     }
 146 
 147     // java --add-exports=... Export.java --help
 148     @Test
 149     void testAddExports() throws IOException {
 150         Path exportJava = Paths.get("Export.java");
 151         createFile(exportJava.toFile(), List.of(
 152             "public class Export {",
 153             "  public static void main(String[] args) {",
 154             "    new com.sun.tools.javac.main.Main(\"demo\").compile(args);",
 155             "  }",
 156             "}"
 157         ));
 158         // verify access fails without --add-exports
 159         TestResult tr1 = doExec(javaCmd, exportJava.toString(), "--help");
 160         if (tr1.isOK())
 161             error(tr1, "Compilation succeeded unexpectedly");
 162         // verify access succeeds with --add-exports
 163         TestResult tr2 = doExec(javaCmd,
 164             "--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
 165             exportJava.toString(), "--help");
 166         if (!tr2.isOK())
 167             error(tr2, "Bad exit code: " + tr2.exitValue);
 168         if (!(tr2.contains("demo") && tr2.contains("Usage")))
 169             error(tr2, "Expected output not found");
 170     }
 171 
 172     // java -cp ... HelloWorld.java  (for a class "java" in package "HelloWorld")
 173     @Test
 174     void testClassNamedJava() throws IOException {
 175         Path base = Files.createDirectories(Paths.get("testClassNamedJava"));
 176         Path src = Files.createDirectories(base.resolve("src"));
 177         Path srcfile = src.resolve("java.java");
 178         createFile(srcfile.toFile(), List.of(
 179                 "package HelloWorld;",
 180                 "class java {",
 181                 "    public static void main(String... args) {",
 182                 "        System.out.println(HelloWorld.java.class.getName());",
 183                 "    }",
 184                 "}"
 185         ));
 186         Path classes = base.resolve("classes");
 187         compile("-d", classes.toString(), srcfile.toString());
 188         TestResult tr =
 189             doExec(javaCmd, "-cp", classes.toString(), "HelloWorld.java");
 190         if (!tr.isOK())
 191             error(tr, "Command failed");
 192         if (!tr.contains("HelloWorld.java"))
 193             error(tr, "Expected output not found");
 194     }
 195 
 196     // java --source
 197     @Test
 198     void testSourceNoArg() throws IOException {
 199         TestResult tr = doExec(javaCmd, "--source");
 200         if (tr.isOK())
 201             error(tr, "Command succeeded unexpectedly");
 202         System.err.println(tr);
 203         if (!tr.contains("--source requires source version"))
 204             error(tr, "Expected output not found");
 205     }
 206 
 207     // java --source 10 -jar simple.jar
 208     @Test
 209     void testSourceJarConflict() throws IOException {
 210         Path base = Files.createDirectories(Paths.get("testSourceJarConflict"));
 211         Path file = getSimpleFile("Simple.java", false);
 212         Path classes = Files.createDirectories(base.resolve("classes"));
 213         compile("-d", classes.toString(), file.toString());
 214         Path simpleJar = base.resolve("simple.jar");
 215         createJar("cf", simpleJar.toString(), "-C", classes.toString(), ".");
 216         TestResult tr =
 217             doExec(javaCmd, "--source", "10", "-jar", simpleJar.toString());
 218         if (tr.isOK())
 219             error(tr, "Command succeeded unexpectedly");
 220         if (!tr.contains("Option -jar is not allowed with --source"))
 221             error(tr, "Expected output not found");
 222     }
 223 
 224     // java --source 10 -m jdk.compiler
 225     @Test
 226     void testSourceModuleConflict() throws IOException {
 227         TestResult tr = doExec(javaCmd, "--source", "10", "-m", "jdk.compiler");
 228         if (tr.isOK())
 229             error(tr, "Command succeeded unexpectedly");
 230         if (!tr.contains("Option -m is not allowed with --source"))
 231             error(tr, "Expected output not found");
 232     }
 233 
 234     // #!.../java --source 10 -version
 235     @Test
 236     void testTerminalOptionInShebang() throws IOException {
 237         if (isWindows) // Will not work without cygwin, pass silently
 238             return;
 239         Path base = Files.createDirectories(
 240             Paths.get("testTerminalOptionInShebang"));
 241         Path bad = base.resolve("bad");
 242         createFile(bad.toFile(), List.of(
 243             "#!" + javaCmd + " --source 10 -version"));
 244         setExecutable(bad);
 245         TestResult tr = doExec(bad.toString());
 246         if (!tr.contains("Option -version is not allowed in this context"))
 247             error(tr, "Expected output not found");
 248     }
 249 
 250     // #!.../java --source 10 @bad.at  (contains -version)
 251     @Test
 252     void testTerminalOptionInShebangAtFile() throws IOException {
 253         if (isWindows) // Will not work without cygwin, pass silently
 254             return;
 255         // Use a short directory name, to avoid line length limitations
 256         Path base = Files.createDirectories(Paths.get("testBadAtFile"));
 257         Path bad_at = base.resolve("bad.at");
 258         createFile(bad_at.toFile(), List.of("-version"));
 259         Path bad = base.resolve("bad");
 260         createFile(bad.toFile(), List.of(
 261             "#!" + javaCmd + " --source 10 @" + bad_at));
 262         setExecutable(bad);
 263         TestResult tr = doExec(bad.toString());
 264         System.err.println("JJG JJG " + tr);
 265         if (!tr.contains("Option -version in @testBadAtFile/bad.at is "
 266                 + "not allowed in this context"))
 267             error(tr, "Expected output not found");
 268     }
 269 
 270     // #!.../java --source 10 HelloWorld
 271     @Test
 272     void testMainClassInShebang() throws IOException {
 273         if (isWindows) // Will not work without cygwin, pass silently
 274             return;
 275         Path base = Files.createDirectories(Paths.get("testMainClassInShebang"));
 276         Path bad = base.resolve("bad");
 277         createFile(bad.toFile(), List.of(
 278             "#!" + javaCmd + " --source 10 HelloWorld"));
 279         setExecutable(bad);
 280         TestResult tr = doExec(bad.toString());
 281         if (!tr.contains("Cannot specify main class in this context"))
 282             error(tr, "Expected output not found");
 283     }
 284 
 285     //--------------------------------------------------------------------------
 286 
 287     private Map<String,String> getLauncherDebugEnv() {
 288         return Map.of("_JAVA_LAUNCHER_DEBUG", "1");
 289     }
 290 
 291     private Path getSimpleFile(String name, boolean shebang) throws IOException {
 292         Path file = Paths.get(name);
 293         if (!Files.exists(file)) {
 294             createFile(file.toFile(), List.of(
 295                 (shebang ? "#!" + javaCmd + " --source 10" : ""),
 296                 "public class Simple {",
 297                 "  public static void main(String[] args) {",
 298                 "    System.out.println(java.util.Arrays.toString(args));",
 299                 "  }}"));
 300         }
 301         return file;
 302     }
 303 
 304     private Path setExecutable(Path file) throws IOException {
 305         Set<PosixFilePermission> perms = Files.getPosixFilePermissions(file);
 306         perms.add(PosixFilePermission.OWNER_EXECUTE);
 307         Files.setPosixFilePermissions(file, perms);
 308         return file;
 309     }
 310 
 311     private void error(TestResult tr, String message) {
 312         System.err.println(tr);
 313         throw new RuntimeException(message);
 314     }
 315 }