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