1 /* 2 * Copyright (c) 2015, 2017, 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 8151754 8080883 8160089 8170162 8166581 8172102 8171343 8178023 8186708 8179856 8185840 8190383 26 * @summary Testing startExCe-up options. 27 * @modules jdk.compiler/com.sun.tools.javac.api 28 * jdk.compiler/com.sun.tools.javac.main 29 * jdk.jdeps/com.sun.tools.javap 30 * jdk.jshell/jdk.internal.jshell.tool 31 * @library /tools/lib 32 * @build Compiler toolbox.ToolBox 33 * @run testng StartOptionTest 34 */ 35 import java.io.ByteArrayInputStream; 36 import java.io.ByteArrayOutputStream; 37 import java.io.InputStream; 38 import java.io.PrintStream; 39 import java.nio.charset.StandardCharsets; 40 import java.nio.file.Path; 41 import java.util.HashMap; 42 import java.util.Locale; 43 import java.util.function.Consumer; 44 45 import java.util.logging.Level; 46 import java.util.logging.Logger; 47 import org.testng.annotations.AfterMethod; 48 import org.testng.annotations.BeforeMethod; 49 import org.testng.annotations.Test; 50 import jdk.jshell.tool.JavaShellToolBuilder; 51 import static org.testng.Assert.assertEquals; 52 import static org.testng.Assert.assertFalse; 53 import static org.testng.Assert.assertTrue; 54 import static org.testng.Assert.fail; 55 56 @Test 57 public class StartOptionTest { 58 59 protected ByteArrayOutputStream cmdout; 60 protected ByteArrayOutputStream cmderr; 61 protected ByteArrayOutputStream console; 62 protected ByteArrayOutputStream userout; 63 protected ByteArrayOutputStream usererr; 64 protected InputStream cmdInStream; 65 66 private JavaShellToolBuilder builder() { 67 // turn on logging of launch failures 68 Logger.getLogger("jdk.jshell.execution").setLevel(Level.ALL); 69 return JavaShellToolBuilder 70 .builder() 71 .out(new PrintStream(cmdout), new PrintStream(console), new PrintStream(userout)) 72 .err(new PrintStream(cmderr), new PrintStream(usererr)) 73 .in(cmdInStream, null) 74 .persistence(new HashMap<>()) 75 .env(new HashMap<>()) 76 .locale(Locale.ROOT); 77 } 78 79 protected int runShell(String... args) { 80 try { 81 return builder() 82 .start(args); 83 } catch (Exception ex) { 84 fail("Repl tool died with exception", ex); 85 } 86 return -1; // for compiler 87 } 88 89 protected void check(ByteArrayOutputStream str, Consumer<String> checkOut, String label) { 90 byte[] bytes = str.toByteArray(); 91 str.reset(); 92 String out = new String(bytes, StandardCharsets.UTF_8); 93 if (checkOut != null) { 94 checkOut.accept(out); 95 } else { 96 assertEquals(out, "", label + ": Expected empty -- "); 97 } 98 } 99 100 protected void checkExit(int ec, Consumer<Integer> checkCode) { 101 if (checkCode != null) { 102 checkCode.accept(ec); 103 } else { 104 assertEquals(ec, 0, "Expected standard exit code (0), but found: " + ec); 105 } 106 } 107 108 // Start and check the resultant: exit code (Ex), command output (Co), 109 // user output (Uo), command error (Ce), and console output (Cn) 110 protected void startExCoUoCeCn(Consumer<Integer> checkExitCode, 111 Consumer<String> checkCmdOutput, 112 Consumer<String> checkUserOutput, 113 Consumer<String> checkError, 114 Consumer<String> checkConsole, 115 String... args) { 116 int ec = runShell(args); 117 checkExit(ec, checkExitCode); 118 check(cmdout, checkCmdOutput, "cmdout"); 119 check(cmderr, checkError, "cmderr"); 120 check(console, checkConsole, "console"); 121 check(userout, checkUserOutput, "userout"); 122 check(usererr, null, "usererr"); 123 } 124 125 // Start with an exit code and command error check 126 protected void startExCe(int eec, Consumer<String> checkError, String... args) { 127 StartOptionTest.this.startExCoUoCeCn( 128 (Integer ec) -> assertEquals((int) ec, eec, 129 "Expected error exit code (" + eec + "), but found: " + ec), 130 null, null, checkError, null, args); 131 } 132 133 // Start with a command output check 134 protected void startCo(Consumer<String> checkCmdOutput, String... args) { 135 StartOptionTest.this.startExCoUoCeCn(null, checkCmdOutput, null, null, null, args); 136 } 137 138 private Consumer<String> assertOrNull(String expected, String label) { 139 return expected == null 140 ? null 141 : s -> assertEquals(s.trim(), expected.trim(), label); 142 } 143 144 // Start and check the resultant: exit code (Ex), command output (Co), 145 // user output (Uo), command error (Ce), and console output (Cn) 146 protected void startExCoUoCeCn(int expectedExitCode, 147 String expectedCmdOutput, 148 String expectedUserOutput, 149 String expectedError, 150 String expectedConsole, 151 String... args) { 152 startExCoUoCeCn( 153 expectedExitCode == 0 154 ? null 155 : (Integer i) -> assertEquals((int) i, expectedExitCode, 156 "Expected exit code (" + expectedExitCode + "), but found: " + i), 157 assertOrNull(expectedCmdOutput, "cmdout: "), 158 assertOrNull(expectedUserOutput, "userout: "), 159 assertOrNull(expectedError, "cmderr: "), 160 assertOrNull(expectedConsole, "console: "), 161 args); 162 } 163 164 // Start with an expected exit code and command error 165 protected void startExCe(int ec, String expectedError, String... args) { 166 startExCoUoCeCn(ec, null, null, expectedError, null, args); 167 } 168 169 // Start with an expected command output 170 protected void startCo(String expectedCmdOutput, String... args) { 171 startExCoUoCeCn(0, expectedCmdOutput, null, null, null, args); 172 } 173 174 // Start with an expected user output 175 protected void startUo(String expectedUserOutput, String... args) { 176 startExCoUoCeCn(0, null, expectedUserOutput, null, null, args); 177 } 178 179 @BeforeMethod 180 public void setUp() { 181 cmdout = new ByteArrayOutputStream(); 182 cmderr = new ByteArrayOutputStream(); 183 console = new ByteArrayOutputStream(); 184 userout = new ByteArrayOutputStream(); 185 usererr = new ByteArrayOutputStream(); 186 setIn("/exit\n"); 187 } 188 189 protected String writeToFile(String stuff) { 190 Compiler compiler = new Compiler(); 191 Path p = compiler.getPath("doit.repl"); 192 compiler.writeToFile(p, stuff); 193 return p.toString(); 194 } 195 196 // Set the input from a String 197 protected void setIn(String s) { 198 cmdInStream = new ByteArrayInputStream(s.getBytes()); 199 } 200 201 // Test load files 202 public void testCommandFile() { 203 String fn = writeToFile("String str = \"Hello \"\n" + 204 "/list\n" + 205 "System.out.println(str + str)\n" + 206 "/exit\n"); 207 startExCoUoCeCn(0, 208 "1 : String str = \"Hello \";\n", 209 "Hello Hello", 210 null, 211 null, 212 "--no-startup", fn, "-s"); 213 } 214 215 // Test that the usage message is printed 216 public void testUsage() { 217 for (String opt : new String[]{"-h", "--help"}) { 218 startCo(s -> { 219 assertTrue(s.split("\n").length >= 7, "Not enough usage lines: " + s); 220 assertTrue(s.startsWith("Usage: jshell <option>..."), "Unexpect usage start: " + s); 221 assertTrue(s.contains("--show-version"), "Expected help: " + s); 222 assertFalse(s.contains("Welcome"), "Unexpected start: " + s); 223 }, opt); 224 } 225 } 226 227 // Test the --help-extra message 228 public void testHelpExtra() { 229 for (String opt : new String[]{"-X", "--help-extra"}) { 230 startCo(s -> { 231 assertTrue(s.split("\n").length >= 5, "Not enough help-extra lines: " + s); 232 assertTrue(s.contains("--add-exports"), "Expected --add-exports: " + s); 233 assertTrue(s.contains("--execution"), "Expected --add-exports: " + s); 234 assertFalse(s.contains("Welcome"), "Unexpected start: " + s); 235 }, opt); 236 } 237 } 238 239 // Test handling of bogus options 240 public void testUnknown() { 241 startExCe(1, "Unknown option: u", "-unknown"); 242 startExCe(1, "Unknown option: unknown", "--unknown"); 243 } 244 245 // Test that input is read with "-" and there is no extra output. 246 public void testHypenFile() { 247 setIn("System.out.print(\"Hello\");\n"); 248 startUo("Hello", "-"); 249 setIn("System.out.print(\"Hello\");\n"); 250 startUo("Hello", "-", "-"); 251 String fn = writeToFile("System.out.print(\"===\");"); 252 setIn("System.out.print(\"Hello\");\n"); 253 startUo("===Hello===", fn, "-", fn); 254 // check that errors go to standard error 255 setIn(") Foobar"); 256 startExCe(0, s -> assertTrue(s.contains("illegal start of expression"), 257 "cmderr: illegal start of expression"), 258 "-"); 259 } 260 261 // Test that user specified exit codes are propagated 262 public void testExitCode() { 263 setIn("/exit 57\n"); 264 startExCoUoCeCn(57, null, null, null, "-> /exit 57", "-s"); 265 setIn("int eight = 8\n" + 266 "/exit eight + \n" + 267 " eight\n"); 268 startExCoUoCeCn(16, null, null, null, 269 "-> int eight = 8\n" + 270 "-> /exit eight + \n" + 271 ">> eight", 272 "-s"); 273 } 274 275 // Test that non-existent load file sends output to stderr and does not startExCe (no welcome). 276 public void testUnknownLoadFile() { 277 startExCe(1, "File 'UNKNOWN' for 'jshell' is not found.", "UNKNOWN"); 278 } 279 280 // Test bad usage of the --startup option 281 public void testStartup() { 282 String fn = writeToFile(""); 283 startExCe(1, "Argument to startup missing.", "--startup"); 284 startExCe(1, "Conflicting options: both --startup and --no-startup were used.", "--no-startup", "--startup", fn); 285 startExCe(1, "Conflicting options: both --startup and --no-startup were used.", "--startup", fn, "--no-startup"); 286 startExCe(1, "Argument to startup missing.", "--no-startup", "--startup"); 287 } 288 289 // Test an option that causes the back-end to fail is propagated 290 public void testStartupFailedOption() { 291 startExCe(1, s -> assertTrue(s.contains("Unrecognized option: -hoge-foo-bar"), "cmderr: " + s), 292 "-R-hoge-foo-bar"); 293 } 294 295 // Test the use of non-existant files with the --startup option 296 public void testStartupUnknown() { 297 startExCe(1, "File 'UNKNOWN' for '--startup' is not found.", "--startup", "UNKNOWN"); 298 startExCe(1, "File 'UNKNOWN' for '--startup' is not found.", "--startup", "DEFAULT", "--startup", "UNKNOWN"); 299 } 300 301 // Test bad usage of --class-path option 302 public void testClasspath() { 303 for (String cp : new String[]{"--class-path"}) { 304 startExCe(1, "Only one --class-path option may be used.", cp, ".", "--class-path", "."); 305 startExCe(1, "Argument to class-path missing.", cp); 306 } 307 } 308 309 // Test bogus module on --add-modules option 310 public void testUnknownModule() { 311 startExCe(1, s -> assertTrue(s.contains("rror") && s.contains("unKnown"), "cmderr: " + s), 312 "--add-modules", "unKnown"); 313 } 314 315 // Test that muliple feedback options fail 316 public void testFeedbackOptionConflict() { 317 startExCe(1, "Only one feedback option (--feedback, -q, -s, or -v) may be used.", 318 "--feedback", "concise", "--feedback", "verbose"); 319 startExCe(1, "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "--feedback", "concise", "-s"); 320 startExCe(1, "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "--feedback", "verbose", "-q"); 321 startExCe(1, "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "--feedback", "concise", "-v"); 322 startExCe(1, "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "-v", "--feedback", "concise"); 323 startExCe(1, "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "-q", "-v"); 324 startExCe(1, "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "-s", "-v"); 325 startExCe(1, "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "-v", "-q"); 326 startExCe(1, "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "-q", "-s"); 327 } 328 329 // Test bogus arguments to the --feedback option 330 public void testNegFeedbackOption() { 331 startExCe(1, "Argument to feedback missing.", "--feedback"); 332 startExCe(1, "Does not match any current feedback mode: blorp -- --feedback blorp", "--feedback", "blorp"); 333 } 334 335 // Test --version 336 public void testVersion() { 337 startCo(s -> { 338 assertTrue(s.startsWith("jshell"), "unexpected version: " + s); 339 assertFalse(s.contains("Welcome"), "Unexpected start: " + s); 340 }, 341 "--version"); 342 } 343 344 // Test --show-version 345 public void testShowVersion() { 346 startExCoUoCeCn(null, 347 s -> { 348 assertTrue(s.startsWith("jshell"), "unexpected version: " + s); 349 assertTrue(s.contains("Welcome"), "Expected start (but got no welcome): " + s); 350 }, 351 null, 352 null, 353 s -> assertTrue(s.trim().startsWith("jshell>"), "Expected prompt, got: " + s), 354 "--show-version"); 355 } 356 357 @AfterMethod 358 public void tearDown() { 359 cmdout = null; 360 cmderr = null; 361 console = null; 362 userout = null; 363 usererr = null; 364 cmdInStream = null; 365 } 366 }