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 import java.io.ByteArrayOutputStream; 25 import java.io.IOException; 26 import java.io.OutputStream; 27 import java.io.PrintStream; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.List; 33 import java.util.Locale; 34 import java.util.Map; 35 import java.util.function.Consumer; 36 import java.util.function.Function; 37 import java.util.function.Predicate; 38 import java.util.logging.Level; 39 import java.util.logging.Logger; 40 import java.util.prefs.AbstractPreferences; 41 import java.util.prefs.BackingStoreException; 42 import java.util.regex.Matcher; 43 import java.util.regex.Pattern; 44 import java.util.stream.Collectors; 45 import java.util.stream.Stream; 46 47 48 import org.testng.annotations.BeforeMethod; 49 50 import jdk.jshell.tool.JavaShellToolBuilder; 51 import static java.util.stream.Collectors.toList; 52 import static org.testng.Assert.assertEquals; 53 import static org.testng.Assert.assertNotNull; 54 import static org.testng.Assert.assertTrue; 55 import static org.testng.Assert.fail; 56 57 public class ReplToolTesting { 58 59 private final static String DEFAULT_STARTUP_MESSAGE = "| Welcome to"; 60 final static List<ImportInfo> START_UP_IMPORTS = Stream.of( 61 "java.io.*", 62 "java.math.*", 63 "java.net.*", 64 "java.nio.file.*", 65 "java.util.*", 66 "java.util.concurrent.*", 67 "java.util.function.*", 68 "java.util.prefs.*", 69 "java.util.regex.*", 70 "java.util.stream.*") 71 .map(s -> new ImportInfo("import " + s + ";", "", s)) 72 .collect(toList()); 73 final static List<MethodInfo> START_UP_METHODS = Stream.<MethodInfo>of() 74 .collect(toList()); 75 final static List<String> START_UP_CMD_METHOD = Stream.<String>of() 76 .collect(toList()); 77 final static List<String> PRINTING_CMD_METHOD = Stream.of( 78 "| void print(boolean)", 79 "| void print(char)", 80 "| void print(int)", 81 "| void print(long)", 82 "| void print(float)", 83 "| void print(double)", 84 "| void print(char s[])", 85 "| void print(String)", 86 "| void print(Object)", 87 "| void println()", 88 "| void println(boolean)", 89 "| void println(char)", 90 "| void println(int)", 91 "| void println(long)", 92 "| void println(float)", 93 "| void println(double)", 94 "| void println(char s[])", 95 "| void println(String)", 96 "| void println(Object)", 97 "| void printf(java.util.Locale,String,Object...)", 98 "| void printf(String,Object...)") 99 .collect(toList()); 100 final static List<String> START_UP = Collections.unmodifiableList( 101 Stream.concat(START_UP_IMPORTS.stream(), START_UP_METHODS.stream()) 102 .map(s -> s.getSource()) 103 .collect(toList())); 104 105 private WaitingTestingInputStream cmdin = null; 106 private ByteArrayOutputStream cmdout = null; 107 private ByteArrayOutputStream cmderr = null; 108 private PromptedCommandOutputStream console = null; 109 private TestingInputStream userin = null; 110 private ByteArrayOutputStream userout = null; 111 private ByteArrayOutputStream usererr = null; 112 113 private List<MemberInfo> keys; 114 private Map<String, VariableInfo> variables; 115 private Map<String, MethodInfo> methods; 116 private Map<String, ClassInfo> classes; 117 private Map<String, ImportInfo> imports; 118 private boolean isDefaultStartUp = true; 119 protected Map<String, String> prefsMap; 120 private Map<String, String> envvars; 121 122 public interface ReplTest { run(boolean after)123 void run(boolean after); 124 } 125 setCommandInput(String s)126 public void setCommandInput(String s) { 127 cmdin.setInput(s); 128 } 129 closeCommandInput()130 public void closeCommandInput() { 131 try { 132 cmdin.close(); 133 } catch (IOException ex) { 134 throw new IllegalStateException(ex); 135 } 136 } 137 138 public final static Pattern idPattern = Pattern.compile("^\\s+(\\d+)"); assertList()139 public Consumer<String> assertList() { 140 return s -> { 141 List<String> lines = Stream.of(s.split("\n")) 142 .filter(l -> !l.isEmpty()) 143 .collect(Collectors.toList()); 144 int previousId = Integer.MIN_VALUE; 145 assertEquals(lines.size(), keys.size(), "Number of keys"); 146 for (int i = 0; i < lines.size(); ++i) { 147 String line = lines.get(i); 148 Matcher matcher = idPattern.matcher(line); 149 assertTrue(matcher.find(), "Snippet id not found: " + line); 150 String src = keys.get(i).getSource(); 151 assertTrue(line.endsWith(src), "Line '" + line + "' does not end with: " + src); 152 int id = Integer.parseInt(matcher.group(1)); 153 assertTrue(previousId < id, 154 String.format("The previous id is not less than the next one: previous: %d, next: %d", 155 previousId, id)); 156 previousId = id; 157 } 158 }; 159 } 160 161 private final static Pattern extractPattern = Pattern.compile("^\\| *(.*)$"); 162 private Consumer<String> assertMembers(String message, Map<String, ? extends MemberInfo> set) { 163 return s -> { 164 List<String> lines = Stream.of(s.split("\n")) 165 .filter(l -> !l.isEmpty()) 166 .filter(l -> !l.startsWith("| ")) // error/unresolved info 167 .collect(Collectors.toList()); 168 assertEquals(lines.size(), set.size(), message + " : expected: " + set.keySet() + "\ngot:\n" + lines); 169 for (String line : lines) { 170 Matcher matcher = extractPattern.matcher(line); 171 assertTrue(matcher.find(), line); 172 String src = matcher.group(1); 173 MemberInfo info = set.get(src); 174 assertNotNull(info, "Not found snippet with signature: " + src + ", line: " 175 + line + ", keys: " + set.keySet() + "\n"); 176 } 177 }; 178 } 179 180 public Consumer<String> assertVariables() { 181 return assertMembers("Variables", variables); 182 } 183 184 public Consumer<String> assertMethods() { 185 return assertMembers("Methods", methods); 186 } 187 188 public Consumer<String> assertClasses() { 189 return assertMembers("Classes", classes); 190 } 191 192 public Consumer<String> assertImports() { 193 return assertMembers("Imports", imports); 194 } 195 196 public String getCommandOutput() { 197 String s = normalizeLineEndings(cmdout.toString()); 198 cmdout.reset(); 199 return s; 200 } 201 202 public String getCommandErrorOutput() { 203 String s = normalizeLineEndings(cmderr.toString()); 204 cmderr.reset(); 205 return s; 206 } 207 208 public void setUserInput(String s) { 209 userin.setInput(s); 210 } 211 212 public String getUserOutput() { 213 String s = normalizeLineEndings(userout.toString()); 214 userout.reset(); 215 return s; 216 } 217 218 public String getUserErrorOutput() { 219 String s = normalizeLineEndings(usererr.toString()); 220 usererr.reset(); 221 return s; 222 } 223 224 public void test(ReplTest... tests) { 225 test(new String[0], tests); 226 } 227 228 public void test(String[] args, ReplTest... tests) { 229 test(true, args, tests); 230 } 231 232 public void test(boolean isDefaultStartUp, String[] args, ReplTest... tests) { 233 test(Locale.ROOT, isDefaultStartUp, args, DEFAULT_STARTUP_MESSAGE, tests); 234 } 235 236 public void testNoStartUp(ReplTest... tests) { 237 test(Locale.ROOT, false, new String[] {"--no-startup"}, DEFAULT_STARTUP_MESSAGE, tests); 238 } 239 240 public void test(Locale locale, boolean isDefaultStartUp, String[] args, String startUpMessage, ReplTest... tests) { 241 this.isDefaultStartUp = isDefaultStartUp; 242 initSnippets(); 243 ReplTest[] wtests = new ReplTest[tests.length + 3]; 244 wtests[0] = a -> assertCommandCheckOutput(a, "<start>", 245 s -> assertTrue(s.startsWith(startUpMessage), "Expected start-up message '" + startUpMessage + "' Got: " + s)); 246 wtests[1] = a -> assertCommand(a, "/debug 0", null); 247 System.arraycopy(tests, 0, wtests, 2, tests.length); 248 wtests[tests.length + 2] = a -> assertCommand(a, "/exit", null); 249 testRaw(locale, args, wtests); 250 } 251 252 private void initSnippets() { 253 keys = new ArrayList<>(); 254 variables = new HashMap<>(); 255 methods = new HashMap<>(); 256 classes = new HashMap<>(); 257 imports = new HashMap<>(); 258 if (isDefaultStartUp) { 259 methods.putAll( 260 START_UP_METHODS.stream() 261 .collect(Collectors.toMap(Object::toString, Function.identity()))); 262 imports.putAll( 263 START_UP_IMPORTS.stream() 264 .collect(Collectors.toMap(Object::toString, Function.identity()))); 265 } 266 } 267 268 @BeforeMethod 269 public void setUp() { 270 prefsMap = new HashMap<>(); 271 prefsMap.put("INDENT", "0"); 272 envvars = new HashMap<>(); 273 System.setProperty("jshell.test.allow.incomplete.inputs", "true"); 274 } 275 276 protected void setEnvVar(String name, String value) { 277 envvars.put(name, value); 278 } 279 280 protected JavaShellToolBuilder builder(Locale locale) { 281 // turn on logging of launch failures 282 Logger.getLogger("jdk.jshell.execution").setLevel(Level.ALL); 283 return JavaShellToolBuilder 284 .builder() 285 .in(cmdin, userin) 286 .out(new PrintStream(cmdout), new PrintStream(console), new PrintStream(userout)) 287 .err(new PrintStream(cmderr), new PrintStream(usererr)) 288 .persistence(prefsMap) 289 .env(envvars) 290 .locale(locale) 291 .promptCapture(true); 292 } 293 294 private void testRaw(Locale locale, String[] args, ReplTest... tests) { 295 testRawInit(tests); 296 testRawRun(locale, args); 297 testRawCheck(locale); 298 } 299 300 private void testRawInit(ReplTest... tests) { 301 cmdin = new WaitingTestingInputStream(); 302 cmdout = new ByteArrayOutputStream(); 303 cmderr = new ByteArrayOutputStream(); 304 console = new PromptedCommandOutputStream(tests); 305 userin = new TestingInputStream(); 306 userout = new ByteArrayOutputStream(); 307 usererr = new ByteArrayOutputStream(); 308 } 309 310 protected void testRawRun(Locale locale, String[] args) { 311 try { 312 builder(locale) 313 .run(args); 314 } catch (Exception ex) { 315 fail("Repl tool died with exception", ex); 316 } 317 } 318 319 private void testRawCheck(Locale locale) { 320 // perform internal consistency checks on state, if desired 321 String cos = getCommandOutput(); 322 String ceos = getCommandErrorOutput(); 323 String uos = getUserOutput(); 324 String ueos = getUserErrorOutput(); 325 assertTrue((cos.isEmpty() || cos.startsWith("| Goodbye") || !locale.equals(Locale.ROOT)), 326 "Expected a goodbye, but got: " + cos); 327 assertTrue(ceos.isEmpty(), "Expected empty command error output, got: " + ceos); 328 assertTrue(uos.isEmpty(), "Expected empty user output, got: " + uos); 329 assertTrue(ueos.isEmpty(), "Expected empty user error output, got: " + ueos); 330 } 331 332 public void assertReset(boolean after, String cmd) { 333 assertCommand(after, cmd, "| Resetting state.\n"); 334 initSnippets(); 335 } 336 337 public void evaluateExpression(boolean after, String type, String expr, String value) { 338 String output = String.format("(\\$\\d+) ==> %s", value); 339 Pattern outputPattern = Pattern.compile(output); 340 assertCommandCheckOutput(after, expr, s -> { 341 Matcher matcher = outputPattern.matcher(s); 342 assertTrue(matcher.find(), "Output: '" + s + "' does not fit pattern: '" + output + "'"); 343 String name = matcher.group(1); 344 VariableInfo tempVar = new TempVariableInfo(expr, type, name, value); 345 variables.put(tempVar.toString(), tempVar); 346 addKey(after, tempVar); 347 }); 348 } 349 350 public void loadVariable(boolean after, String type, String name) { 351 loadVariable(after, type, name, null, null); 352 } 353 354 public void loadVariable(boolean after, String type, String name, String expr, String value) { 355 String src = expr == null 356 ? String.format("%s %s", type, name) 357 : String.format("%s %s = %s", type, name, expr); 358 VariableInfo var = expr == null 359 ? new VariableInfo(src, type, name) 360 : new VariableInfo(src, type, name, value); 361 addKey(after, var, variables); 362 addKey(after, var); 363 } 364 365 public void assertVariable(boolean after, String type, String name) { 366 assertVariable(after, type, name, null, null); 367 } 368 369 public void assertVariable(boolean after, String type, String name, String expr, String value) { 370 String src = expr == null 371 ? String.format("%s %s", type, name) 372 : String.format("%s %s = %s", type, name, expr); 373 VariableInfo var = expr == null 374 ? new VariableInfo(src, type, name) 375 : new VariableInfo(src, type, name, value); 376 assertCommandCheckOutput(after, src, var.checkOutput()); 377 addKey(after, var, variables); 378 addKey(after, var); 379 } 380 381 public void loadMethod(boolean after, String src, String signature, String name) { 382 MethodInfo method = new MethodInfo(src, signature, name); 383 addKey(after, method, methods); 384 addKey(after, method); 385 } 386 387 public void assertMethod(boolean after, String src, String signature, String name) { 388 MethodInfo method = new MethodInfo(src, signature, name); 389 assertCommandCheckOutput(after, src, method.checkOutput()); 390 addKey(after, method, methods); 391 addKey(after, method); 392 } 393 394 public void loadClass(boolean after, String src, String type, String name) { 395 ClassInfo clazz = new ClassInfo(src, type, name); 396 addKey(after, clazz, classes); 397 addKey(after, clazz); 398 } 399 400 public void assertClass(boolean after, String src, String type, String name) { 401 ClassInfo clazz = new ClassInfo(src, type, name); 402 assertCommandCheckOutput(after, src, clazz.checkOutput()); 403 addKey(after, clazz, classes); 404 addKey(after, clazz); 405 } 406 407 public void loadImport(boolean after, String src, String type, String name) { 408 ImportInfo i = new ImportInfo(src, type, name); 409 addKey(after, i, imports); 410 addKey(after, i); 411 } 412 413 public void assertImport(boolean after, String src, String type, String name) { 414 ImportInfo i = new ImportInfo(src, type, name); 415 assertCommandCheckOutput(after, src, i.checkOutput()); 416 addKey(after, i, imports); 417 addKey(after, i); 418 } 419 420 private <T extends MemberInfo> void addKey(boolean after, T memberInfo, Map<String, T> map) { 421 if (after) { 422 map.entrySet().removeIf(e -> e.getValue().equals(memberInfo)); 423 map.put(memberInfo.toString(), memberInfo); 424 } 425 } 426 427 private <T extends MemberInfo> void addKey(boolean after, T memberInfo) { 428 if (after) { 429 for (int i = 0; i < keys.size(); ++i) { 430 MemberInfo m = keys.get(i); 431 if (m.equals(memberInfo)) { 432 keys.set(i, memberInfo); 433 return; 434 } 435 } 436 keys.add(memberInfo); 437 } 438 } 439 440 private void dropKey(boolean after, String cmd, String name, Map<String, ? extends MemberInfo> map, String output) { 441 assertCommand(after, cmd, output); 442 if (after) { 443 map.remove(name); 444 for (int i = 0; i < keys.size(); ++i) { 445 MemberInfo m = keys.get(i); 446 if (m.toString().equals(name)) { 447 keys.remove(i); 448 return; 449 } 450 } 451 throw new AssertionError("Key not found: " + name + ", keys: " + keys); 452 } 453 } 454 455 public void dropVariable(boolean after, String cmd, String name, String output) { 456 dropKey(after, cmd, name, variables, output); 457 } 458 459 public void dropMethod(boolean after, String cmd, String name, String output) { 460 dropKey(after, cmd, name, methods, output); 461 } 462 463 public void dropClass(boolean after, String cmd, String name, String output) { 464 dropKey(after, cmd, name, classes, output); 465 } 466 467 public void dropImport(boolean after, String cmd, String name, String output) { 468 dropKey(after, cmd, name, imports, output); 469 } 470 471 public void assertCommand(boolean after, String cmd, String out) { 472 assertCommand(after, cmd, out, "", null, "", ""); 473 } 474 475 public void assertCommandOutputContains(boolean after, String cmd, String... hasThese) { 476 assertCommandCheckOutput(after, cmd, (s) 477 -> assertTrue(Arrays.stream(hasThese) 478 .allMatch(has -> s.contains(has)), 479 "Output: \'" + s + "' does not contain: " 480 + Arrays.stream(hasThese) 481 .filter(has -> !s.contains(has)) 482 .collect(Collectors.joining(", ")))); 483 } 484 485 public void assertCommandOutputStartsWith(boolean after, String cmd, String starts) { 486 assertCommandCheckOutput(after, cmd, assertStartsWith(starts)); 487 } 488 489 public void assertCommandCheckOutput(boolean after, String cmd, Consumer<String> check) { 490 if (!after) { 491 assertCommand(false, cmd, null); 492 } else { 493 String got = getCommandOutput(); 494 check.accept(got); 495 assertCommand(true, cmd, null); 496 } 497 } 498 499 public void assertCommand(boolean after, String cmd, String out, String err, 500 String userinput, String print, String usererr) { 501 if (!after) { 502 if (userinput != null) { 503 setUserInput(userinput); 504 } 505 if (cmd.endsWith("\u0003")) { 506 setCommandInput(cmd); 507 } else { 508 setCommandInput(cmd + "\n"); 509 } 510 } else { 511 assertOutput(getCommandOutput().trim(), out==null? out : out.trim(), "command output: " + cmd); 512 assertOutput(getCommandErrorOutput(), err, "command error: " + cmd); 513 assertOutput(getUserOutput(), print, "user output: " + cmd); 514 assertOutput(getUserErrorOutput(), usererr, "user error: " + cmd); 515 } 516 } 517 518 public Consumer<String> assertStartsWith(String prefix) { 519 return (output) -> { 520 if (!output.trim().startsWith(prefix)) { 521 int i = 0; 522 } 523 assertTrue(output.trim().startsWith(prefix), "Output: \'" + output + "' does not start with: " + prefix); 524 }; 525 } 526 527 public void assertOutput(String got, String expected, String display) { 528 if (expected != null) { 529 assertEquals(got, expected, display + ".\n"); 530 } 531 } 532 533 private String normalizeLineEndings(String text) { 534 return ANSI_CODE_PATTERN.matcher(text.replace(System.getProperty("line.separator"), "\n")).replaceAll(""); 535 } 536 private static final Pattern ANSI_CODE_PATTERN = Pattern.compile("\033\\[[\060-\077]*[\040-\057]*[\100-\176]"); 537 538 public static abstract class MemberInfo { 539 public final String source; 540 public final String type; 541 public final String name; 542 543 public MemberInfo(String source, String type, String name) { 544 this.source = source; 545 this.type = type; 546 this.name = name; 547 } 548 549 @Override 550 public int hashCode() { 551 return name.hashCode(); 552 } 553 554 @Override 555 public boolean equals(Object o) { 556 if (o instanceof MemberInfo) { 557 MemberInfo mi = (MemberInfo) o; 558 return name.equals(mi.name); 559 } 560 return false; 561 } 562 563 public abstract Consumer<String> checkOutput(); 564 565 public String getSource() { 566 return source; 567 } 568 } 569 570 public static class VariableInfo extends MemberInfo { 571 572 public final String value; 573 public final String initialValue; 574 575 public VariableInfo(String src, String type, String name) { 576 super(src, type, name); 577 this.initialValue = null; 578 switch (type) { 579 case "byte": 580 case "short": 581 case "int": 582 case "long": 583 value = "0"; 584 break; 585 case "boolean": 586 value = "false"; 587 break; 588 case "char": 589 value = "''"; 590 break; 591 case "float": 592 case "double": 593 value = "0.0"; 594 break; 595 default: 596 value = "null"; 597 } 598 } 599 600 public VariableInfo(String src, String type, String name, String value) { 601 super(src, type, name); 602 this.value = value; 603 this.initialValue = value; 604 } 605 606 @Override 607 public Consumer<String> checkOutput() { 608 String arrowPattern = String.format("%s ==> %s", name, value); 609 Predicate<String> arrowCheckOutput = Pattern.compile(arrowPattern).asPredicate(); 610 String howeverPattern = String.format("\\| *\\w+ variable %s, however*.", name); 611 Predicate<String> howeverCheckOutput = Pattern.compile(howeverPattern).asPredicate(); 612 return output -> { 613 if (output.startsWith("| ")) { 614 assertTrue(howeverCheckOutput.test(output), 615 "Output: " + output + " does not fit pattern: " + howeverPattern); 616 } else { 617 assertTrue(arrowCheckOutput.test(output), 618 "Output: " + output + " does not fit pattern: " + arrowPattern); 619 } 620 }; 621 } 622 623 @Override 624 public int hashCode() { 625 return name.hashCode(); 626 } 627 628 @Override 629 public boolean equals(Object o) { 630 if (o instanceof VariableInfo) { 631 VariableInfo v = (VariableInfo) o; 632 return name.equals(v.name); 633 } 634 return false; 635 } 636 637 @Override 638 public String toString() { 639 return String.format("%s %s = %s", type, name, value); 640 } 641 642 @Override 643 public String getSource() { 644 String src = super.getSource(); 645 return src.endsWith(";") ? src : src + ";"; 646 } 647 } 648 649 public static class TempVariableInfo extends VariableInfo { 650 651 public TempVariableInfo(String src, String type, String name, String value) { 652 super(src, type, name, value); 653 } 654 655 @Override 656 public String getSource() { 657 return source; 658 } 659 } 660 661 public static class MethodInfo extends MemberInfo { 662 663 public final String signature; 664 665 public MethodInfo(String source, String signature, String name) { 666 super(source, signature.substring(0, signature.lastIndexOf(')') + 1), name); 667 this.signature = signature; 668 } 669 670 @Override 671 public Consumer<String> checkOutput() { 672 String expectedOutput = String.format("\\| *\\w+ method %s", name); 673 Predicate<String> checkOutput = Pattern.compile(expectedOutput).asPredicate(); 674 return s -> assertTrue(checkOutput.test(s), "Expected: '" + expectedOutput + "', actual: " + s); 675 } 676 677 @Override 678 public int hashCode() { 679 return (name.hashCode() << 2) ^ type.hashCode() ; 680 } 681 682 @Override 683 public boolean equals(Object o) { 684 if (o instanceof MemberInfo) { 685 MemberInfo m = (MemberInfo) o; 686 return name.equals(m.name) && type.equals(m.type); 687 } 688 return false; 689 } 690 691 @Override 692 public String toString() { 693 int i = signature.lastIndexOf(")") + 1; 694 if (i <= 0) { 695 return String.format("%s", name); 696 } else { 697 return String.format("%s %s%s", signature.substring(i), name, signature.substring(0, i)); 698 } 699 } 700 } 701 702 public static class ClassInfo extends MemberInfo { 703 704 public ClassInfo(String source, String type, String name) { 705 super(source, type, name); 706 } 707 708 @Override 709 public Consumer<String> checkOutput() { 710 String fullType = type.equals("@interface")? "annotation interface" : type; 711 String expectedOutput = String.format("\\| *\\w+ %s %s", fullType, name); 712 Predicate<String> checkOutput = Pattern.compile(expectedOutput).asPredicate(); 713 return s -> assertTrue(checkOutput.test(s), "Expected: '" + expectedOutput + "', actual: " + s); 714 } 715 716 @Override 717 public int hashCode() { 718 return name.hashCode() ; 719 } 720 721 @Override 722 public boolean equals(Object o) { 723 if (o instanceof ClassInfo) { 724 ClassInfo c = (ClassInfo) o; 725 return name.equals(c.name); 726 } 727 return false; 728 } 729 730 @Override 731 public String toString() { 732 return String.format("%s %s", type, name); 733 } 734 } 735 736 public static class ImportInfo extends MemberInfo { 737 public ImportInfo(String source, String type, String fullname) { 738 super(source, type, fullname); 739 } 740 741 @Override 742 public Consumer<String> checkOutput() { 743 return s -> assertTrue("".equals(s), "Expected: '', actual: " + s); 744 } 745 746 @Override 747 public int hashCode() { 748 return (name.hashCode() << 2) ^ type.hashCode() ; 749 } 750 751 @Override 752 public boolean equals(Object o) { 753 if (o instanceof ImportInfo) { 754 ImportInfo i = (ImportInfo) o; 755 return name.equals(i.name) && type.equals(i.type); 756 } 757 return false; 758 } 759 760 @Override 761 public String toString() { 762 return String.format("import %s%s", type.equals("static") ? "static " : "", name); 763 } 764 } 765 766 class WaitingTestingInputStream extends TestingInputStream { 767 768 private boolean closed; 769 770 @Override 771 synchronized void setInput(String s) { 772 super.setInput(s); 773 notify(); 774 } 775 776 synchronized void waitForInput() { 777 boolean interrupted = false; 778 try { 779 while (available() == 0 && !closed) { 780 try { 781 wait(); 782 } catch (InterruptedException e) { 783 interrupted = true; 784 // fall through and retry 785 } 786 } 787 } finally { 788 if (interrupted) { 789 Thread.currentThread().interrupt(); 790 } 791 } 792 } 793 794 @Override 795 public int read() { 796 waitForInput(); 797 return super.read(); 798 } 799 800 @Override 801 public int read(byte b[], int off, int len) { 802 waitForInput(); 803 return super.read(b, off, len); 804 } 805 806 @Override 807 public synchronized void close() throws IOException { 808 closed = true; 809 notify(); 810 } 811 } 812 813 class PromptedCommandOutputStream extends OutputStream { 814 private final ReplTest[] tests; 815 private int index = 0; 816 PromptedCommandOutputStream(ReplTest[] tests) { 817 this.tests = tests; 818 } 819 820 @Override 821 public synchronized void write(int b) { 822 if (b == 5 || b == 6) { 823 if (index < (tests.length - 1)) { 824 tests[index].run(true); 825 tests[index + 1].run(false); 826 } else { 827 fail("Did not exit Repl tool after test"); 828 } 829 ++index; 830 } // For now, anything else is thrown away 831 } 832 833 @Override 834 public synchronized void write(byte b[], int off, int len) { 835 if ((off < 0) || (off > b.length) || (len < 0) 836 || ((off + len) - b.length > 0)) { 837 throw new IndexOutOfBoundsException(); 838 } 839 for (int i = 0; i < len; ++i) { 840 write(b[off + i]); 841 } 842 } 843 } 844 845 public static final class MemoryPreferences extends AbstractPreferences { 846 847 private final Map<String, String> values = new HashMap<>(); 848 private final Map<String, MemoryPreferences> nodes = new HashMap<>(); 849 850 public MemoryPreferences() { 851 this(null, ""); 852 } 853 854 public MemoryPreferences(MemoryPreferences parent, String name) { 855 super(parent, name); 856 } 857 858 @Override 859 protected void putSpi(String key, String value) { 860 values.put(key, value); 861 } 862 863 @Override 864 protected String getSpi(String key) { 865 return values.get(key); 866 } 867 868 @Override 869 protected void removeSpi(String key) { 870 values.remove(key); 871 } 872 873 @Override 874 protected void removeNodeSpi() throws BackingStoreException { 875 ((MemoryPreferences) parent()).nodes.remove(name()); 876 } 877 878 @Override 879 protected String[] keysSpi() throws BackingStoreException { 880 return values.keySet().toArray(new String[0]); 881 } 882 883 @Override 884 protected String[] childrenNamesSpi() throws BackingStoreException { 885 return nodes.keySet().toArray(new String[0]); 886 } 887 888 @Override 889 protected AbstractPreferences childSpi(String name) { 890 return nodes.computeIfAbsent(name, n -> new MemoryPreferences(this, name)); 891 } 892 893 @Override 894 protected void syncSpi() throws BackingStoreException { 895 } 896 897 @Override 898 protected void flushSpi() throws BackingStoreException { 899 } 900 901 } 902 } 903