1 /*
   2  * Copyright (c) 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 launcher
  28  * @library /tools/lib
  29  * @modules jdk.compiler/com.sun.tools.javac.api
  30  *          jdk.compiler/com.sun.tools.javac.launcher
  31  *          jdk.compiler/com.sun.tools.javac.main
  32  * @build toolbox.JavaTask toolbox.JavacTask toolbox.TestRunner toolbox.ToolBox
  33  * @run main SourceLauncherTest
  34  */
  35 
  36 import java.io.ByteArrayOutputStream;
  37 import java.io.IOException;
  38 import java.io.PrintStream;
  39 import java.io.PrintWriter;
  40 import java.io.StringWriter;
  41 import java.lang.reflect.InvocationTargetException;
  42 import java.nio.file.Files;
  43 import java.nio.file.Path;
  44 import java.nio.file.Paths;
  45 import java.util.ArrayList;
  46 import java.util.Collections;
  47 import java.util.List;
  48 import java.util.Properties;
  49 
  50 import com.sun.tools.javac.launcher.Main;
  51 
  52 import toolbox.JavaTask;
  53 import toolbox.JavacTask;
  54 import toolbox.Task;
  55 import toolbox.TestRunner;
  56 import toolbox.TestRunner;
  57 import toolbox.ToolBox;
  58 
  59 public class SourceLauncherTest extends TestRunner {
  60     public static void main(String... args) throws Exception {
  61         SourceLauncherTest t = new SourceLauncherTest();
  62         t.runTests(m -> new Object[] { Paths.get(m.getName()) });
  63     }
  64 
  65     SourceLauncherTest() {
  66         super(System.err);
  67         tb = new ToolBox();
  68     }
  69 
  70     private final ToolBox tb;
  71 
  72     /*
  73      * Positive tests.
  74      */
  75 
  76     @Test
  77     public void testHelloWorld(Path base) throws IOException {
  78         tb.writeJavaFiles(base,
  79             "import java.util.Arrays;\n" +
  80             "class HelloWorld {\n" +
  81             "    public static void main(String... args) {\n" +
  82             "        System.out.println(\"Hello World! \" + Arrays.toString(args));\n" +
  83             "    }\n" +
  84             "}");
  85         testSuccess(base.resolve("HelloWorld.java"), "Hello World! [1, 2, 3]\n");
  86     }
  87 
  88     @Test
  89     public void testHelloWorldInPackage(Path base) throws IOException {
  90         tb.writeJavaFiles(base,
  91             "package hello;\n" +
  92             "import java.util.Arrays;\n" +
  93             "class World {\n" +
  94             "    public static void main(String... args) {\n" +
  95             "        System.out.println(\"Hello World! \" + Arrays.toString(args));\n" +
  96             "    }\n" +
  97             "}");
  98         testSuccess(base.resolve("hello").resolve("World.java"), "Hello World! [1, 2, 3]\n");
  99     }
 100 
 101     @Test
 102     public void testHelloWorldWithAux(Path base) throws IOException {
 103         tb.writeJavaFiles(base,
 104             "import java.util.Arrays;\n" +
 105             "class HelloWorld {\n" +
 106             "    public static void main(String... args) {\n" +
 107             "        Aux.write(args);\n" +
 108             "    }\n" +
 109             "}\n" +
 110             "class Aux {\n" +
 111             "    static void write(String... args) {\n" +
 112             "        System.out.println(\"Hello World! \" + Arrays.toString(args));\n" +
 113             "    }\n" +
 114             "}");
 115         testSuccess(base.resolve("HelloWorld.java"), "Hello World! [1, 2, 3]\n");
 116     }
 117 
 118     void testSuccess(Path file, String expect) throws IOException {
 119         Result r = run(file, Collections.emptyList(), List.of("1", "2", "3"));
 120         checkEqual("stdout", r.stdOut, expect);
 121         checkEmpty("stderr", r.stdErr);
 122         checkNull("exception", r.exception);
 123     }
 124 
 125     /*
 126      * Negative tests: cannot find or execute main method.
 127      */
 128 
 129     @Test
 130     public void testNoClass(Path base) throws IOException {
 131         Files.createDirectories(base);
 132         Path file = base.resolve("NoClass.java");
 133         Files.write(file, List.of("package p;"));
 134         testError(file, "", "error: no class declared in file");
 135     }
 136 
 137     @Test
 138     public void testWrongClass(Path base) throws IOException {
 139         Path src = base.resolve("src");
 140         Path file = src.resolve("WrongClass.java");
 141         tb.writeJavaFiles(src, "class WrongClass { }");
 142         Path classes = Files.createDirectories(base.resolve("classes"));
 143         new JavacTask(tb)
 144                 .outdir(classes)
 145                 .files(file)
 146                 .run();
 147         String log = new JavaTask(tb)
 148                 .classpath(classes.toString())
 149                 .className(file.toString())
 150                 .run(Task.Expect.FAIL)
 151                 .getOutput(Task.OutputKind.STDERR);
 152         checkEqual("stderr", log.trim(), 
 153                 "error: class found on application class path: WrongClass");
 154     }
 155 
 156     @Test
 157     public void testSyntaxErr(Path base) throws IOException {
 158         tb.writeJavaFiles(base, "class SyntaxErr {");
 159         Path file = base.resolve("SyntaxErr.java");
 160         testError(file,
 161                 file + ":1: error: reached end of file while parsing\n" +
 162                 "class SyntaxErr {\n" +
 163                 "                 ^\n" +
 164                 "1 error\n",
 165                 "error: compilation failed");
 166     }
 167 
 168     @Test
 169     public void testNoMain(Path base) throws IOException {
 170         tb.writeJavaFiles(base, "class NoMain { }");
 171         testError(base.resolve("NoMain.java"), "",
 172                 "error: can't find main(String[]) method in class: NoMain");
 173     }
 174 
 175     @Test
 176     public void testMainBadParams(Path base) throws IOException {
 177         tb.writeJavaFiles(base,
 178                 "class BadParams { public static void main() { } }");
 179         testError(base.resolve("BadParams.java"), "",
 180                 "error: can't find main(String[]) method in class: BadParams");
 181     }
 182 
 183     @Test
 184     public void testMainNotPublic(Path base) throws IOException {
 185         tb.writeJavaFiles(base,
 186                 "class NotPublic { static void main(String... args) { } }");
 187         testError(base.resolve("NotPublic.java"), "",
 188                 "error: 'main' method is not declared 'public static'");
 189     }
 190 
 191     @Test
 192     public void testMainNotStatic(Path base) throws IOException {
 193         tb.writeJavaFiles(base,
 194                 "class NotStatic { public void main(String... args) { } }");
 195         testError(base.resolve("NotStatic.java"), "",
 196                 "error: 'main' method is not declared 'public static'");
 197     }
 198 
 199     @Test
 200     public void testMainNotVoid(Path base) throws IOException {
 201         tb.writeJavaFiles(base,
 202                 "class NotVoid { public static int main(String... args) { return 0; } }");
 203         testError(base.resolve("NotVoid.java"), "",
 204                 "error: 'main' method is not declared with a return type of 'void'");
 205     }
 206 
 207     @Test
 208     public void testClassInModule(Path base) throws IOException {
 209         tb.writeJavaFiles(base, "package java.net; class InModule { }");
 210         Path file = base.resolve("java").resolve("net").resolve("InModule.java");
 211         testError(file,
 212                 file + ":1: error: package exists in another module: java.base\n" +
 213                 "package java.net; class InModule { }\n" +
 214                 "^\n" +
 215                 "1 error\n",
 216                 "error: compilation failed");
 217     }
 218 
 219     @Test
 220     public void testBadSourceOpt(Path base) throws IOException {
 221         Files.createDirectories(base);
 222         Path file = base.resolve("DummyClass.java");
 223         Files.write(file, List.of("class DummyClass { }"));
 224         Properties sysProps = System.getProperties();
 225         Properties p = new Properties(sysProps);
 226         p.setProperty("jdk.internal.javac.source", "<BAD>");
 227         System.setProperties(p);
 228         try {
 229             testError(file, "", "error: invalid value for --source option: <BAD>");
 230         } finally {
 231             System.setProperties(sysProps);
 232         }
 233     }
 234 
 235     void testError(Path file, String expectStdErr, String expectFault) throws IOException {
 236         Result r = run(file, Collections.emptyList(), List.of("1", "2", "3"));
 237         checkEmpty("stdout", r.stdOut);
 238         checkEqual("stderr", r.stdErr, expectStdErr);
 239         checkFault("exception", r.exception, expectFault);
 240     }
 241 
 242     /*
 243      * Tests in which main throws an exception.
 244      */
 245     @Test
 246     public void testTargetException1(Path base) throws IOException {
 247         tb.writeJavaFiles(base,
 248             "import java.util.Arrays;\n" +
 249             "class Thrower {\n" +
 250             "    public static void main(String... args) {\n" +
 251             "        throwWhenZero(Integer.parseInt(args[0]));\n" +
 252             "    }\n" +
 253             "    static void throwWhenZero(int arg) {\n" +
 254             "        if (arg == 0) throw new Error(\"zero!\");\n" +
 255             "        throwWhenZero(arg - 1);\n" +
 256             "    }\n" +
 257             "}");
 258         Path file = base.resolve("Thrower.java");
 259         Result r = run(file, Collections.emptyList(), List.of("3"));
 260         checkEmpty("stdout", r.stdOut);
 261         checkEmpty("stderr", r.stdErr);
 262         checkTrace("exception", r.exception,
 263                 "java.lang.Error: zero!",
 264                 "at Thrower.throwWhenZero(Thrower.java:7)",
 265                 "at Thrower.throwWhenZero(Thrower.java:8)",
 266                 "at Thrower.throwWhenZero(Thrower.java:8)",
 267                 "at Thrower.throwWhenZero(Thrower.java:8)",
 268                 "at Thrower.main(Thrower.java:4)");
 269     }
 270 
 271     Result run(Path file, List<String> runtimeArgs, List<String> appArgs) {
 272         List<String> args = new ArrayList<>();
 273         args.add(file.toString());
 274         args.addAll(appArgs);
 275 
 276         PrintStream prev = System.out;
 277         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 278         try (PrintStream out = new PrintStream(baos, true)) {
 279             System.setOut(out);
 280             StringWriter sw = new StringWriter();
 281             try (PrintWriter err = new PrintWriter(sw, true)) {
 282                 Main m = new Main(err);
 283                 m.run(toArray(runtimeArgs), toArray(args));
 284                 return new Result(baos.toString(), sw.toString(), null);
 285             } catch (Throwable t) {
 286                 return new Result(baos.toString(), sw.toString(), t);
 287             }
 288         } finally {
 289             System.setOut(prev);
 290         }
 291     }
 292 
 293     void checkEqual(String name, String found, String expect) {
 294         expect = expect.replace("\n", tb.lineSeparator);
 295         out.println(name + ": " + found);
 296         out.println(name + ": " + found);
 297         if (!expect.equals(found)) {
 298             error("Unexpected output; expected: " + expect);
 299         }
 300     }
 301 
 302     void checkEmpty(String name, String found) {
 303         out.println(name + ": " + found);
 304         if (!found.isEmpty()) {
 305             error("Unexpected output; expected empty string");
 306         }
 307     }
 308 
 309     void checkNull(String name, Throwable found) {
 310         out.println(name + ": " + found);
 311         if (found != null) {
 312             error("Unexpected exception; expected null");
 313         }
 314     }
 315 
 316     void checkFault(String name, Throwable found, String expect) {
 317         expect = expect.replace("\n", tb.lineSeparator);
 318         out.println(name + ": " + found);
 319         if (!(found instanceof Main.Fault)) {
 320             error("Unexpected exception; expected Main.Fault");
 321         }
 322         if (!(found.getMessage().equals(expect))) {
 323             error("Unexpected detail message; expected: " + expect);
 324         }
 325     }
 326 
 327     void checkTrace(String name, Throwable found, String... expect) {
 328         if (!(found instanceof InvocationTargetException)) {
 329             error("Unexpected exception; expected InvocationTargetException");
 330             out.println("Found:");
 331             found.printStackTrace(out);
 332         }
 333         StringWriter sw = new StringWriter();
 334         try (PrintWriter pw = new PrintWriter(sw)) {
 335             ((InvocationTargetException) found).getTargetException().printStackTrace(pw);
 336         }
 337         String trace = sw.toString();
 338         out.println(name + ":\n" + trace);
 339         String[] traceLines = trace.trim().split("[\r\n]+\\s+");
 340         try {
 341             tb.checkEqual(List.of(traceLines), List.of(expect));
 342         } catch (Error e) {
 343             error(e.getMessage());
 344         }
 345     }
 346 
 347     String[] toArray(List<String> list) {
 348         return list.toArray(new String[list.size()]);
 349     }
 350 
 351     class Result {
 352         private final String stdOut;
 353         private final String stdErr;
 354         private final Throwable exception;
 355 
 356         Result(String stdOut, String stdErr, Throwable exception) {
 357             this.stdOut = stdOut;
 358             this.stdErr = stdErr;
 359             this.exception = exception;
 360         }
 361     }
 362 }