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 }