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 builder()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 runShell(String... args)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 check(ByteArrayOutputStream str, Consumer<String> checkOut, String label)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 checkExit(int ec, Consumer<Integer> checkCode)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) startExCoUoCeCn(Consumer<Integer> checkExitCode, Consumer<String> checkCmdOutput, Consumer<String> checkUserOutput, Consumer<String> checkError, Consumer<String> checkConsole, String... args)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 startExCe(int eec, Consumer<String> checkError, String... args)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 startCo(Consumer<String> checkCmdOutput, String... args)138 protected void startCo(Consumer<String> checkCmdOutput, String... args) { 139 StartOptionTest.this.startExCoUoCeCn(null, checkCmdOutput, null, null, null, args); 140 } 141 assertOrNull(String expected, String label)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) startExCoUoCeCn(int expectedExitCode, String expectedCmdOutput, String expectedUserOutput, String expectedError, String expectedConsole, String... args)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 startExCe(int ec, String expectedError, String... args)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 startCo(String expectedCmdOutput, String... args)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 startUo(String expectedUserOutput, String... args)179 protected void startUo(String expectedUserOutput, String... args) { 180 startExCoUoCeCn(0, null, expectedUserOutput, null, null, args); 181 } 182 183 @BeforeMethod setUp()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 writeToFile(String stuff)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 setIn(String s)201 protected void setIn(String s) { 202 cmdInStream = new ByteArrayInputStream(s.getBytes()); 203 } 204 205 // Test load files testCommandFile()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 testUsage()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 testHelpExtra()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 testUnknown()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. testHypenFile()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 testExitCode()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). testUnknownLoadFile()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 testStartup()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 testStartupFailedOption()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 testStartupUnknown()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 testClasspath()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 testUnknownModule()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 testFeedbackOptionConflict()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 testNegFeedbackOption()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 testVersion()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 testShowVersion()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 tearDown()362 public void tearDown() { 363 cmdout = null; 364 cmderr = null; 365 console = null; 366 userout = null; 367 usererr = null; 368 cmdInStream = null; 369 } 370 stripAnsi(String str)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 } 378