ToolBasicTest.java revision 3409:3c09f576196a
1/*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 8143037 8142447 8144095 8140265 8144906 8146138 8147887 8147886 8148316 8148317 8143955
27 * @summary Tests for Basic tests for REPL tool
28 * @requires os.family != "solaris"
29 * @modules jdk.compiler/com.sun.tools.javac.api
30 *          jdk.compiler/com.sun.tools.javac.main
31 *          jdk.jdeps/com.sun.tools.javap
32 *          jdk.jshell/jdk.internal.jshell.tool
33 * @library /tools/lib
34 * @ignore 8139873
35 * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
36 * @build KullaTesting TestingInputStream Compiler
37 * @run testng/timeout=600 ToolBasicTest
38 */
39
40import java.io.FileInputStream;
41import java.io.IOException;
42import java.io.PrintWriter;
43import java.io.StringWriter;
44import java.nio.file.Files;
45import java.nio.file.Path;
46import java.nio.file.Paths;
47import java.util.ArrayList;
48import java.util.Arrays;
49import java.util.List;
50import java.util.Locale;
51import java.util.Scanner;
52import java.util.function.BiFunction;
53import java.util.function.Consumer;
54import java.util.function.Function;
55import java.util.prefs.BackingStoreException;
56import java.util.prefs.Preferences;
57import java.util.stream.Collectors;
58import java.util.stream.Stream;
59
60import org.testng.annotations.Test;
61
62import static org.testng.Assert.assertEquals;
63import static org.testng.Assert.assertTrue;
64import static org.testng.Assert.fail;
65
66@Test
67public class ToolBasicTest extends ReplToolTesting {
68
69    public void elideStartUpFromList() {
70        test(
71                (a) -> assertCommandOutputContains(a, "123", "type int"),
72                (a) -> assertCommandCheckOutput(a, "/list", (s) -> {
73                    int cnt;
74                    try (Scanner scanner = new Scanner(s)) {
75                        cnt = 0;
76                        while (scanner.hasNextLine()) {
77                            String line = scanner.nextLine();
78                            if (!line.trim().isEmpty()) {
79                                ++cnt;
80                            }
81                        }
82                    }
83                    assertEquals(cnt, 1, "Expected only one listed line");
84                })
85        );
86    }
87
88    public void elideStartUpFromSave() throws IOException {
89        Compiler compiler = new Compiler();
90        Path path = compiler.getPath("myfile");
91        test(
92                (a) -> assertCommandOutputContains(a, "123", "type int"),
93                (a) -> assertCommand(a, "/save " + path.toString(), "")
94        );
95        try (Stream<String> lines = Files.lines(path)) {
96            assertEquals(lines.count(), 1, "Expected only one saved line");
97        }
98    }
99
100    public void testInterrupt() {
101        ReplTest interrupt = (a) -> assertCommand(a, "\u0003", "");
102        for (String s : new String[] { "", "\u0003" }) {
103            test(false, new String[]{"-nostartup"},
104                    (a) -> assertCommand(a, "int a = 2 +" + s, ""),
105                    interrupt,
106                    (a) -> assertCommand(a, "int a\u0003", ""),
107                    (a) -> assertCommand(a, "int a = 2 + 2\u0003", ""),
108                    (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
109                    (a) -> evaluateExpression(a, "int", "2", "2"),
110                    (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
111                    (a) -> assertCommand(a, "void f() {", ""),
112                    (a) -> assertCommand(a, "int q = 10;" + s, ""),
113                    interrupt,
114                    (a) -> assertCommand(a, "void f() {}\u0003", ""),
115                    (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
116                    (a) -> assertMethod(a, "int f() { return 0; }", "()int", "f"),
117                    (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
118                    (a) -> assertCommand(a, "class A {" + s, ""),
119                    interrupt,
120                    (a) -> assertCommand(a, "class A {}\u0003", ""),
121                    (a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
122                    (a) -> assertClass(a, "interface A {}", "interface", "A"),
123                    (a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
124                    (a) -> assertCommand(a, "import java.util.stream." + s, ""),
125                    interrupt,
126                    (a) -> assertCommand(a, "import java.util.stream.\u0003", ""),
127                    (a) -> assertCommandCheckOutput(a, "/imports", assertImports()),
128                    (a) -> assertImport(a, "import java.util.stream.Stream", "", "java.util.stream.Stream"),
129                    (a) -> assertCommandCheckOutput(a, "/imports", assertImports())
130            );
131        }
132    }
133
134    private final Object lock = new Object();
135    private PrintWriter out;
136    private boolean isStopped;
137    private Thread t;
138    private void assertStop(boolean after, String cmd, String output) {
139        if (!after) {
140            isStopped = false;
141            StringWriter writer = new StringWriter();
142            out = new PrintWriter(writer);
143            setCommandInput(cmd + "\n");
144            t = new Thread(() -> {
145                try {
146                    // no chance to know whether cmd is being evaluated
147                    Thread.sleep(5000);
148                } catch (InterruptedException ignored) {
149                }
150                int i = 1;
151                int n = 30;
152                synchronized (lock) {
153                    do {
154                        setCommandInput("\u0003");
155                        if (!isStopped) {
156                            out.println("Not stopped. Try again: " + i);
157                            try {
158                                lock.wait(1000);
159                            } catch (InterruptedException ignored) {
160                            }
161                        }
162                    } while (i++ < n && !isStopped);
163                    if (!isStopped) {
164                        System.err.println(writer.toString());
165                        fail("Evaluation was not stopped: '" + cmd + "'");
166                    }
167                }
168            });
169            t.start();
170        } else {
171            synchronized (lock)  {
172                out.println("Evaluation was stopped successfully: '" + cmd + "'");
173                isStopped = true;
174                lock.notify();
175            }
176            try {
177                t.join();
178                t = null;
179            } catch (InterruptedException ignored) {
180            }
181            assertOutput(getCommandOutput(), "", "command");
182            assertOutput(getCommandErrorOutput(), "", "command error");
183            assertOutput(getUserOutput().trim(), output, "user");
184            assertOutput(getUserErrorOutput(), "", "user error");
185        }
186    }
187
188    public void testStop() {
189        test(
190                (a) -> assertStop(a, "while (true) {}", "Killed."),
191                (a) -> assertStop(a, "while (true) { try { Thread.sleep(100); } catch (InterruptedException ex) { } }", "Killed.")
192        );
193    }
194
195    @Test(enabled = false) // TODO 8130450
196    public void testRerun() {
197        test(false, new String[] {"-nostartup"},
198                (a) -> assertCommand(a, "/0", "|  No such command or snippet id: /0\n|  Type /help for help."),
199                (a) -> assertCommand(a, "/5", "|  No such command or snippet id: /5\n|  Type /help for help.")
200        );
201        String[] codes = new String[] {
202                "int a = 0;", // var
203                "class A {}", // class
204                "void f() {}", // method
205                "bool b;", // active failed
206                "void g() { h(); }", // active corralled
207        };
208        List<ReplTest> tests = new ArrayList<>();
209        for (String s : codes) {
210            tests.add((a) -> assertCommand(a, s, null));
211        }
212        for (int i = 0; i < codes.length; ++i) {
213            final int finalI = i;
214            Consumer<String> check = (s) -> {
215                String[] ss = s.split("\n");
216                assertEquals(ss[0], codes[finalI]);
217                assertTrue(ss.length > 1, s);
218            };
219            tests.add((a) -> assertCommandCheckOutput(a, "/" + (finalI + 1), check));
220        }
221        for (int i = 0; i < codes.length; ++i) {
222            final int finalI = i;
223            Consumer<String> check = (s) -> {
224                String[] ss = s.split("\n");
225                assertEquals(ss[0], codes[codes.length - finalI - 1]);
226                assertTrue(ss.length > 1, s);
227            };
228            tests.add((a) -> assertCommandCheckOutput(a, "/-" + (finalI + 1), check));
229        }
230        tests.add((a) -> assertCommandCheckOutput(a, "/!", assertStartsWith("void g() { h(); }")));
231        test(false, new String[]{"-nostartup"},
232                tests.toArray(new ReplTest[tests.size()]));
233    }
234
235    public void test8142447() {
236        Function<String, BiFunction<String, Integer, ReplTest>> assertRerun = cmd -> (code, assertionCount) ->
237                (a) -> assertCommandCheckOutput(a, cmd, s -> {
238                            String[] ss = s.split("\n");
239                            assertEquals(ss[0], code);
240                            loadVariable(a, "int", "assertionCount", Integer.toString(assertionCount), Integer.toString(assertionCount));
241                        });
242        ReplTest assertVariables = (a) -> assertCommandCheckOutput(a, "/v", assertVariables());
243
244        Compiler compiler = new Compiler();
245        Path startup = compiler.getPath("StartupFileOption/startup.txt");
246        compiler.writeToFile(startup, "int assertionCount = 0;\n" + // id: s1
247                "void add(int n) { assertionCount += n; }");
248        test(new String[]{"-startup", startup.toString()},
249                (a) -> assertCommand(a, "add(1)", ""), // id: 1
250                (a) -> assertCommandCheckOutput(a, "add(ONE)", s -> assertEquals(s.split("\n")[0], "|  Error:")), // id: e1
251                (a) -> assertVariable(a, "int", "ONE", "1", "1"),
252                assertRerun.apply("/1").apply("add(1)", 2), assertVariables,
253                assertRerun.apply("/e1").apply("add(ONE)", 3), assertVariables,
254                assertRerun.apply("/s1").apply("int assertionCount = 0;", 0), assertVariables
255        );
256
257        test(false, new String[] {"-nostartup"},
258                (a) -> assertCommand(a, "/s1", "|  No such command or snippet id: /s1\n|  Type /help for help."),
259                (a) -> assertCommand(a, "/1", "|  No such command or snippet id: /1\n|  Type /help for help."),
260                (a) -> assertCommand(a, "/e1", "|  No such command or snippet id: /e1\n|  Type /help for help.")
261        );
262    }
263
264    public void testClasspathDirectory() {
265        Compiler compiler = new Compiler();
266        Path outDir = Paths.get("testClasspathDirectory");
267        compiler.compile(outDir, "package pkg; public class A { public String toString() { return \"A\"; } }");
268        Path classpath = compiler.getPath(outDir);
269        test(
270                (a) -> assertCommand(a, "/classpath " + classpath, String.format("|  Path '%s' added to classpath", classpath)),
271                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "A")
272        );
273        test(new String[] { "-cp", classpath.toString() },
274                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "A")
275        );
276        test(new String[] { "-classpath", classpath.toString() },
277                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "A")
278        );
279    }
280
281    public void testClasspathJar() {
282        Compiler compiler = new Compiler();
283        Path outDir = Paths.get("testClasspathJar");
284        compiler.compile(outDir, "package pkg; public class A { public String toString() { return \"A\"; } }");
285        String jarName = "test.jar";
286        compiler.jar(outDir, jarName, "pkg/A.class");
287        Path jarPath = compiler.getPath(outDir).resolve(jarName);
288        test(
289                (a) -> assertCommand(a, "/classpath " + jarPath, String.format("|  Path '%s' added to classpath", jarPath)),
290                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "A")
291        );
292        test(new String[] { "-cp", jarPath.toString() },
293                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "A")
294        );
295        test(new String[] { "-classpath", jarPath.toString() },
296                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "A")
297        );
298    }
299
300    public void testStartupFileOption() {
301        try {
302            Compiler compiler = new Compiler();
303            Path startup = compiler.getPath("StartupFileOption/startup.txt");
304            compiler.writeToFile(startup, "class A { public String toString() { return \"A\"; } }");
305            test(new String[]{"-startup", startup.toString()},
306                    (a) -> evaluateExpression(a, "A", "new A()", "A")
307            );
308            test(new String[]{"-nostartup"},
309                    (a) -> assertCommandCheckOutput(a, "printf(\"\")", assertStartsWith("|  Error:\n|  cannot find symbol"))
310            );
311            test((a) -> assertCommand(a, "printf(\"A\")", "", "", null, "A", ""));
312            test(Locale.ROOT, false, new String[]{"-startup", "UNKNOWN"}, "|  File 'UNKNOWN' for start-up is not found.");
313        } finally {
314            removeStartup();
315        }
316    }
317
318    public void testLoadingFromArgs() {
319        Compiler compiler = new Compiler();
320        Path path = compiler.getPath("loading.repl");
321        compiler.writeToFile(path, "int a = 10; double x = 20; double a = 10;");
322        test(new String[] { path.toString() },
323                (a) -> assertCommand(a, "x", "x ==> 20.0"),
324                (a) -> assertCommand(a, "a", "a ==> 10.0")
325        );
326        Path unknown = compiler.getPath("UNKNOWN.jar");
327        test(Locale.ROOT, true, new String[]{unknown.toString()},
328                "|  File " + unknown
329                + " is not found: " + unresolvableMessage(unknown));
330    }
331
332    public void testReset() {
333        test(
334                (a) -> assertReset(a, "/res"),
335                (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
336                (a) -> assertVariable(a, "int", "x"),
337                (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
338                (a) -> assertMethod(a, "void f() { }", "()void", "f"),
339                (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
340                (a) -> assertClass(a, "class A { }", "class", "A"),
341                (a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
342                (a) -> assertImport(a, "import java.util.stream.*;", "", "java.util.stream.*"),
343                (a) -> assertCommandCheckOutput(a, "/imports", assertImports()),
344                (a) -> assertReset(a, "/reset"),
345                (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
346                (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
347                (a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
348                (a) -> assertCommandCheckOutput(a, "/imports", assertImports())
349        );
350    }
351
352    public void testOpen() {
353        Compiler compiler = new Compiler();
354        Path path = compiler.getPath("testOpen.repl");
355        compiler.writeToFile(path,
356                "int a = 10;\ndouble x = 20;\ndouble a = 10;\n" +
357                        "class A { public String toString() { return \"A\"; } }\nimport java.util.stream.*;");
358        for (String s : new String[]{"/o", "/open"}) {
359            test(
360                    (a) -> assertCommand(a, s + " " + path.toString(), ""),
361                    (a) -> assertCommand(a, "a", "a ==> 10.0"),
362                    (a) -> evaluateExpression(a, "A", "new A();", "A"),
363                    (a) -> evaluateExpression(a, "long", "Stream.of(\"A\").count();", "1"),
364                    (a) -> {
365                        loadVariable(a, "double", "x", "20.0", "20.0");
366                        loadVariable(a, "double", "a", "10.0", "10.0");
367                        loadVariable(a, "A", "$7", "new A();", "A");
368                        loadVariable(a, "long", "$8", "Stream.of(\"A\").count();", "1");
369                        loadClass(a, "class A { public String toString() { return \"A\"; } }",
370                                "class", "A");
371                        loadImport(a, "import java.util.stream.*;", "", "java.util.stream.*");
372                        assertCommandCheckOutput(a, "/types", assertClasses());
373                    },
374                    (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
375                    (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
376                    (a) -> assertCommandCheckOutput(a, "/imports", assertImports())
377            );
378            Path unknown = compiler.getPath("UNKNOWN.repl");
379            test(
380                    (a) -> assertCommand(a, s + " " + unknown,
381                            "|  File '" + unknown + "' for '/open' is not found.")
382            );
383        }
384    }
385
386    public void testSave() throws IOException {
387        Compiler compiler = new Compiler();
388        Path path = compiler.getPath("testSave.repl");
389        List<String> list = Arrays.asList(
390                "int a;",
391                "class A { public String toString() { return \"A\"; } }"
392        );
393        test(
394                (a) -> assertVariable(a, "int", "a"),
395                (a) -> assertCommand(a, "()", null, null, null, "", ""),
396                (a) -> assertClass(a, "class A { public String toString() { return \"A\"; } }", "class", "A"),
397                (a) -> assertCommand(a, "/save " + path.toString(), "")
398        );
399        assertEquals(Files.readAllLines(path), list);
400        {
401            List<String> output = new ArrayList<>();
402            test(
403                    (a) -> assertCommand(a, "int a;", null),
404                    (a) -> assertCommand(a, "()", null, null, null, "", ""),
405                    (a) -> assertClass(a, "class A { public String toString() { return \"A\"; } }", "class", "A"),
406                    (a) -> assertCommandCheckOutput(a, "/list -all", (out) ->
407                            output.addAll(Stream.of(out.split("\n"))
408                                    .filter(str -> !str.isEmpty())
409                                    .map(str -> str.substring(str.indexOf(':') + 2))
410                                    .filter(str -> !str.startsWith("/"))
411                                    .collect(Collectors.toList()))),
412                    (a) -> assertCommand(a, "/save -all " + path.toString(), "")
413            );
414            assertEquals(Files.readAllLines(path), output);
415        }
416        List<String> output = new ArrayList<>();
417        test(
418                (a) -> assertVariable(a, "int", "a"),
419                (a) -> assertCommand(a, "()", null, null, null, "", ""),
420                (a) -> assertClass(a, "class A { public String toString() { return \"A\"; } }", "class", "A"),
421                (a) -> assertCommandCheckOutput(a, "/history", (out) ->
422                        output.addAll(Stream.of(out.split("\n"))
423                                .filter(str -> !str.isEmpty())
424                                .collect(Collectors.toList()))),
425                (a) -> assertCommand(a, "/save history " + path.toString(), "")
426        );
427        output.add("/save history " + path.toString());
428        assertEquals(Files.readAllLines(path), output);
429    }
430
431    public void testStartSet() throws BackingStoreException {
432        try {
433            Compiler compiler = new Compiler();
434            Path startUpFile = compiler.getPath("startUp.txt");
435            test(
436                    (a) -> assertVariable(a, "int", "a"),
437                    (a) -> assertVariable(a, "double", "b", "10", "10.0"),
438                    (a) -> assertMethod(a, "void f() {}", "()V", "f"),
439                    (a) -> assertImport(a, "import java.util.stream.*;", "", "java.util.stream.*"),
440                    (a) -> assertCommand(a, "/save " + startUpFile.toString(), null),
441                    (a) -> assertCommand(a, "/set start " + startUpFile.toString(), null)
442            );
443            Path unknown = compiler.getPath("UNKNOWN");
444            test(
445                    (a) -> assertCommandOutputStartsWith(a, "/set start " + unknown.toString(),
446                            "|  File '" + unknown + "' for '/set start' is not found.")
447            );
448            test(false, new String[0],
449                    (a) -> {
450                        loadVariable(a, "int", "a");
451                        loadVariable(a, "double", "b", "10.0", "10.0");
452                        loadMethod(a, "void f() {}", "()void", "f");
453                        loadImport(a, "import java.util.stream.*;", "", "java.util.stream.*");
454                        assertCommandCheckOutput(a, "/types", assertClasses());
455                    },
456                    (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
457                    (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
458                    (a) -> assertCommandCheckOutput(a, "/imports", assertImports())
459            );
460        } finally {
461            removeStartup();
462        }
463    }
464
465    private void removeStartup() {
466        Preferences preferences = Preferences.userRoot().node("tool/JShell");
467        if (preferences != null) {
468            preferences.remove("STARTUP");
469        }
470    }
471
472    public void testStartSave() throws IOException {
473        Compiler compiler = new Compiler();
474        Path startSave = compiler.getPath("startSave.txt");
475        test(a -> assertCommand(a, "/save -start " + startSave.toString(), null));
476        List<String> lines = Files.lines(startSave)
477                .filter(s -> !s.isEmpty())
478                .collect(Collectors.toList());
479        assertEquals(lines, START_UP);
480    }
481
482    public void testConstrainedUpdates() {
483        test(
484                a -> assertClass(a, "class XYZZY { }", "class", "XYZZY"),
485                a -> assertVariable(a, "XYZZY", "xyzzy"),
486                a -> assertCommandCheckOutput(a, "import java.util.stream.*",
487                        (out) -> assertTrue(out.trim().isEmpty(), "Expected no output, got: " + out))
488        );
489    }
490
491    public void testRemoteExit() {
492        test(
493                a -> assertVariable(a, "int", "x"),
494                a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
495                a -> assertCommandOutputContains(a, "System.exit(5);", "terminated"),
496                a -> assertCommandCheckOutput(a, "/vars", s ->
497                        assertTrue(s.trim().isEmpty(), s)),
498                a -> assertMethod(a, "void f() { }", "()void", "f"),
499                a -> assertCommandCheckOutput(a, "/methods", assertMethods())
500        );
501    }
502
503    public void testFeedbackNegative() {
504        test(a -> assertCommandCheckOutput(a, "/set feedback aaaa",
505                assertStartsWith("|  Does not match any current feedback mode")));
506    }
507
508    public void testFeedbackSilent() {
509        for (String off : new String[]{"s", "silent"}) {
510            test(
511                    a -> assertCommand(a, "/set feedback " + off, ""),
512                    a -> assertCommand(a, "int a", ""),
513                    a -> assertCommand(a, "void f() {}", ""),
514                    a -> assertCommandCheckOutput(a, "aaaa", assertStartsWith("|  Error:")),
515                    a -> assertCommandCheckOutput(a, "public void f() {}", assertStartsWith("|  Warning:"))
516            );
517        }
518    }
519
520    public void testFeedbackNormal() {
521        Compiler compiler = new Compiler();
522        Path testNormalFile = compiler.getPath("testConciseNormal");
523        String[] sources = new String[] {"int a", "void f() {}", "class A {}", "a = 10"};
524        String[] sources2 = new String[] {"int a //again", "void f() {int y = 4;}", "class A {} //again", "a = 10"};
525        String[] output = new String[] {
526                "a ==> 0",
527                "|  created method f()",
528                "|  created class A",
529                "a ==> 10"
530        };
531        compiler.writeToFile(testNormalFile, sources2);
532        for (String feedback : new String[]{"/set fe", "/set feedback"}) {
533            for (String feedbackState : new String[]{"n", "normal"}) {
534                test(
535                        a -> assertCommand(a, feedback + " " + feedbackState, "|  Feedback mode: normal"),
536                        a -> assertCommand(a, sources[0], output[0]),
537                        a -> assertCommand(a, sources[1], output[1]),
538                        a -> assertCommand(a, sources[2], output[2]),
539                        a -> assertCommand(a, sources[3], output[3]),
540                        a -> assertCommand(a, "/o " + testNormalFile.toString(), "")
541                );
542            }
543        }
544    }
545
546    public void testHistoryReference() {
547        test(false, new String[]{"-nostartup"},
548                a -> assertCommand(a, "System.err.println(1)", "", "", null, "", "1\n"),
549                a -> assertCommand(a, "System.err.println(2)", "", "", null, "", "2\n"),
550                a -> assertCommand(a, "/-2", "System.err.println(1)", "", null, "", "1\n"),
551                a -> assertCommand(a, "/history",
552                                                    "/debug 0\n" +
553                                                    "System.err.println(1)\n" +
554                                                    "System.err.println(2)\n" +
555                                                    "System.err.println(1)\n" +
556                                                    "/history\n"),
557                a -> assertCommand(a, "/-2", "System.err.println(2)", "", null, "", "2\n"),
558                a -> assertCommand(a, "/!", "System.err.println(2)", "", null, "", "2\n"),
559                a -> assertCommand(a, "/2", "System.err.println(2)", "", null, "", "2\n"),
560                a -> assertCommand(a, "/1", "System.err.println(1)", "", null, "", "1\n")
561        );
562    }
563
564    private String unresolvableMessage(Path p) {
565        try {
566            new FileInputStream(p.toFile());
567            throw new AssertionError("Expected exception did not occur.");
568        } catch (IOException ex) {
569            return ex.getMessage();
570        }
571    }
572}
573