1 /* 2 * Copyright (c) 2016, 2020, 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 8153716 8143955 8151754 8150382 8153920 8156910 8131024 8160089 8153897 8167128 8154513 8170015 8170368 8172102 8172103 8165405 8173073 8173848 8174041 8173916 8174028 8174262 8174797 8177079 8180508 8177466 8172154 8192979 8191842 8198573 8198801 8210596 8210959 8215099 8199623 8236715 8239536 8247456 8246774 8238173 27 * @summary Simple jshell tool tests 28 * @modules jdk.compiler/com.sun.tools.javac.api 29 * jdk.compiler/com.sun.tools.javac.main 30 * jdk.jdeps/com.sun.tools.javap 31 * jdk.jshell/jdk.internal.jshell.tool 32 * @build KullaTesting TestingInputStream 33 * @run testng ToolSimpleTest 34 */ 35 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.List; 39 import java.util.Locale; 40 import java.util.function.Consumer; 41 import java.util.regex.Pattern; 42 import java.util.stream.Collectors; 43 import java.util.stream.Stream; 44 45 import org.testng.annotations.Test; 46 47 import static org.testng.Assert.assertEquals; 48 import static org.testng.Assert.assertTrue; 49 50 public class ToolSimpleTest extends ReplToolTesting { 51 52 @Test testRemaining()53 public void testRemaining() { 54 test( 55 (a) -> assertCommand(a, "int z; z =", "z ==> 0"), 56 (a) -> assertCommand(a, "5", "z ==> 5"), 57 (a) -> assertCommand(a, "/*nada*/; int q =", ""), 58 (a) -> assertCommand(a, "77", "q ==> 77"), 59 (a) -> assertCommand(a, "//comment;", ""), 60 (a) -> assertCommand(a, "int v;", "v ==> 0"), 61 (a) -> assertCommand(a, "int v; int c", 62 "v ==> 0\n" + 63 "c ==> 0") 64 ); 65 } 66 67 @Test testOpenComment()68 public void testOpenComment() { 69 test( 70 (a) -> assertCommand(a, "int z = /* blah", ""), 71 (a) -> assertCommand(a, "baz */ 5", "z ==> 5"), 72 (a) -> assertCommand(a, "/** hoge ", ""), 73 (a) -> assertCommand(a, "baz **/", ""), 74 (a) -> assertCommand(a, "int v", "v ==> 0") 75 ); 76 } 77 78 @Test testSwitchExpression()79 public void testSwitchExpression() { 80 test(false, new String[]{"--no-startup"}, 81 (a) -> assertCommand(a, "enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }", "| created enum Day"), 82 (a) -> assertCommand(a, "Day day = Day.FRIDAY;", "day ==> FRIDAY"), 83 (a) -> assertCommand(a, "switch (day) {", ""), 84 (a) -> assertCommand(a, "case MONDAY, FRIDAY, SUNDAY -> 6;", ""), 85 (a) -> assertCommand(a, "case TUESDAY -> 7;", ""), 86 (a) -> assertCommand(a, "case THURSDAY, SATURDAY -> 8;", ""), 87 (a) -> assertCommand(a, "case WEDNESDAY -> 9;", ""), 88 (a) -> assertCommandOutputContains(a, "}", " ==> 6") 89 ); 90 } 91 92 @Test testSwitchExpressionCompletion()93 public void testSwitchExpressionCompletion() { 94 test(false, new String[]{"--no-startup"}, 95 (a) -> assertCommand(a, "enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }", "| created enum Day"), 96 (a) -> assertCommand(a, "Day day = Day.FRIDAY;", "day ==> FRIDAY"), 97 (a) -> assertCommand(a, "switch (day) {", ""), 98 (a) -> assertCommand(a, "case MONDAY, FRIDAY, SUNDAY -> 6;", ""), 99 (a) -> assertCommand(a, "case TUESDAY -> 7;", ""), 100 (a) -> assertCommand(a, "case THURSDAY, SATURDAY -> 8;", ""), 101 (a) -> assertCommand(a, "case WEDNESDAY -> 9;", ""), 102 (a) -> assertCommand(a, "} +", ""), 103 (a) -> assertCommandOutputContains(a, "1000", " ==> 1006") 104 ); 105 } 106 107 @Test testLessThan()108 public void testLessThan() { 109 test( 110 (a) -> assertCommand(a, "45", "$1 ==> 45"), 111 (a) -> assertCommand(a, "72", "$2 ==> 72"), 112 (a) -> assertCommand(a, "$1 < $2", "$3 ==> true"), 113 (a) -> assertCommand(a, "int a, b", "a ==> 0\n" + 114 "b ==> 0"), 115 (a) -> assertCommand(a, "a < b", "$6 ==> false") 116 ); 117 } 118 119 @Test testChainedThrow()120 public void testChainedThrow() { 121 test( 122 (a) -> assertCommand(a, "void p() throws Exception { ((String) null).toString(); }", 123 "| created method p()"), 124 (a) -> assertCommand(a, "void n() throws Exception { try { p(); } catch (Exception ex) { throw new IOException(\"bar\", ex); }}", 125 "| created method n()"), 126 (a) -> assertCommand(a, "void m() { try { n(); } catch (Exception ex) { throw new RuntimeException(\"foo\", ex); }}", 127 "| created method m()"), 128 (a) -> assertCommand(a, "m()", 129 "| Exception java.lang.RuntimeException: foo\n" 130 + "| at m (#3:1)\n" 131 + "| at (#4:1)\n" 132 + "| Caused by: java.io.IOException: bar\n" 133 + "| at n (#2:1)\n" 134 + "| ...\n" 135 + "| Caused by: java.lang.NullPointerException: Cannot invoke \"String.toString()\" because \"null\" is null\n" 136 + "| at p (#1:1)\n" 137 + "| ..."), 138 (a) -> assertCommand(a, "/drop p", 139 "| dropped method p()"), 140 (a) -> assertCommand(a, "m()", 141 "| attempted to call method n() which cannot be invoked until method p() is declared") 142 ); 143 } 144 145 @Test testThrowWithPercent()146 public void testThrowWithPercent() { 147 test( 148 (a) -> assertCommandCheckOutput(a, 149 "URI u = new URI(\"http\", null, \"h\", -1, \"a\" + (char)0x04, null, null);", (s) -> 150 assertTrue(s.contains("URISyntaxException") && !s.contains("JShellTool"), 151 "Output: '" + s + "'")), 152 (a) -> assertCommandCheckOutput(a, 153 "throw new Exception(\"%z\")", (s) -> 154 assertTrue(s.contains("java.lang.Exception") && !s.contains("UnknownFormatConversionException"), 155 "Output: '" + s + "'")) 156 ); 157 } 158 159 @Test oneLineOfError()160 public void oneLineOfError() { 161 test( 162 (a) -> assertCommand(a, "12+", null), 163 (a) -> assertCommandCheckOutput(a, " true", (s) -> 164 assertTrue(s.contains("12+") && !s.contains("true"), "Output: '" + s + "'")) 165 ); 166 } 167 168 @Test defineVariables()169 public void defineVariables() { 170 test( 171 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 172 (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()), 173 (a) -> assertVariable(a, "int", "a"), 174 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 175 (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()), 176 (a) -> assertVariable(a, "double", "a", "1", "1.0"), 177 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 178 (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()), 179 (a) -> evaluateExpression(a, "double", "2 * a", "2.0"), 180 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 181 (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()) 182 ); 183 } 184 185 @Test defineMethods()186 public void defineMethods() { 187 test( 188 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 189 (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()), 190 (a) -> assertMethod(a, "int f() { return 0; }", "()int", "f"), 191 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 192 (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()), 193 (a) -> assertMethod(a, "void f(int a) { g(); }", "(int)void", "f"), 194 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 195 (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()), 196 (a) -> assertMethod(a, "void g() {}", "()void", "g"), 197 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 198 (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()) 199 ); 200 } 201 202 @Test defineTypes()203 public void defineTypes() { 204 test( 205 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 206 (a) -> assertCommandCheckOutput(a, "/types", assertClasses()), 207 (a) -> assertClass(a, "class A { }", "class", "A"), 208 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 209 (a) -> assertCommandCheckOutput(a, "/types", assertClasses()), 210 (a) -> assertClass(a, "interface A { }", "interface", "A"), 211 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 212 (a) -> assertCommandCheckOutput(a, "/types", assertClasses()), 213 (a) -> assertClass(a, "enum A { }", "enum", "A"), 214 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 215 (a) -> assertCommandCheckOutput(a, "/types", assertClasses()), 216 (a) -> assertClass(a, "@interface A { }", "@interface", "A"), 217 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 218 (a) -> assertCommandCheckOutput(a, "/types", assertClasses()) 219 ); 220 } 221 222 @Test defineImports()223 public void defineImports() { 224 test( 225 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 226 (a) -> assertCommandCheckOutput(a, "/imports", assertImports()), 227 (a) -> assertImport(a, "import java.util.stream.Stream;", "", "java.util.stream.Stream"), 228 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 229 (a) -> assertCommandCheckOutput(a, "/imports", assertImports()), 230 (a) -> assertImport(a, "import java.util.stream.*;", "", "java.util.stream.*"), 231 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 232 (a) -> assertCommandCheckOutput(a, "/imports", assertImports()), 233 (a) -> assertImport(a, "import static java.lang.Math.PI;", "static", "java.lang.Math.PI"), 234 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 235 (a) -> assertCommandCheckOutput(a, "/imports", assertImports()), 236 (a) -> assertImport(a, "import static java.lang.Math.*;", "static", "java.lang.Math.*"), 237 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 238 (a) -> assertCommandCheckOutput(a, "/imports", assertImports()) 239 ); 240 } 241 242 @Test defineVar()243 public void defineVar() { 244 test( 245 (a) -> assertCommand(a, "int x = 72", "x ==> 72"), 246 (a) -> assertCommand(a, "x", "x ==> 72"), 247 (a) -> assertCommand(a, "/vars", "| int x = 72") 248 ); 249 } 250 251 @Test defineUnresolvedVar()252 public void defineUnresolvedVar() { 253 test( 254 (a) -> assertCommand(a, "undefined x", 255 "| created variable x, however, it cannot be referenced until class undefined is declared"), 256 (a) -> assertCommand(a, "/vars", "| undefined x = (not-active)") 257 ); 258 } 259 260 @Test testUnresolved()261 public void testUnresolved() { 262 test( 263 (a) -> assertCommand(a, "int f() { return g() + x + new A().a; }", 264 "| created method f(), however, it cannot be invoked until method g(), variable x, and class A are declared"), 265 (a) -> assertCommand(a, "f()", 266 "| attempted to call method f() which cannot be invoked until method g(), variable x, and class A are declared"), 267 (a) -> assertCommandOutputStartsWith(a, "int g() { return x; }", 268 "| created method g(), however, it cannot be invoked until variable x is declared"), 269 (a) -> assertCommand(a, "g()", "| attempted to call method g() which cannot be invoked until variable x is declared") 270 ); 271 } 272 273 @Test testAbstractMethod()274 public void testAbstractMethod() { 275 test( 276 (a) -> assertCommand(a, "abstract int f(int x);", 277 "| created method f(int), however, it cannot be invoked until method f(int) is declared"), 278 (a) -> assertCommand(a, "f(13)", 279 "| attempted to call method f(int) which cannot be invoked until method f(int) is declared"), 280 (a) -> assertCommand(a, " abstract void m(Blah b);", 281 "| created method m(Blah), however, it cannot be referenced until class Blah, and method m(Blah) are declared") 282 ); 283 } 284 285 // 8199623 286 @Test testTwoForkedDrop()287 public void testTwoForkedDrop() { 288 test( 289 (a) -> assertCommand(a, "void p() throws Exception { ((String) null).toString(); }", 290 "| created method p()"), 291 (a) -> assertCommand(a, "void n() throws Exception { try { p(); } catch (Exception ex) { throw new IOException(\"bar\", ex); }} ", 292 "| created method n()"), 293 (a) -> assertCommand(a, "void m() { try { n(); } catch (Exception ex) { throw new RuntimeException(\"foo\", ex); }}", 294 "| created method m()"), 295 (a) -> assertCommand(a, "void c() throws Throwable { p(); }", 296 "| created method c()"), 297 (a) -> assertCommand(a, "/drop p", 298 "| dropped method p()"), 299 (a) -> assertCommand(a, "m()", 300 "| attempted to call method n() which cannot be invoked until method p() is declared"), 301 (a) -> assertCommand(a, "/meth n", 302 "| void n()\n" + 303 "| which cannot be invoked until method p() is declared"), 304 (a) -> assertCommand(a, "/meth m", 305 "| void m()"), 306 (a) -> assertCommand(a, "/meth c", 307 "| void c()\n" + 308 "| which cannot be invoked until method p() is declared") 309 ); 310 } 311 312 @Test testUnknownCommand()313 public void testUnknownCommand() { 314 test((a) -> assertCommand(a, "/unknown", 315 "| Invalid command: /unknown\n" + 316 "| Type /help for help.")); 317 } 318 319 @Test testEmptyClassPath()320 public void testEmptyClassPath() { 321 test(after -> assertCommand(after, "/env --class-path", "| Argument to class-path missing.")); 322 } 323 324 @Test testInvalidClassPath()325 public void testInvalidClassPath() { 326 test( 327 a -> assertCommand(a, "/env --class-path snurgefusal", 328 "| File 'snurgefusal' for '--class-path' is not found."), 329 a -> assertCommand(a, "/env --class-path ?", 330 "| File '?' for '--class-path' is not found.") 331 ); 332 } 333 334 @Test testNoArgument()335 public void testNoArgument() { 336 test( 337 (a) -> assertCommand(a, "/save", 338 "| '/save' requires a filename argument."), 339 (a) -> assertCommand(a, "/open", 340 "| '/open' requires a filename argument."), 341 (a) -> assertCommandOutputStartsWith(a, "/drop", 342 "| In the /drop argument, please specify an import, variable, method, or class to drop.") 343 ); 344 } 345 346 @Test testDebug()347 public void testDebug() { 348 test( 349 (a) -> assertCommand(a, "/deb", "| Debugging on"), 350 (a) -> assertCommand(a, "/debug", "| Debugging off"), 351 (a) -> assertCommand(a, "/debug", "| Debugging on"), 352 (a) -> assertCommand(a, "/deb", "| Debugging off") 353 ); 354 } 355 356 @Test testDrop()357 public void testDrop() { 358 test(false, new String[]{"--no-startup"}, 359 a -> assertVariable(a, "int", "a"), 360 a -> dropVariable(a, "/drop 1", "int a = 0", "| dropped variable a"), 361 a -> assertMethod(a, "int b() { return 0; }", "()int", "b"), 362 a -> dropMethod(a, "/drop 2", "int b()", "| dropped method b()"), 363 a -> assertClass(a, "class A {}", "class", "A"), 364 a -> dropClass(a, "/drop 3", "class A", "| dropped class A"), 365 a -> assertImport(a, "import java.util.stream.*;", "", "java.util.stream.*"), 366 a -> dropImport(a, "/drop 4", "import java.util.stream.*", ""), 367 a -> assertCommand(a, "for (int i = 0; i < 10; ++i) {}", ""), 368 a -> assertCommand(a, "/drop 5", ""), 369 a -> assertCommand(a, "/list", ""), 370 a -> assertCommandCheckOutput(a, "/vars", assertVariables()), 371 a -> assertCommandCheckOutput(a, "/methods", assertMethods()), 372 a -> assertCommandCheckOutput(a, "/types", assertClasses()), 373 a -> assertCommandCheckOutput(a, "/imports", assertImports()) 374 ); 375 test(false, new String[]{"--no-startup"}, 376 a -> assertVariable(a, "int", "a"), 377 a -> dropVariable(a, "/drop a", "int a = 0", "| dropped variable a"), 378 a -> assertMethod(a, "int b() { return 0; }", "()int", "b"), 379 a -> dropMethod(a, "/drop b", "int b()", "| dropped method b()"), 380 a -> assertClass(a, "class A {}", "class", "A"), 381 a -> dropClass(a, "/drop A", "class A", "| dropped class A"), 382 a -> assertCommandCheckOutput(a, "/vars", assertVariables()), 383 a -> assertCommandCheckOutput(a, "/methods", assertMethods()), 384 a -> assertCommandCheckOutput(a, "/types", assertClasses()), 385 a -> assertCommandCheckOutput(a, "/imports", assertImports()) 386 ); 387 } 388 389 @Test testDropRange()390 public void testDropRange() { 391 test(false, new String[]{"--no-startup"}, 392 a -> assertVariable(a, "int", "a"), 393 a -> assertMethod(a, "int b() { return 0; }", "()int", "b"), 394 a -> assertClass(a, "class A {}", "class", "A"), 395 a -> assertImport(a, "import java.util.stream.*;", "", "java.util.stream.*"), 396 a -> assertCommand(a, "for (int i = 0; i < 10; ++i) {}", ""), 397 a -> assertCommand(a, "/drop 3-5 b 1", 398 "| dropped class A\n" + 399 "| dropped method b()\n" + 400 "| dropped variable a\n"), 401 a -> assertCommand(a, "/list", "") 402 ); 403 } 404 405 @Test testDropNegative()406 public void testDropNegative() { 407 test(false, new String[]{"--no-startup"}, 408 a -> assertCommandOutputStartsWith(a, "/drop 0", "| No snippet with ID: 0"), 409 a -> assertCommandOutputStartsWith(a, "/drop a", "| No such snippet: a"), 410 a -> assertCommandCheckOutput(a, "/drop", 411 assertStartsWith("| In the /drop argument, please specify an import, variable, method, or class to drop.")), 412 a -> assertVariable(a, "int", "a"), 413 a -> assertCommand(a, "a", "a ==> 0"), 414 a -> assertCommand(a, "/drop 2", ""), 415 a -> assertCommand(a, "/drop 2", 416 "| This command does not accept the snippet '2' : a\n" + 417 "| See /types, /methods, /vars, or /list") 418 ); 419 } 420 421 @Test testAmbiguousDrop()422 public void testAmbiguousDrop() { 423 test( 424 a -> assertVariable(a, "int", "a"), 425 a -> assertMethod(a, "int a() { return 0; }", "()int", "a"), 426 a -> assertClass(a, "class a {}", "class", "a"), 427 a -> assertCommand(a, "/drop a", 428 "| dropped variable a\n" + 429 "| dropped method a()\n" + 430 "| dropped class a") 431 ); 432 test( 433 a -> assertMethod(a, "int a() { return 0; }", "()int", "a"), 434 a -> assertMethod(a, "double a(int a) { return 0; }", "(int)double", "a"), 435 a -> assertMethod(a, "double a(double a) { return 0; }", "(double)double", "a"), 436 a -> assertCommand(a, "/drop a", 437 "| dropped method a()\n" + 438 "| dropped method a(int)\n" + 439 "| dropped method a(double)\n") 440 ); 441 } 442 443 @Test testApplicationOfPost()444 public void testApplicationOfPost() { 445 test( 446 (a) -> assertCommand(a, "/set mode t normal -command", "| Created new feedback mode: t"), 447 (a) -> assertCommand(a, "/set feedback t", "| Feedback mode: t"), 448 (a) -> assertCommand(a, "/set format t post \"$%n\"", ""), 449 (a) -> assertCommand(a, "/set prompt t \"+\" \"-\"", ""), 450 (a) -> assertCommand(a, "/set prompt t", "| /set prompt t \"+\" \"-\"$") 451 ); 452 } 453 454 @Test testHelpLength()455 public void testHelpLength() { 456 Consumer<String> testOutput = (s) -> { 457 List<String> ss = Stream.of(s.split("\n")) 458 .filter(l -> !l.isEmpty()) 459 .collect(Collectors.toList()); 460 assertTrue(ss.size() >= 10, "Help does not print enough lines:" + s); 461 }; 462 test( 463 (a) -> assertCommandCheckOutput(a, "/?", testOutput), 464 (a) -> assertCommandCheckOutput(a, "/help", testOutput), 465 (a) -> assertCommandCheckOutput(a, "/help /list", testOutput) 466 ); 467 } 468 469 @Test testHelp()470 public void testHelp() { 471 test( 472 (a) -> assertHelp(a, "/?", "/list", "/help", "/exit", "intro"), 473 (a) -> assertHelp(a, "/help", "/list", "/help", "/exit", "intro"), 474 (a) -> assertHelp(a, "/help short", "shortcuts", "Tab"), 475 (a) -> assertHelp(a, "/help keys", "line", "Shift", "imports", "history"), 476 (a) -> assertHelp(a, "/? /li", "/list -all", "snippets"), 477 (a) -> assertHelp(a, "/help /set prompt", "optionally contain '%s'", "quoted"), 478 (a) -> assertHelp(a, "/help /help", "/help <command>"), 479 (a) -> assertHelp(a, "/help li", "/list -start"), 480 (a) -> assertHelp(a, "/help fe", "/set feedback -retain") 481 ); 482 } 483 484 @Test testHelpStart()485 public void testHelpStart() { 486 test( 487 (a) -> assertCommandCheckOutput(a, "/help /exit", 488 s -> assertTrue(s.replaceAll("\\r\\n?", "\n").startsWith( 489 "| \n" + 490 "| /exit\n" + 491 "| =====\n" + 492 "| " 493 )) 494 ) 495 ); 496 } 497 498 @Test testHelpFormat()499 public void testHelpFormat() { 500 test( 501 (a) -> assertCommandCheckOutput(a, "/help", s -> { 502 String[] lines = s.split("\\R"); 503 assertTrue(lines.length > 20, 504 "Too few lines of /help output: " + lines.length 505 + "\n" + s); 506 for (int i = 0; i < lines.length; ++i) { 507 String l = lines[i]; 508 assertTrue(l.startsWith("| "), 509 "Expected /help line to start with | :\n" + l); 510 assertTrue(l.length() <= 80, 511 "/help line too long: " + l.length() + "\n" + l); 512 } 513 }) 514 ); 515 } 516 517 @Test testConfusedUserPseudoCommands()518 public void testConfusedUserPseudoCommands() { 519 test( 520 (a) -> assertHelp(a, "/-<n>", "last snippet", "digits"), 521 (a) -> assertHelp(a, "/<id>", "last snippet", "digits") 522 ); 523 } 524 assertHelp(boolean a, String command, String... find)525 private void assertHelp(boolean a, String command, String... find) { 526 assertCommandCheckOutput(a, command, s -> { 527 for (String f : find) { 528 assertTrue(s.contains(f), 529 "Expected output of " + command + " to contain: " + f 530 + "\n" + s); 531 } 532 }); 533 } 534 535 // Check that each line of output contains the corresponding string from the list checkLineToList(String in, List<String> match)536 private void checkLineToList(String in, List<String> match) { 537 String trimmed = in.trim(); 538 String[] res = trimmed.isEmpty() 539 ? new String[0] 540 : trimmed.split("\n"); 541 assertEquals(res.length, match.size(), "Got: " + Arrays.asList(res)); 542 for (int i = 0; i < match.size(); ++i) { 543 assertTrue(res[i].contains(match.get(i))); 544 } 545 } 546 547 @Test testListArgs()548 public void testListArgs() { 549 String arg = "qqqq"; 550 List<String> startVarList = new ArrayList<>(START_UP); 551 startVarList.add("int aardvark"); 552 startVarList.add("int weevil"); 553 test( 554 a -> assertCommandCheckOutput(a, "/list -all", 555 s -> checkLineToList(s, START_UP)), 556 a -> assertCommandOutputStartsWith(a, "/list " + arg, 557 "| No such snippet: " + arg), 558 a -> assertVariable(a, "int", "aardvark"), 559 a -> assertVariable(a, "int", "weevil"), 560 a -> assertCommandOutputContains(a, "/list aardvark", "aardvark"), 561 a -> assertCommandCheckOutput(a, "/list -start", 562 s -> checkLineToList(s, START_UP)), 563 a -> assertCommandCheckOutput(a, "/list -all", 564 s -> checkLineToList(s, startVarList)), 565 a -> assertCommandOutputStartsWith(a, "/list s3", 566 "s3 : import"), 567 a -> assertCommandCheckOutput(a, "/list 1-2 s3", 568 s -> { 569 assertTrue(Pattern.matches(".*aardvark.*\\R.*weevil.*\\R.*s3.*import.*", s.trim()), 570 "No match: " + s); 571 }), 572 a -> assertCommandOutputStartsWith(a, "/list " + arg, 573 "| No such snippet: " + arg) 574 ); 575 } 576 577 @Test testVarsArgs()578 public void testVarsArgs() { 579 String arg = "qqqq"; 580 List<String> startVarList = new ArrayList<>(); 581 test( 582 a -> assertCommandCheckOutput(a, "/vars -all", 583 s -> checkLineToList(s, startVarList)), 584 a -> assertCommand(a, "/vars " + arg, 585 "| No such snippet: " + arg), 586 a -> assertVariable(a, "int", "aardvark"), 587 a -> assertMethod(a, "int f() { return 0; }", "()int", "f"), 588 a -> assertVariable(a, "int", "a"), 589 a -> assertVariable(a, "double", "a", "1", "1.0"), 590 a -> assertCommandOutputStartsWith(a, "/vars aardvark", 591 "| int aardvark = 0"), 592 a -> assertCommandCheckOutput(a, "/vars -start", 593 s -> checkLineToList(s, startVarList)), 594 a -> assertCommandOutputStartsWith(a, "/vars -all", 595 "| int aardvark = 0\n| int a = "), 596 a -> assertCommandOutputStartsWith(a, "/vars 1-4", 597 "| int aardvark = 0\n| int a = "), 598 a -> assertCommandOutputStartsWith(a, "/vars f", 599 "| This command does not accept the snippet 'f'"), 600 a -> assertCommand(a, "/var " + arg, 601 "| No such snippet: " + arg) 602 ); 603 } 604 605 @Test testMethodsArgs()606 public void testMethodsArgs() { 607 String arg = "qqqq"; 608 List<String> printingMethodList = new ArrayList<>(PRINTING_CMD_METHOD); 609 test(new String[]{"--startup", "PRINTING"}, 610 a -> assertCommandCheckOutput(a, "/methods -all", 611 s -> checkLineToList(s, printingMethodList)), 612 a -> assertCommandCheckOutput(a, "/methods -start", 613 s -> checkLineToList(s, printingMethodList)), 614 a -> assertCommandCheckOutput(a, "/methods print println printf", 615 s -> checkLineToList(s, printingMethodList)), 616 a -> assertCommandCheckOutput(a, "/methods println", 617 s -> assertEquals(s.trim().split("\n").length, 10)), 618 a -> assertCommandCheckOutput(a, "/methods", 619 s -> checkLineToList(s, printingMethodList)), 620 a -> assertCommandOutputStartsWith(a, "/methods " + arg, 621 "| No such snippet: " + arg), 622 a -> assertMethod(a, "int f() { return 0; }", "()int", "f"), 623 a -> assertVariable(a, "int", "aardvark"), 624 a -> assertMethod(a, "void f(int a) { g(); }", "(int)void", "f"), 625 a -> assertMethod(a, "void g() {}", "()void", "g"), 626 a -> assertCommandOutputStartsWith(a, "/methods " + arg, 627 "| No such snippet: " + arg), 628 a -> assertCommandOutputStartsWith(a, "/methods aardvark", 629 "| This command does not accept the snippet 'aardvark' : int aardvark"), 630 a -> assertCommandCheckOutput(a, "/methods -start", 631 s -> checkLineToList(s, printingMethodList)), 632 a -> assertCommandCheckOutput(a, "/methods print println printf", 633 s -> checkLineToList(s, printingMethodList)), 634 a -> assertCommandOutputStartsWith(a, "/methods g", 635 "| void g()"), 636 a -> assertCommandOutputStartsWith(a, "/methods f", 637 "| int f()\n" + 638 "| void f(int)") 639 ); 640 } 641 642 @Test testMethodsWithErrors()643 public void testMethodsWithErrors() { 644 test(new String[]{"--no-startup"}, 645 a -> assertCommand(a, "double m(int x) { return x; }", 646 "| created method m(int)"), 647 a -> assertCommand(a, "GARBAGE junk() { return TRASH; }", 648 "| created method junk(), however, it cannot be referenced until class GARBAGE, and variable TRASH are declared"), 649 a -> assertCommand(a, "int w = 5;", 650 "w ==> 5"), 651 a -> assertCommand(a, "int tyer() { return w; }", 652 "| created method tyer()"), 653 a -> assertCommand(a, "String w = \"hi\";", 654 "w ==> \"hi\""), 655 a -> assertCommand(a, "/methods", 656 "| double m(int)\n" + 657 "| GARBAGE junk()\n" + 658 "| which cannot be referenced until class GARBAGE, and variable TRASH are declared\n" + 659 "| int tyer()\n" + 660 "| which cannot be invoked until this error is corrected: \n" + 661 "| incompatible types: java.lang.String cannot be converted to int\n" + 662 "| int tyer() { return w; }\n" + 663 "| ^\n") 664 ); 665 } 666 667 @Test testTypesWithErrors()668 public void testTypesWithErrors() { 669 test(new String[]{"--no-startup"}, 670 a -> assertCommand(a, "class C extends NONE { int x; }", 671 "| created class C, however, it cannot be referenced until class NONE is declared"), 672 a -> assertCommand(a, "class D { void m() { System.out.println(nada); } }", 673 "| created class D, however, it cannot be instantiated or its methods invoked until variable nada is declared"), 674 a -> assertCommand(a, "/types", 675 "| class C\n" + 676 "| which cannot be referenced until class NONE is declared\n" + 677 "| class D\n" + 678 "| which cannot be instantiated or its methods invoked until variable nada is declared\n") 679 ); 680 } 681 682 @Test testTypesArgs()683 public void testTypesArgs() { 684 String arg = "qqqq"; 685 List<String> startTypeList = new ArrayList<>(); 686 test( 687 a -> assertCommandCheckOutput(a, "/types -all", 688 s -> checkLineToList(s, startTypeList)), 689 a -> assertCommandCheckOutput(a, "/types -start", 690 s -> checkLineToList(s, startTypeList)), 691 a -> assertCommandOutputStartsWith(a, "/types " + arg, 692 "| No such snippet: " + arg), 693 a -> assertVariable(a, "int", "aardvark"), 694 (a) -> assertClass(a, "class A { }", "class", "A"), 695 (a) -> assertClass(a, "interface A { }", "interface", "A"), 696 a -> assertCommandOutputStartsWith(a, "/types -all", 697 "| class A\n" + 698 "| interface A"), 699 (a) -> assertClass(a, "enum E { }", "enum", "E"), 700 (a) -> assertClass(a, "@interface B { }", "@interface", "B"), 701 a -> assertCommand(a, "/types aardvark", 702 "| This command does not accept the snippet 'aardvark' : int aardvark;"), 703 a -> assertCommandOutputStartsWith(a, "/types A", 704 "| interface A"), 705 a -> assertCommandOutputStartsWith(a, "/types E", 706 "| enum E"), 707 a -> assertCommandOutputStartsWith(a, "/types B", 708 "| @interface B"), 709 a -> assertCommandOutputStartsWith(a, "/types " + arg, 710 "| No such snippet: " + arg), 711 a -> assertCommandCheckOutput(a, "/types -start", 712 s -> checkLineToList(s, startTypeList)) 713 ); 714 } 715 716 @Test testBlankLinesInSnippetContinuation()717 public void testBlankLinesInSnippetContinuation() { 718 test(Locale.ROOT, false, new String[]{"--no-startup"}, "", 719 a -> assertCommand(a, "class C {", 720 ""), 721 a -> assertCommand(a, "", 722 ""), 723 a -> assertCommand(a, "", 724 ""), 725 a -> assertCommand(a, " int x;", 726 ""), 727 a -> assertCommand(a, "", 728 ""), 729 a -> assertCommand(a, "", 730 ""), 731 a -> assertCommand(a, "}", 732 "| created class C"), 733 a -> assertCommand(a, "/list", 734 "\n" + 735 " 1 : class C {\n" + 736 " \n" + 737 " \n" + 738 " int x;\n" + 739 " \n" + 740 " \n" + 741 " }") 742 ); 743 } 744 745 @Test testCompoundStart()746 public void testCompoundStart() { 747 test(new String[]{"--startup", "DEFAULT", "--startup", "PRINTING"}, 748 (a) -> assertCommand(a, "printf(\"%4.2f\", Math.PI)", 749 "", "", null, "3.14", "") 750 ); 751 } 752 753 @Test testJavaSeStart()754 public void testJavaSeStart() { 755 test(new String[]{"--startup", "JAVASE"}, 756 (a) -> assertCommand(a, "ZoneOffsetTransitionRule.TimeDefinition.WALL", 757 "$1 ==> WALL") 758 ); 759 } 760 761 @Test testJavaSeSetStart()762 public void testJavaSeSetStart() { 763 test( 764 (a) -> assertCommand(a, "/set sta JAVASE", ""), 765 (a) -> assertCommand(a, "/reset", "| Resetting state."), 766 (a) -> assertCommandCheckOutput(a, "/li -a", 767 s -> assertTrue(s.split("import ").length > 160, 768 "not enough imports for JAVASE:\n" + s)) 769 ); 770 } 771 772 @Test defineClasses()773 public void defineClasses() { 774 test( 775 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 776 (a) -> assertCommandCheckOutput(a, "/types", assertClasses()), 777 (a) -> assertClass(a, "class A { }", "class", "A"), 778 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 779 (a) -> assertCommandCheckOutput(a, "/types", assertClasses()), 780 (a) -> assertClass(a, "interface A { }", "interface", "A"), 781 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 782 (a) -> assertCommandCheckOutput(a, "/types", assertClasses()), 783 (a) -> assertClass(a, "enum A { }", "enum", "A"), 784 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 785 (a) -> assertCommandCheckOutput(a, "/types", assertClasses()), 786 (a) -> assertClass(a, "@interface A { }", "@interface", "A"), 787 (a) -> assertCommandCheckOutput(a, "/list", assertList()), 788 (a) -> assertCommandCheckOutput(a, "/types", assertClasses()) 789 ); 790 } 791 792 @Test testCommandPrefix()793 public void testCommandPrefix() { 794 test(a -> assertCommandCheckOutput(a, "/s", 795 assertStartsWith("| Command: '/s' is ambiguous: /save, /set")), 796 a -> assertCommand(a, "int var", "var ==> 0"), 797 a -> assertCommandCheckOutput(a, "/va", 798 assertStartsWith("| int var = 0")), 799 a -> assertCommandCheckOutput(a, "/save", 800 assertStartsWith("| '/save' requires a filename argument."))); 801 } 802 803 @Test testOptionQ()804 public void testOptionQ() { 805 test(Locale.ROOT, false, new String[]{"-q", "--no-startup"}, "", 806 (a) -> assertCommand(a, "1+1", "$1 ==> 2"), 807 (a) -> assertCommand(a, "int x = 5", "") 808 ); 809 } 810 811 @Test testOptionS()812 public void testOptionS() { 813 test(Locale.ROOT, false, new String[]{"-s", "--no-startup"}, "", 814 (a) -> assertCommand(a, "1+1", "") 815 ); 816 } 817 818 @Test testOptionV()819 public void testOptionV() { 820 test(new String[]{"-v", "--no-startup"}, 821 (a) -> assertCommand(a, "1+1", 822 "$1 ==> 2\n" + 823 "| created scratch variable $1 : int") 824 ); 825 } 826 827 @Test testOptionFeedback()828 public void testOptionFeedback() { 829 test(Locale.ROOT, false, new String[]{"--feedback", "concise", "--no-startup"}, "", 830 (a) -> assertCommand(a, "1+1", "$1 ==> 2"), 831 (a) -> assertCommand(a, "int x = 5", "") 832 ); 833 } 834 835 @Test testCompoundOptions()836 public void testCompoundOptions() { 837 Consumer<String> confirmNoStartup = s -> { 838 assertEquals(0, Stream.of(s.split("\n")) 839 .filter(l -> !l.isEmpty()) 840 .count(), "Expected no lines: " + s); 841 }; 842 test(Locale.ROOT, false, new String[]{"-nq"}, "", 843 (a) -> assertCommandCheckOutput(a, "/list -all", confirmNoStartup), 844 (a) -> assertCommand(a, "1+1", "$1 ==> 2"), 845 (a) -> assertCommand(a, "int x = 5", "") 846 ); 847 test(Locale.ROOT, false, new String[]{"-qn"}, "", 848 (a) -> assertCommandCheckOutput(a, "/list -all", confirmNoStartup), 849 (a) -> assertCommand(a, "1+1", "$1 ==> 2"), 850 (a) -> assertCommand(a, "int x = 5", "") 851 ); 852 test(Locale.ROOT, false, new String[]{"-ns"}, "", 853 (a) -> assertCommandCheckOutput(a, "/list -all", confirmNoStartup), 854 (a) -> assertCommand(a, "1+1", "") 855 ); 856 } 857 858 @Test testOptionR()859 public void testOptionR() { 860 test(new String[]{"-R-Dthe.sound=blorp", "--no-startup"}, 861 (a) -> assertCommand(a, "System.getProperty(\"the.sound\")", 862 "$1 ==> \"blorp\"") 863 ); 864 } 865 866 @Test testWrapSourceHandlerDiagCrash()867 public void testWrapSourceHandlerDiagCrash() { 868 test(new String[]{"--add-exports", "jdk.javadoc/ALL-UNNAMED"}, 869 (a) -> assertCommand(a, "1+1", "$1 ==> 2") 870 ); 871 } 872 873 @Test test8156910()874 public void test8156910() { 875 test( 876 (a) -> assertCommandOutputContains(a, "System.out.println(\"%5d\", 10);", "%5d"), 877 (a) -> assertCommandOutputContains(a, "1234", "==> 1234") 878 ); 879 } 880 881 @Test testIntersection()882 public void testIntersection() { 883 test( 884 (a) -> assertCommandOutputContains(a, "<Z extends Runnable&CharSequence> Z get1() { return null; }", "get1()"), 885 (a) -> assertCommandOutputContains(a, "var g1 = get1()", "g1"), 886 (a) -> assertCommand(a, "/vars g1", "| CharSequence&Runnable g1 = null"), 887 (a) -> assertCommandOutputContains(a, "<Z extends Number&CharSequence> Z get2() { return null; }", "get2()"), 888 (a) -> assertCommandOutputContains(a, "var g2 = get2()", "g2"), 889 (a) -> assertCommand(a, "/vars g2", "| Number&CharSequence g2 = null") 890 ); 891 } 892 893 @Test testAnonymous()894 public void testAnonymous() { 895 test( 896 (a) -> assertCommandOutputContains(a, "var r1 = new Object() {}", "r1"), 897 (a) -> assertCommandOutputContains(a, "/vars r1", "| <anonymous class extending Object> r1 = "), 898 (a) -> assertCommandOutputContains(a, "var r2 = new Runnable() { public void run() { } }", "r2"), 899 (a) -> assertCommandOutputContains(a, "/vars r2", "| <anonymous class implementing Runnable> r2 = "), 900 (a) -> assertCommandOutputContains(a, "import java.util.stream.*;", ""), 901 (a) -> assertCommandOutputContains(a, "var list = Stream.of(1, 2, 3).map(j -> new Object() { int i = j; }).collect(Collectors.toList());", 902 "list"), 903 (a) -> assertCommandOutputContains(a, "/vars list", "| List<<anonymous class extending Object>> list = ") 904 ); 905 } 906 907 // This is mainly interesting in the TestLocalSimpleTest case (8198573) 908 @Test testUpdateFalsePositive()909 public void testUpdateFalsePositive() { 910 test( 911 a -> assertClass(a, "class A { int a() { int error = 0; return error; } }", "class", "A"), 912 a -> assertVariable(a, "A", "a", "new A()", "A@.+"), 913 a -> assertVariable(a, "int", "error", "4711", "4711"), 914 a -> assertCommandOutputContains(a, "a", "A@") 915 ); 916 } 917 918 @Test testRecords()919 public void testRecords() { 920 test(new String[] {}, 921 (a) -> assertCommandOutputContains(a, "record R(int i) { public int g() { return j; } }", 922 "| created record R, however, it cannot be instantiated or its methods invoked until variable j is declared"), 923 (a) -> assertCommandOutputContains(a, "new R(0)", 924 "| attempted to use record R which cannot be instantiated or its methods invoked until variable j is declared") 925 ); 926 } 927 928 @Test testImportChange()929 public void testImportChange() { 930 for (String feedback : new String[] {"verbose", "normal"}) { 931 test( 932 (a) -> assertCommandOutputContains(a, "/set feedback " + feedback, "| Feedback mode: " + feedback), 933 (a) -> assertCommand(a, "import java.util.*", ""), 934 (a) -> assertCommandOutputContains(a, "var v1 = List.of(1);", "v1 ==> [1]"), 935 (a) -> assertCommandOutputContains(a, "import java.awt.List;", 936 "| update replaced variable v1 which cannot be referenced until this error is corrected:"), 937 (a) -> assertCommandOutputContains(a, "var b = java.util.List.of(\"bb\")", 938 "b ==> [bb]"), 939 (a) -> assertCommandOutputContains(a, "b", "b ==> [bb]") 940 ); 941 } 942 } 943 944 @Test testSwitchStatementExpressionDisambiguation()945 public void testSwitchStatementExpressionDisambiguation() { 946 test(false, new String[]{"--no-startup"}, 947 (a) -> assertCommand(a, "switch (0) { default -> 0; }", "$1 ==> 0"), 948 (a) -> assertCommand(a, "int i;", "i ==> 0"), 949 (a) -> assertCommand(a, "switch (0) { case 0 -> i = 1; }", ""), 950 (a) -> assertCommand(a, "i", "i ==> 1"), 951 (a) -> assertCommandOutputStartsWith(a, "switch (0) { default -> throw new IllegalStateException(); }", "| Exception java.lang.IllegalStateException") 952 ); 953 test(false, new String[]{"--no-startup", "-C-source", "-C8"}, 954 (a) -> assertCommand(a, "int i;", "i ==> 0"), 955 (a) -> assertCommand(a, "switch (0) { default: i = 1; }", ""), 956 (a) -> assertCommand(a, "i", "i ==> 1") 957 ); 958 } 959 } 960