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