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;
octeon_ddr_conf_table_get(int * count,int * def_ddr_freq)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;
board_fix_fdt(void * fdt)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)",
board_configure_qlms(void)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(
board_late_init(void)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 {
123         void run(boolean after);
124     }
125 
126     public void setCommandInput(String s) {
127         cmdin.setInput(s);
128     }
129 
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+)");
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