ReplToolTesting.java revision 4111:256d9fce6c53
126219Swpaul/* 226219Swpaul * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 326219Swpaul * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 426219Swpaul * 526219Swpaul * This code is free software; you can redistribute it and/or modify it 626219Swpaul * under the terms of the GNU General Public License version 2 only, as 726219Swpaul * published by the Free Software Foundation. 826219Swpaul * 926219Swpaul * This code is distributed in the hope that it will be useful, but WITHOUT 1026219Swpaul * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1126219Swpaul * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1226219Swpaul * version 2 for more details (a copy is included in the LICENSE file that 1326219Swpaul * accompanied this code). 1426219Swpaul * 1526219Swpaul * You should have received a copy of the GNU General Public License version 1626219Swpaul * 2 along with this work; if not, write to the Free Software Foundation, 1726219Swpaul * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1826219Swpaul * 1926219Swpaul * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2026219Swpaul * or visit www.oracle.com if you need additional information or have any 2126219Swpaul * questions. 2226219Swpaul */ 2326219Swpaul 2426219Swpaulimport java.io.ByteArrayOutputStream; 2526219Swpaulimport java.io.OutputStream; 2626219Swpaulimport java.io.PrintStream; 2726219Swpaulimport java.util.ArrayList; 2826219Swpaulimport java.util.Arrays; 2926219Swpaulimport java.util.Collections; 3026219Swpaulimport java.util.HashMap; 3155837Sjasoneimport java.util.List; 3255837Sjasoneimport java.util.Locale; 3326219Swpaulimport java.util.Map; 3471579Sdeischenimport java.util.function.Consumer; 3526219Swpaulimport java.util.function.Function; 3626219Swpaulimport java.util.function.Predicate; 3726219Swpaulimport java.util.logging.Level; 3826219Swpaulimport java.util.logging.Logger; 3926219Swpaulimport java.util.prefs.AbstractPreferences; 4026219Swpaulimport java.util.prefs.BackingStoreException; 4126219Swpaulimport java.util.regex.Matcher; 4226219Swpaulimport java.util.regex.Pattern; 4326219Swpaulimport java.util.stream.Collectors; 4426219Swpaulimport java.util.stream.Stream; 4526219Swpaul 4626219Swpaul 4726219Swpaulimport org.testng.annotations.BeforeMethod; 4874462Salfred 4926219Swpaulimport jdk.jshell.tool.JavaShellToolBuilder; 5026219Swpaulimport static java.util.stream.Collectors.toList; 5171579Sdeischenimport static org.testng.Assert.assertEquals; 5226219Swpaulimport static org.testng.Assert.assertNotNull; 5326219Swpaulimport static org.testng.Assert.assertTrue; 5426219Swpaulimport static org.testng.Assert.fail; 5526219Swpaul 5626219Swpaulpublic class ReplToolTesting { 5726219Swpaul 5826219Swpaul private final static String DEFAULT_STARTUP_MESSAGE = "| Welcome to"; 5926219Swpaul final static List<ImportInfo> START_UP_IMPORTS = Stream.of( 6026219Swpaul "java.io.*", 6126219Swpaul "java.math.*", 6226219Swpaul "java.net.*", 6326219Swpaul "java.nio.file.*", 6426219Swpaul "java.util.*", 6526219Swpaul "java.util.concurrent.*", 6626219Swpaul "java.util.function.*", 6726219Swpaul "java.util.prefs.*", 6826219Swpaul "java.util.regex.*", 6926219Swpaul "java.util.stream.*") 7026219Swpaul .map(s -> new ImportInfo("import " + s + ";", "", s)) 7126219Swpaul .collect(toList()); 7226219Swpaul final static List<MethodInfo> START_UP_METHODS = Stream.<MethodInfo>of() 7326219Swpaul .collect(toList()); 7426219Swpaul final static List<String> START_UP_CMD_METHOD = Stream.<String>of() 7526219Swpaul .collect(toList()); 7626219Swpaul final static List<String> PRINTING_CMD_METHOD = Stream.of( 7726219Swpaul "| void print(boolean)", 7826219Swpaul "| void print(char)", 7926219Swpaul "| void print(int)", 8026219Swpaul "| void print(long)", 8126219Swpaul "| void print(float)", 8226219Swpaul "| void print(double)", 8326219Swpaul "| void print(char s[])", 8426219Swpaul "| void print(String)", 8526219Swpaul "| void print(Object)", 8626219Swpaul "| void println()", 8726219Swpaul "| void println(boolean)", 8826219Swpaul "| void println(char)", 8926219Swpaul "| void println(int)", 9026219Swpaul "| void println(long)", 9126219Swpaul "| void println(float)", 9226219Swpaul "| void println(double)", 9326219Swpaul "| void println(char s[])", 9426219Swpaul "| void println(String)", 9526219Swpaul "| void println(Object)", 9626219Swpaul "| void printf(java.util.Locale,String,Object...)", 9726219Swpaul "| void printf(String,Object...)") 9826219Swpaul .collect(toList()); 9926219Swpaul final static List<String> START_UP = Collections.unmodifiableList( 10026219Swpaul Stream.concat(START_UP_IMPORTS.stream(), START_UP_METHODS.stream()) 10126219Swpaul .map(s -> s.getSource()) 10226219Swpaul .collect(toList())); 10326219Swpaul 10426219Swpaul private WaitingTestingInputStream cmdin = null; 10526219Swpaul private ByteArrayOutputStream cmdout = null; 10626219Swpaul private ByteArrayOutputStream cmderr = null; 10726219Swpaul private PromptedCommandOutputStream console = null; 10826219Swpaul private TestingInputStream userin = null; 10926219Swpaul private ByteArrayOutputStream userout = null; 11026219Swpaul private ByteArrayOutputStream usererr = null; 11126219Swpaul 11226219Swpaul private List<MemberInfo> keys; 11326219Swpaul private Map<String, VariableInfo> variables; 11426219Swpaul private Map<String, MethodInfo> methods; 11526219Swpaul private Map<String, ClassInfo> classes; 11626219Swpaul private Map<String, ImportInfo> imports; 11726219Swpaul private boolean isDefaultStartUp = true; 11826219Swpaul private Map<String, String> prefsMap; 11926219Swpaul private Map<String, String> envvars; 12026219Swpaul 12126219Swpaul public interface ReplTest { 12226219Swpaul void run(boolean after); 12326219Swpaul } 12426219Swpaul 12526219Swpaul public void setCommandInput(String s) { 12626219Swpaul cmdin.setInput(s); 12726219Swpaul } 12826219Swpaul 12926219Swpaul public final static Pattern idPattern = Pattern.compile("^\\s+(\\d+)"); 13026219Swpaul public Consumer<String> assertList() { 13126219Swpaul return s -> { 13226219Swpaul List<String> lines = Stream.of(s.split("\n")) 13326219Swpaul .filter(l -> !l.isEmpty()) 13426219Swpaul .collect(Collectors.toList()); 13526219Swpaul int previousId = Integer.MIN_VALUE; 13626219Swpaul assertEquals(lines.size(), keys.size(), "Number of keys"); 13726219Swpaul for (int i = 0; i < lines.size(); ++i) { 13826219Swpaul String line = lines.get(i); 13926219Swpaul Matcher matcher = idPattern.matcher(line); 14026219Swpaul assertTrue(matcher.find(), "Snippet id not found: " + line); 14126219Swpaul String src = keys.get(i).getSource(); 14226219Swpaul assertTrue(line.endsWith(src), "Line '" + line + "' does not end with: " + src); 14326219Swpaul int id = Integer.parseInt(matcher.group(1)); 14426219Swpaul assertTrue(previousId < id, 14526219Swpaul String.format("The previous id is not less than the next one: previous: %d, next: %d", 14626219Swpaul previousId, id)); 14726219Swpaul previousId = id; 14826219Swpaul } 14926219Swpaul }; 15026219Swpaul } 15126219Swpaul 15226219Swpaul private final static Pattern extractPattern = Pattern.compile("^\\| *(.*)$"); 15326219Swpaul private Consumer<String> assertMembers(String message, Map<String, ? extends MemberInfo> set) { 15426219Swpaul return s -> { 15526219Swpaul List<String> lines = Stream.of(s.split("\n")) 15626219Swpaul .filter(l -> !l.isEmpty()) 15726219Swpaul .filter(l -> !l.startsWith("| ")) // error/unresolved info 15826219Swpaul .collect(Collectors.toList()); 15926219Swpaul assertEquals(lines.size(), set.size(), message + " : expected: " + set.keySet() + "\ngot:\n" + lines); 16026219Swpaul for (String line : lines) { 16126219Swpaul Matcher matcher = extractPattern.matcher(line); 16226219Swpaul assertTrue(matcher.find(), line); 16326219Swpaul String src = matcher.group(1); 16426219Swpaul MemberInfo info = set.get(src); 16526219Swpaul assertNotNull(info, "Not found snippet with signature: " + src + ", line: " 16626219Swpaul + line + ", keys: " + set.keySet() + "\n"); 16726219Swpaul } 16826219Swpaul }; 16926219Swpaul } 17026219Swpaul 17126219Swpaul public Consumer<String> assertVariables() { 17226219Swpaul return assertMembers("Variables", variables); 17326219Swpaul } 17426219Swpaul 17526219Swpaul public Consumer<String> assertMethods() { 17626219Swpaul return assertMembers("Methods", methods); 17726219Swpaul } 17826219Swpaul 17926219Swpaul public Consumer<String> assertClasses() { 18026219Swpaul return assertMembers("Classes", classes); 18126219Swpaul } 18226219Swpaul 18326219Swpaul public Consumer<String> assertImports() { 18426219Swpaul return assertMembers("Imports", imports); 18526219Swpaul } 18626219Swpaul 18726219Swpaul public String getCommandOutput() { 18826219Swpaul String s = normalizeLineEndings(cmdout.toString()); 18926219Swpaul cmdout.reset(); 19026219Swpaul return s; 19126219Swpaul } 19226219Swpaul 19326219Swpaul public String getCommandErrorOutput() { 19426219Swpaul String s = normalizeLineEndings(cmderr.toString()); 19526219Swpaul cmderr.reset(); 19626219Swpaul return s; 19726219Swpaul } 19826219Swpaul 19926219Swpaul public void setUserInput(String s) { 20026219Swpaul userin.setInput(s); 20126219Swpaul } 20226219Swpaul 20326219Swpaul public String getUserOutput() { 20426219Swpaul String s = normalizeLineEndings(userout.toString()); 20526219Swpaul userout.reset(); 20626219Swpaul return s; 20726219Swpaul } 20826219Swpaul 20926219Swpaul public String getUserErrorOutput() { 21026219Swpaul String s = normalizeLineEndings(usererr.toString()); 21126219Swpaul usererr.reset(); 21226219Swpaul return s; 21326219Swpaul } 21426219Swpaul 21526219Swpaul public void test(ReplTest... tests) { 21626219Swpaul test(new String[0], tests); 21726219Swpaul } 21826219Swpaul 21926219Swpaul public void test(String[] args, ReplTest... tests) { 22026219Swpaul test(true, args, tests); 22126219Swpaul } 22226219Swpaul 22326219Swpaul public void test(boolean isDefaultStartUp, String[] args, ReplTest... tests) { 22426219Swpaul test(Locale.ROOT, isDefaultStartUp, args, DEFAULT_STARTUP_MESSAGE, tests); 22526219Swpaul } 22626219Swpaul 22726219Swpaul public void testNoStartUp(ReplTest... tests) { 22826219Swpaul test(Locale.ROOT, false, new String[] {"--no-startup"}, DEFAULT_STARTUP_MESSAGE, tests); 22926219Swpaul } 23026219Swpaul 23126219Swpaul public void test(Locale locale, boolean isDefaultStartUp, String[] args, String startUpMessage, ReplTest... tests) { 23226219Swpaul this.isDefaultStartUp = isDefaultStartUp; 23326219Swpaul initSnippets(); 23426219Swpaul ReplTest[] wtests = new ReplTest[tests.length + 3]; 23526219Swpaul wtests[0] = a -> assertCommandCheckOutput(a, "<start>", 23626219Swpaul s -> assertTrue(s.startsWith(startUpMessage), "Expected start-up message '" + startUpMessage + "' Got: " + s)); 23726219Swpaul wtests[1] = a -> assertCommand(a, "/debug 0", null); 23826219Swpaul System.arraycopy(tests, 0, wtests, 2, tests.length); 23926219Swpaul wtests[tests.length + 2] = a -> assertCommand(a, "/exit", null); 24026219Swpaul testRaw(locale, args, wtests); 24126219Swpaul } 24226219Swpaul 24326219Swpaul private void initSnippets() { 24426219Swpaul keys = new ArrayList<>(); 24526219Swpaul variables = new HashMap<>(); 24626219Swpaul methods = new HashMap<>(); 24726219Swpaul classes = new HashMap<>(); 24826219Swpaul imports = new HashMap<>(); 24926219Swpaul if (isDefaultStartUp) { 25026219Swpaul methods.putAll( 25126219Swpaul START_UP_METHODS.stream() 25226219Swpaul .collect(Collectors.toMap(Object::toString, Function.identity()))); 25326219Swpaul imports.putAll( 25426219Swpaul START_UP_IMPORTS.stream() 25526219Swpaul .collect(Collectors.toMap(Object::toString, Function.identity()))); 25626219Swpaul } 25726219Swpaul } 25826219Swpaul 25926219Swpaul @BeforeMethod 26026219Swpaul public void setUp() { 26126219Swpaul prefsMap = new HashMap<>(); 26226219Swpaul envvars = new HashMap<>(); 26326219Swpaul } 26426219Swpaul 26526219Swpaul protected void setEnvVar(String name, String value) { 26626219Swpaul envvars.put(name, value); 26726219Swpaul } 26826219Swpaul 26926219Swpaul protected JavaShellToolBuilder builder(Locale locale) { 27026219Swpaul // turn on logging of launch failures 27126219Swpaul Logger.getLogger("jdk.jshell.execution").setLevel(Level.ALL); 27226219Swpaul return JavaShellToolBuilder 27326219Swpaul .builder() 27426219Swpaul .in(cmdin, userin) 27526219Swpaul .out(new PrintStream(cmdout), new PrintStream(console), new PrintStream(userout)) 27626219Swpaul .err(new PrintStream(cmderr), new PrintStream(usererr)) 27726219Swpaul .persistence(prefsMap) 27826219Swpaul .env(envvars) 27926219Swpaul .locale(locale) 28026219Swpaul .promptCapture(true); 28126219Swpaul } 28226219Swpaul 28326219Swpaul private void testRaw(Locale locale, String[] args, ReplTest... tests) { 28426219Swpaul testRawInit(tests); 28526219Swpaul testRawRun(locale, args); 28626219Swpaul testRawCheck(locale); 28726219Swpaul } 28826219Swpaul 28926219Swpaul private void testRawInit(ReplTest... tests) { 29026219Swpaul cmdin = new WaitingTestingInputStream(); 29126219Swpaul cmdout = new ByteArrayOutputStream(); 29226219Swpaul cmderr = new ByteArrayOutputStream(); 29326219Swpaul console = new PromptedCommandOutputStream(tests); 29426219Swpaul userin = new TestingInputStream(); 29526219Swpaul userout = new ByteArrayOutputStream(); 29626219Swpaul usererr = new ByteArrayOutputStream(); 29726219Swpaul } 29826219Swpaul 29926219Swpaul protected void testRawRun(Locale locale, String[] args) { 30026219Swpaul try { 30126219Swpaul builder(locale) 30226219Swpaul .run(args); 30326219Swpaul } catch (Exception ex) { 30426219Swpaul fail("Repl tool died with exception", ex); 30526219Swpaul } 30626219Swpaul } 30726219Swpaul 30826219Swpaul private void testRawCheck(Locale locale) { 30926219Swpaul // perform internal consistency checks on state, if desired 31026219Swpaul String cos = getCommandOutput(); 31126219Swpaul String ceos = getCommandErrorOutput(); 31226219Swpaul String uos = getUserOutput(); 31326219Swpaul String ueos = getUserErrorOutput(); 31426219Swpaul assertTrue((cos.isEmpty() || cos.startsWith("| Goodbye") || !locale.equals(Locale.ROOT)), 31526219Swpaul "Expected a goodbye, but got: " + cos); 31626219Swpaul assertTrue(ceos.isEmpty(), "Expected empty command error output, got: " + ceos); 31726219Swpaul assertTrue(uos.isEmpty(), "Expected empty user output, got: " + uos); 31826219Swpaul assertTrue(ueos.isEmpty(), "Expected empty user error output, got: " + ueos); 31926219Swpaul } 32026219Swpaul 32126666Swpaul public void assertReset(boolean after, String cmd) { 32226219Swpaul assertCommand(after, cmd, "| Resetting state.\n"); 32326219Swpaul initSnippets(); 32426219Swpaul } 32526219Swpaul 32626219Swpaul public void evaluateExpression(boolean after, String type, String expr, String value) { 32726219Swpaul String output = String.format("(\\$\\d+) ==> %s", value); 32826219Swpaul Pattern outputPattern = Pattern.compile(output); 32926219Swpaul assertCommandCheckOutput(after, expr, s -> { 33026219Swpaul Matcher matcher = outputPattern.matcher(s); 33126219Swpaul assertTrue(matcher.find(), "Output: '" + s + "' does not fit pattern: '" + output + "'"); 33226219Swpaul String name = matcher.group(1); 33326219Swpaul VariableInfo tempVar = new TempVariableInfo(expr, type, name, value); 33426219Swpaul variables.put(tempVar.toString(), tempVar); 33526219Swpaul addKey(after, tempVar); 33626219Swpaul }); 33726219Swpaul } 33826219Swpaul 33926219Swpaul public void loadVariable(boolean after, String type, String name) { 34026219Swpaul loadVariable(after, type, name, null, null); 34126219Swpaul } 34226219Swpaul 34326219Swpaul public void loadVariable(boolean after, String type, String name, String expr, String value) { 34426219Swpaul String src = expr == null 34526219Swpaul ? String.format("%s %s", type, name) 34626219Swpaul : String.format("%s %s = %s", type, name, expr); 34726219Swpaul VariableInfo var = expr == null 34826219Swpaul ? new VariableInfo(src, type, name) 34926219Swpaul : new VariableInfo(src, type, name, value); 35026219Swpaul addKey(after, var, variables); 35126219Swpaul addKey(after, var); 35226219Swpaul } 35326219Swpaul 35426219Swpaul public void assertVariable(boolean after, String type, String name) { 35526219Swpaul assertVariable(after, type, name, null, null); 35626219Swpaul } 35726219Swpaul 35826219Swpaul public void assertVariable(boolean after, String type, String name, String expr, String value) { 35926219Swpaul String src = expr == null 36026219Swpaul ? String.format("%s %s", type, name) 36126219Swpaul : String.format("%s %s = %s", type, name, expr); 36226219Swpaul VariableInfo var = expr == null 36326219Swpaul ? new VariableInfo(src, type, name) 36426219Swpaul : new VariableInfo(src, type, name, value); 36526219Swpaul assertCommandCheckOutput(after, src, var.checkOutput()); 36626219Swpaul addKey(after, var, variables); 36726219Swpaul addKey(after, var); 36826219Swpaul } 36926219Swpaul 37026219Swpaul public void loadMethod(boolean after, String src, String signature, String name) { 37126219Swpaul MethodInfo method = new MethodInfo(src, signature, name); 37226219Swpaul addKey(after, method, methods); 37326219Swpaul addKey(after, method); 37426219Swpaul } 37526219Swpaul 37626219Swpaul public void assertMethod(boolean after, String src, String signature, String name) { 37726219Swpaul MethodInfo method = new MethodInfo(src, signature, name); 37826219Swpaul assertCommandCheckOutput(after, src, method.checkOutput()); 37926219Swpaul addKey(after, method, methods); 38026219Swpaul addKey(after, method); 38171579Sdeischen } 38226219Swpaul 38326219Swpaul public void loadClass(boolean after, String src, String type, String name) { 38426219Swpaul ClassInfo clazz = new ClassInfo(src, type, name); 38526219Swpaul addKey(after, clazz, classes); 38626219Swpaul addKey(after, clazz); 38726219Swpaul } 38826219Swpaul 38926219Swpaul public void assertClass(boolean after, String src, String type, String name) { 39026219Swpaul ClassInfo clazz = new ClassInfo(src, type, name); 39126219Swpaul assertCommandCheckOutput(after, src, clazz.checkOutput()); 39226219Swpaul addKey(after, clazz, classes); 39326219Swpaul addKey(after, clazz); 39426219Swpaul } 39526219Swpaul 39626219Swpaul public void loadImport(boolean after, String src, String type, String name) { 39771579Sdeischen ImportInfo i = new ImportInfo(src, type, name); 39826219Swpaul addKey(after, i, imports); 39926219Swpaul addKey(after, i); 40026219Swpaul } 40126219Swpaul 40226219Swpaul public void assertImport(boolean after, String src, String type, String name) { 40326219Swpaul ImportInfo i = new ImportInfo(src, type, name); 40426219Swpaul assertCommandCheckOutput(after, src, i.checkOutput()); 40571579Sdeischen addKey(after, i, imports); 40626219Swpaul addKey(after, i); 40726219Swpaul } 40826219Swpaul 40926219Swpaul private <T extends MemberInfo> void addKey(boolean after, T memberInfo, Map<String, T> map) { 41026219Swpaul if (after) { 41171579Sdeischen map.entrySet().removeIf(e -> e.getValue().equals(memberInfo)); 41226219Swpaul map.put(memberInfo.toString(), memberInfo); 41326219Swpaul } 41426219Swpaul } 41526219Swpaul 41626219Swpaul private <T extends MemberInfo> void addKey(boolean after, T memberInfo) { 41726219Swpaul if (after) { 41826219Swpaul for (int i = 0; i < keys.size(); ++i) { 41926219Swpaul MemberInfo m = keys.get(i); 42026219Swpaul if (m.equals(memberInfo)) { 42126219Swpaul keys.set(i, memberInfo); 42226219Swpaul return; 42326219Swpaul } 42471579Sdeischen } 42526219Swpaul keys.add(memberInfo); 42626219Swpaul } 42726219Swpaul } 42826219Swpaul 42926219Swpaul private void dropKey(boolean after, String cmd, String name, Map<String, ? extends MemberInfo> map, String output) { 43026219Swpaul assertCommand(after, cmd, output); 43126219Swpaul if (after) { 43226219Swpaul map.remove(name); 43356698Sjasone for (int i = 0; i < keys.size(); ++i) { 43426219Swpaul MemberInfo m = keys.get(i); 43526219Swpaul if (m.toString().equals(name)) { 43626219Swpaul keys.remove(i); 43726219Swpaul return; 43826219Swpaul } 43926219Swpaul } 44026219Swpaul throw new AssertionError("Key not found: " + name + ", keys: " + keys); 44126219Swpaul } 44226219Swpaul } 44326219Swpaul 44426219Swpaul public void dropVariable(boolean after, String cmd, String name, String output) { 44556698Sjasone dropKey(after, cmd, name, variables, output); 44626219Swpaul } 44726219Swpaul 44826219Swpaul public void dropMethod(boolean after, String cmd, String name, String output) { 44926219Swpaul dropKey(after, cmd, name, methods, output); 45026219Swpaul } 45126219Swpaul 45226219Swpaul public void dropClass(boolean after, String cmd, String name, String output) { 45326219Swpaul dropKey(after, cmd, name, classes, output); 45426219Swpaul } 45526219Swpaul 45626219Swpaul public void dropImport(boolean after, String cmd, String name, String output) { 45726219Swpaul dropKey(after, cmd, name, imports, output); 45826219Swpaul } 45926219Swpaul 46026219Swpaul public void assertCommand(boolean after, String cmd, String out) { 46126219Swpaul assertCommand(after, cmd, out, "", null, "", ""); 46226219Swpaul } 46326219Swpaul 46456698Sjasone public void assertCommandOutputContains(boolean after, String cmd, String... hasThese) { 46526219Swpaul assertCommandCheckOutput(after, cmd, (s) 46626219Swpaul -> assertTrue(Arrays.stream(hasThese) 46726219Swpaul .allMatch(has -> s.contains(has)), 46826219Swpaul "Output: \'" + s + "' does not contain: " 46926219Swpaul + Arrays.stream(hasThese) 47026219Swpaul .filter(has -> !s.contains(has)) 47126219Swpaul .collect(Collectors.joining(", ")))); 47226219Swpaul } 47326219Swpaul 47426219Swpaul public void assertCommandOutputStartsWith(boolean after, String cmd, String starts) { 47526219Swpaul assertCommandCheckOutput(after, cmd, assertStartsWith(starts)); 47626219Swpaul } 47726219Swpaul 47826219Swpaul public void assertCommandCheckOutput(boolean after, String cmd, Consumer<String> check) { 47926219Swpaul if (!after) { 48026219Swpaul assertCommand(false, cmd, null); 48126219Swpaul } else { 48226219Swpaul String got = getCommandOutput(); 48326219Swpaul check.accept(got); 48426219Swpaul assertCommand(true, cmd, null); 48526219Swpaul } 48626219Swpaul } 48726219Swpaul 48826219Swpaul public void assertCommand(boolean after, String cmd, String out, String err, 48926219Swpaul String userinput, String print, String usererr) { 49026219Swpaul if (!after) { 49126219Swpaul if (userinput != null) { 49226219Swpaul setUserInput(userinput); 49326219Swpaul } 49426219Swpaul setCommandInput(cmd + "\n"); 49526219Swpaul } else { 49626219Swpaul assertOutput(getCommandOutput().trim(), out==null? out : out.trim(), "command output: " + cmd); 497 assertOutput(getCommandErrorOutput(), err, "command error: " + cmd); 498 assertOutput(getUserOutput(), print, "user output: " + cmd); 499 assertOutput(getUserErrorOutput(), usererr, "user error: " + cmd); 500 } 501 } 502 503 public Consumer<String> assertStartsWith(String prefix) { 504 return (output) -> assertTrue(output.trim().startsWith(prefix), "Output: \'" + output + "' does not start with: " + prefix); 505 } 506 507 public void assertOutput(String got, String expected, String display) { 508 if (expected != null) { 509 assertEquals(got, expected, display + ".\n"); 510 } 511 } 512 513 private String normalizeLineEndings(String text) { 514 return text.replace(System.getProperty("line.separator"), "\n"); 515 } 516 517 public static abstract class MemberInfo { 518 public final String source; 519 public final String type; 520 public final String name; 521 522 public MemberInfo(String source, String type, String name) { 523 this.source = source; 524 this.type = type; 525 this.name = name; 526 } 527 528 @Override 529 public int hashCode() { 530 return name.hashCode(); 531 } 532 533 @Override 534 public boolean equals(Object o) { 535 if (o instanceof MemberInfo) { 536 MemberInfo mi = (MemberInfo) o; 537 return name.equals(mi.name); 538 } 539 return false; 540 } 541 542 public abstract Consumer<String> checkOutput(); 543 544 public String getSource() { 545 return source; 546 } 547 } 548 549 public static class VariableInfo extends MemberInfo { 550 551 public final String value; 552 public final String initialValue; 553 554 public VariableInfo(String src, String type, String name) { 555 super(src, type, name); 556 this.initialValue = null; 557 switch (type) { 558 case "byte": 559 case "short": 560 case "int": 561 case "long": 562 value = "0"; 563 break; 564 case "boolean": 565 value = "false"; 566 break; 567 case "char": 568 value = "''"; 569 break; 570 case "float": 571 case "double": 572 value = "0.0"; 573 break; 574 default: 575 value = "null"; 576 } 577 } 578 579 public VariableInfo(String src, String type, String name, String value) { 580 super(src, type, name); 581 this.value = value; 582 this.initialValue = value; 583 } 584 585 @Override 586 public Consumer<String> checkOutput() { 587 String arrowPattern = String.format("%s ==> %s", name, value); 588 Predicate<String> arrowCheckOutput = Pattern.compile(arrowPattern).asPredicate(); 589 String howeverPattern = String.format("\\| *\\w+ variable %s, however*.", name); 590 Predicate<String> howeverCheckOutput = Pattern.compile(howeverPattern).asPredicate(); 591 return output -> { 592 if (output.startsWith("| ")) { 593 assertTrue(howeverCheckOutput.test(output), 594 "Output: " + output + " does not fit pattern: " + howeverPattern); 595 } else { 596 assertTrue(arrowCheckOutput.test(output), 597 "Output: " + output + " does not fit pattern: " + arrowPattern); 598 } 599 }; 600 } 601 602 @Override 603 public int hashCode() { 604 return name.hashCode(); 605 } 606 607 @Override 608 public boolean equals(Object o) { 609 if (o instanceof VariableInfo) { 610 VariableInfo v = (VariableInfo) o; 611 return name.equals(v.name); 612 } 613 return false; 614 } 615 616 @Override 617 public String toString() { 618 return String.format("%s %s = %s", type, name, value); 619 } 620 621 @Override 622 public String getSource() { 623 String src = super.getSource(); 624 return src.endsWith(";") ? src : src + ";"; 625 } 626 } 627 628 public static class TempVariableInfo extends VariableInfo { 629 630 public TempVariableInfo(String src, String type, String name, String value) { 631 super(src, type, name, value); 632 } 633 634 @Override 635 public String getSource() { 636 return source; 637 } 638 } 639 640 public static class MethodInfo extends MemberInfo { 641 642 public final String signature; 643 644 public MethodInfo(String source, String signature, String name) { 645 super(source, signature.substring(0, signature.lastIndexOf(')') + 1), name); 646 this.signature = signature; 647 } 648 649 @Override 650 public Consumer<String> checkOutput() { 651 String expectedOutput = String.format("\\| *\\w+ method %s", name); 652 Predicate<String> checkOutput = Pattern.compile(expectedOutput).asPredicate(); 653 return s -> assertTrue(checkOutput.test(s), "Expected: '" + expectedOutput + "', actual: " + s); 654 } 655 656 @Override 657 public int hashCode() { 658 return (name.hashCode() << 2) ^ type.hashCode() ; 659 } 660 661 @Override 662 public boolean equals(Object o) { 663 if (o instanceof MemberInfo) { 664 MemberInfo m = (MemberInfo) o; 665 return name.equals(m.name) && type.equals(m.type); 666 } 667 return false; 668 } 669 670 @Override 671 public String toString() { 672 int i = signature.lastIndexOf(")") + 1; 673 if (i <= 0) { 674 return String.format("%s", name); 675 } else { 676 return String.format("%s %s%s", signature.substring(i), name, signature.substring(0, i)); 677 } 678 } 679 } 680 681 public static class ClassInfo extends MemberInfo { 682 683 public ClassInfo(String source, String type, String name) { 684 super(source, type, name); 685 } 686 687 @Override 688 public Consumer<String> checkOutput() { 689 String fullType = type.equals("@interface")? "annotation interface" : type; 690 String expectedOutput = String.format("\\| *\\w+ %s %s", fullType, name); 691 Predicate<String> checkOutput = Pattern.compile(expectedOutput).asPredicate(); 692 return s -> assertTrue(checkOutput.test(s), "Expected: '" + expectedOutput + "', actual: " + s); 693 } 694 695 @Override 696 public int hashCode() { 697 return name.hashCode() ; 698 } 699 700 @Override 701 public boolean equals(Object o) { 702 if (o instanceof ClassInfo) { 703 ClassInfo c = (ClassInfo) o; 704 return name.equals(c.name); 705 } 706 return false; 707 } 708 709 @Override 710 public String toString() { 711 return String.format("%s %s", type, name); 712 } 713 } 714 715 public static class ImportInfo extends MemberInfo { 716 public ImportInfo(String source, String type, String fullname) { 717 super(source, type, fullname); 718 } 719 720 @Override 721 public Consumer<String> checkOutput() { 722 return s -> assertTrue("".equals(s), "Expected: '', actual: " + s); 723 } 724 725 @Override 726 public int hashCode() { 727 return (name.hashCode() << 2) ^ type.hashCode() ; 728 } 729 730 @Override 731 public boolean equals(Object o) { 732 if (o instanceof ImportInfo) { 733 ImportInfo i = (ImportInfo) o; 734 return name.equals(i.name) && type.equals(i.type); 735 } 736 return false; 737 } 738 739 @Override 740 public String toString() { 741 return String.format("import %s%s", type.equals("static") ? "static " : "", name); 742 } 743 } 744 745 class WaitingTestingInputStream extends TestingInputStream { 746 747 @Override 748 synchronized void setInput(String s) { 749 super.setInput(s); 750 notify(); 751 } 752 753 synchronized void waitForInput() { 754 boolean interrupted = false; 755 try { 756 while (available() == 0) { 757 try { 758 wait(); 759 } catch (InterruptedException e) { 760 interrupted = true; 761 // fall through and retry 762 } 763 } 764 } finally { 765 if (interrupted) { 766 Thread.currentThread().interrupt(); 767 } 768 } 769 } 770 771 @Override 772 public int read() { 773 waitForInput(); 774 return super.read(); 775 } 776 777 @Override 778 public int read(byte b[], int off, int len) { 779 waitForInput(); 780 return super.read(b, off, len); 781 } 782 } 783 784 class PromptedCommandOutputStream extends OutputStream { 785 private final ReplTest[] tests; 786 private int index = 0; 787 PromptedCommandOutputStream(ReplTest[] tests) { 788 this.tests = tests; 789 } 790 791 @Override 792 public synchronized void write(int b) { 793 if (b == 5 || b == 6) { 794 if (index < (tests.length - 1)) { 795 tests[index].run(true); 796 tests[index + 1].run(false); 797 } else { 798 fail("Did not exit Repl tool after test"); 799 } 800 ++index; 801 } // For now, anything else is thrown away 802 } 803 804 @Override 805 public synchronized void write(byte b[], int off, int len) { 806 if ((off < 0) || (off > b.length) || (len < 0) 807 || ((off + len) - b.length > 0)) { 808 throw new IndexOutOfBoundsException(); 809 } 810 for (int i = 0; i < len; ++i) { 811 write(b[off + i]); 812 } 813 } 814 } 815 816 public static final class MemoryPreferences extends AbstractPreferences { 817 818 private final Map<String, String> values = new HashMap<>(); 819 private final Map<String, MemoryPreferences> nodes = new HashMap<>(); 820 821 public MemoryPreferences() { 822 this(null, ""); 823 } 824 825 public MemoryPreferences(MemoryPreferences parent, String name) { 826 super(parent, name); 827 } 828 829 @Override 830 protected void putSpi(String key, String value) { 831 values.put(key, value); 832 } 833 834 @Override 835 protected String getSpi(String key) { 836 return values.get(key); 837 } 838 839 @Override 840 protected void removeSpi(String key) { 841 values.remove(key); 842 } 843 844 @Override 845 protected void removeNodeSpi() throws BackingStoreException { 846 ((MemoryPreferences) parent()).nodes.remove(name()); 847 } 848 849 @Override 850 protected String[] keysSpi() throws BackingStoreException { 851 return values.keySet().toArray(new String[0]); 852 } 853 854 @Override 855 protected String[] childrenNamesSpi() throws BackingStoreException { 856 return nodes.keySet().toArray(new String[0]); 857 } 858 859 @Override 860 protected AbstractPreferences childSpi(String name) { 861 return nodes.computeIfAbsent(name, n -> new MemoryPreferences(this, name)); 862 } 863 864 @Override 865 protected void syncSpi() throws BackingStoreException { 866 } 867 868 @Override 869 protected void flushSpi() throws BackingStoreException { 870 } 871 872 } 873} 874