8178077: jshell tool: crash on ctrl-up or ctrl-down
authorjlahoda
Thu, 06 Apr 2017 16:19:33 +0200
changeset 44570 7fec4e13a5cf
parent 44569 b1accf8b2aed
child 44571 1140b8dad6bb
8178077: jshell tool: crash on ctrl-up or ctrl-down Summary: Adding a test for EditingHistory. Reviewed-by: rfield
langtools/test/jdk/jshell/HistoryUITest.java
langtools/test/jdk/jshell/MergedTabShiftTabTest.java
langtools/test/jdk/jshell/UITesting.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/HistoryUITest.java	Thu Apr 06 16:19:33 2017 +0200
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8178077
+ * @summary Check the UI behavior of editing history.
+ * @modules
+ *     jdk.compiler/com.sun.tools.javac.api
+ *     jdk.compiler/com.sun.tools.javac.main
+ *     jdk.jshell/jdk.jshell:open
+ * @library /tools/lib
+ * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
+ * @build Compiler UITesting
+ * @build HistoryUITest
+ * @run testng HistoryUITest
+ */
+
+import org.testng.annotations.Test;
+
+@Test
+public class HistoryUITest extends UITesting {
+
+    public void testPrevNextSnippet() throws Exception {
+        doRunTest((inputSink, out) -> {
+            inputSink.write("void test1() {\nSystem.err.println(1);\n}\n");
+            waitOutput(out, "\u0005");
+            inputSink.write("void test2() {\nSystem.err.println(2);\n}\n");
+            waitOutput(out, "\u0005");
+            inputSink.write(CTRL_UP);
+            waitOutput(out, "^void test2\\(\\) \\{");
+            inputSink.write(CTRL_UP);
+            waitOutput(out, "^" + clearOut("2() {") + "1\\(\\) \\{");
+            inputSink.write(CTRL_DOWN);
+            waitOutput(out, "^" + clearOut("1() {") + "2\\(\\) \\{");
+            inputSink.write(ENTER);
+            waitOutput(out, "^\n\u0006");
+            inputSink.write(UP);
+            waitOutput(out, "^}");
+            inputSink.write(UP);
+            waitOutput(out, "^" + clearOut("}") + "System.err.println\\(2\\);");
+            inputSink.write(UP);
+            waitOutput(out, "^" + clearOut("System.err.println(2);") + "void test2\\(\\) \\{");
+            inputSink.write(UP);
+            waitOutput(out, "^\u0007");
+            inputSink.write(DOWN);
+            waitOutput(out, "^" + clearOut("void test2() {") + "System.err.println\\(2\\);");
+            inputSink.write(DOWN);
+            waitOutput(out, "^" + clearOut("System.err.println(2);") + "}");
+            inputSink.write(DOWN);
+            waitOutput(out, "^" + clearOut("}"));
+            inputSink.write(DOWN);
+            waitOutput(out, "^\u0007");
+        });
+    }
+    //where:
+        private static final String CTRL_UP = "\033[1;5A";
+        private static final String CTRL_DOWN = "\033[1;5B";
+        private static final String UP = "\033[A";
+        private static final String DOWN = "\033[B";
+        private static final String ENTER = "\n";
+
+}
--- a/langtools/test/jdk/jshell/MergedTabShiftTabTest.java	Thu Apr 06 11:55:58 2017 +0200
+++ b/langtools/test/jdk/jshell/MergedTabShiftTabTest.java	Thu Apr 06 16:19:33 2017 +0200
@@ -31,37 +31,29 @@
  *     jdk.jshell/jdk.jshell:open
  * @library /tools/lib
  * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
- * @build Compiler
+ * @build Compiler UITesting
  * @build MergedTabShiftTabTest
  * @run testng MergedTabShiftTabTest
  */
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintStream;
-import java.io.Writer;
 import java.lang.reflect.Field;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.text.MessageFormat;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.Locale;
 import java.util.ResourceBundle;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import jdk.jshell.JShell;
-import jdk.jshell.tool.JavaShellToolBuilder;
 import org.testng.annotations.Test;
 
 @Test
-public class MergedTabShiftTabTest {
+public class MergedTabShiftTabTest extends UITesting {
 
     public void testCommand() throws Exception {
         doRunTest((inputSink, out) -> {
@@ -281,60 +273,6 @@
         });
     }
 
-    private void doRunTest(Test test) throws Exception {
-        PipeInputStream input = new PipeInputStream();
-        StringBuilder out = new StringBuilder();
-        PrintStream outS = new PrintStream(new OutputStream() {
-            @Override public void write(int b) throws IOException {
-                synchronized (out) {
-                    System.out.print((char) b);
-                    out.append((char) b);
-                    out.notifyAll();
-                }
-            }
-        });
-        Thread runner = new Thread(() -> {
-            try {
-                JavaShellToolBuilder.builder()
-                        .in(input, input)
-                        .out(outS)
-                        .err(outS)
-                        .promptCapture(true)
-                        .persistence(new HashMap<>())
-                        .locale(Locale.US)
-                        .run("--no-startup");
-            } catch (Exception ex) {
-                throw new IllegalStateException(ex);
-            }
-        });
-
-        Writer inputSink = new OutputStreamWriter(input.createOutput()) {
-            @Override
-            public void write(String str) throws IOException {
-                super.write(str);
-                flush();
-            }
-        };
-
-        runner.start();
-
-        try {
-            waitOutput(out, "\u0005");
-            test.test(inputSink, out);
-        } finally {
-            inputSink.write("\003\003/exit");
-
-            runner.join(1000);
-            if (runner.isAlive()) {
-                runner.stop();
-            }
-        }
-    }
-
-    interface Test {
-        public void test(Writer inputSink, StringBuilder out) throws Exception;
-    }
-
     private Path prepareZip() {
         String clazz1 =
                 "package jshelltest;\n" +
@@ -404,162 +342,4 @@
         return MessageFormat.format(resources.getString(key), args);
     }
 
-    private static final long TIMEOUT;
-
-    static {
-        long factor;
-
-        try {
-            factor = (long) Double.parseDouble(System.getProperty("test.timeout.factor", "1"));
-        } catch (NumberFormatException ex) {
-            factor = 1;
-        }
-        TIMEOUT = 60_000 * factor;
-    }
-
-    private void waitOutput(StringBuilder out, String expected) {
-        expected = expected.replaceAll("\n", System.getProperty("line.separator"));
-        Pattern expectedPattern = Pattern.compile(expected, Pattern.DOTALL);
-        synchronized (out) {
-            long s = System.currentTimeMillis();
-
-            while (true) {
-                Matcher m = expectedPattern.matcher(out);
-                if (m.find()) {
-                    out.delete(0, m.end() + 1);
-                    return ;
-                }
-                long e =  System.currentTimeMillis();
-                if ((e - s) > TIMEOUT) {
-                    throw new IllegalStateException("Timeout waiting for: " + quote(expected) + ", actual output so far: " + quote(out.toString()));
-                }
-                try {
-                    out.wait(TIMEOUT);
-                } catch (InterruptedException ex) {
-                    ex.printStackTrace();
-                }
-            }
-        }
-    }
-
-    private String quote(String original) {
-        StringBuilder output = new StringBuilder();
-
-        for (char c : original.toCharArray()) {
-            if (c < 32) {
-                output.append(String.format("\\u%04X", (int) c));
-            } else {
-                output.append(c);
-            }
-        }
-
-        return output.toString();
-    }
-
-    private static class PipeInputStream extends InputStream {
-
-        private static final int INITIAL_SIZE = 128;
-        private int[] buffer = new int[INITIAL_SIZE];
-        private int start;
-        private int end;
-        private boolean closed;
-
-        @Override
-        public synchronized int read() throws IOException {
-            if (start == end && !closed) {
-                inputNeeded();
-            }
-            while (start == end) {
-                if (closed) {
-                    return -1;
-                }
-                try {
-                    wait();
-                } catch (InterruptedException ex) {
-                    //ignore
-                }
-            }
-            try {
-                return buffer[start];
-            } finally {
-                start = (start + 1) % buffer.length;
-            }
-        }
-
-        @Override
-        public synchronized int read(byte[] b, int off, int len) throws IOException {
-            if (b == null) {
-                throw new NullPointerException();
-            } else if (off < 0 || len < 0 || len > b.length - off) {
-                throw new IndexOutOfBoundsException();
-            } else if (len == 0) {
-                return 0;
-            }
-
-            int c = read();
-            if (c == -1) {
-                return -1;
-            }
-            b[off] = (byte)c;
-
-            int totalRead = 1;
-            while (totalRead < len && start != end) {
-                int r = read();
-                if (r == (-1))
-                    break;
-                b[off + totalRead++] = (byte) r;
-            }
-            return totalRead;
-        }
-
-        protected void inputNeeded() throws IOException {}
-
-        private synchronized void write(int b) {
-            if (closed) {
-                throw new IllegalStateException("Already closed.");
-            }
-            int newEnd = (end + 1) % buffer.length;
-            if (newEnd == start) {
-                //overflow:
-                int[] newBuffer = new int[buffer.length * 2];
-                int rightPart = (end > start ? end : buffer.length) - start;
-                int leftPart = end > start ? 0 : start - 1;
-                System.arraycopy(buffer, start, newBuffer, 0, rightPart);
-                System.arraycopy(buffer, 0, newBuffer, rightPart, leftPart);
-                buffer = newBuffer;
-                start = 0;
-                end = rightPart + leftPart;
-                newEnd = end + 1;
-            }
-            buffer[end] = b;
-            end = newEnd;
-            notifyAll();
-        }
-
-        @Override
-        public synchronized void close() {
-            closed = true;
-            notifyAll();
-        }
-
-        public OutputStream createOutput() {
-            return new OutputStream() {
-                @Override public void write(int b) throws IOException {
-                    PipeInputStream.this.write(b);
-                }
-                @Override
-                public void write(byte[] b, int off, int len) throws IOException {
-                    for (int i = 0 ; i < len ; i++) {
-                        write(Byte.toUnsignedInt(b[off + i]));
-                    }
-                }
-                @Override
-                public void close() throws IOException {
-                    PipeInputStream.this.close();
-                }
-            };
-        }
-
-    }
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/UITesting.java	Thu Apr 06 16:19:33 2017 +0200
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jdk.jshell.tool.JavaShellToolBuilder;
+
+public class UITesting {
+
+    protected void doRunTest(Test test) throws Exception {
+        PipeInputStream input = new PipeInputStream();
+        StringBuilder out = new StringBuilder();
+        PrintStream outS = new PrintStream(new OutputStream() {
+            @Override public void write(int b) throws IOException {
+                synchronized (out) {
+                    System.out.print((char) b);
+                    out.append((char) b);
+                    out.notifyAll();
+                }
+            }
+        });
+        Thread runner = new Thread(() -> {
+            try {
+                JavaShellToolBuilder.builder()
+                        .in(input, input)
+                        .out(outS)
+                        .err(outS)
+                        .promptCapture(true)
+                        .persistence(new HashMap<>())
+                        .locale(Locale.US)
+                        .run("--no-startup");
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        });
+
+        Writer inputSink = new OutputStreamWriter(input.createOutput()) {
+            @Override
+            public void write(String str) throws IOException {
+                super.write(str);
+                flush();
+            }
+        };
+
+        runner.start();
+
+        try {
+            waitOutput(out, "\u0005");
+            test.test(inputSink, out);
+        } finally {
+            inputSink.write("\003\003/exit");
+
+            runner.join(1000);
+            if (runner.isAlive()) {
+                runner.stop();
+            }
+        }
+    }
+
+    protected interface Test {
+        public void test(Writer inputSink, StringBuilder out) throws Exception;
+    }
+
+    private static final long TIMEOUT;
+
+    static {
+        long factor;
+
+        try {
+            factor = (long) Double.parseDouble(System.getProperty("test.timeout.factor", "1"));
+        } catch (NumberFormatException ex) {
+            factor = 1;
+        }
+        TIMEOUT = 60_000 * factor;
+    }
+
+    protected void waitOutput(StringBuilder out, String expected) {
+        expected = expected.replaceAll("\n", System.getProperty("line.separator"));
+        Pattern expectedPattern = Pattern.compile(expected, Pattern.DOTALL);
+        synchronized (out) {
+            long s = System.currentTimeMillis();
+
+            while (true) {
+                Matcher m = expectedPattern.matcher(out);
+                if (m.find()) {
+                    out.delete(0, m.end() + 1);
+                    return ;
+                }
+                long e =  System.currentTimeMillis();
+                if ((e - s) > TIMEOUT) {
+                    throw new IllegalStateException("Timeout waiting for: " + quote(expected) + ", actual output so far: " + quote(out.toString()));
+                }
+                try {
+                    out.wait(TIMEOUT);
+                } catch (InterruptedException ex) {
+                    ex.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private String quote(String original) {
+        StringBuilder output = new StringBuilder();
+
+        for (char c : original.toCharArray()) {
+            if (c < 32) {
+                output.append(String.format("\\u%04X", (int) c));
+            } else {
+                output.append(c);
+            }
+        }
+
+        return output.toString();
+    }
+
+    protected String clearOut(String what) {
+        return backspace(what.length()) + space(what.length()) + backspace(what.length());
+    }
+
+    protected String backspace(int n) {
+        return fill(n, '\010');
+    }
+
+    protected String space(int n) {
+        return fill(n, ' ');
+    }
+
+    private String fill(int n, char c) {
+        StringBuilder result = new StringBuilder(n);
+
+        while (n-- > 0)
+            result.append(c);
+
+        return result.toString();
+    }
+
+    private static class PipeInputStream extends InputStream {
+
+        private static final int INITIAL_SIZE = 128;
+        private int[] buffer = new int[INITIAL_SIZE];
+        private int start;
+        private int end;
+        private boolean closed;
+
+        @Override
+        public synchronized int read() throws IOException {
+            if (start == end && !closed) {
+                inputNeeded();
+            }
+            while (start == end) {
+                if (closed) {
+                    return -1;
+                }
+                try {
+                    wait();
+                } catch (InterruptedException ex) {
+                    //ignore
+                }
+            }
+            try {
+                return buffer[start];
+            } finally {
+                start = (start + 1) % buffer.length;
+            }
+        }
+
+        @Override
+        public synchronized int read(byte[] b, int off, int len) throws IOException {
+            if (b == null) {
+                throw new NullPointerException();
+            } else if (off < 0 || len < 0 || len > b.length - off) {
+                throw new IndexOutOfBoundsException();
+            } else if (len == 0) {
+                return 0;
+            }
+
+            int c = read();
+            if (c == -1) {
+                return -1;
+            }
+            b[off] = (byte)c;
+
+            int totalRead = 1;
+            while (totalRead < len && start != end) {
+                int r = read();
+                if (r == (-1))
+                    break;
+                b[off + totalRead++] = (byte) r;
+            }
+            return totalRead;
+        }
+
+        protected void inputNeeded() throws IOException {}
+
+        private synchronized void write(int b) {
+            if (closed) {
+                throw new IllegalStateException("Already closed.");
+            }
+            int newEnd = (end + 1) % buffer.length;
+            if (newEnd == start) {
+                //overflow:
+                int[] newBuffer = new int[buffer.length * 2];
+                int rightPart = (end > start ? end : buffer.length) - start;
+                int leftPart = end > start ? 0 : start - 1;
+                System.arraycopy(buffer, start, newBuffer, 0, rightPart);
+                System.arraycopy(buffer, 0, newBuffer, rightPart, leftPart);
+                buffer = newBuffer;
+                start = 0;
+                end = rightPart + leftPart;
+                newEnd = end + 1;
+            }
+            buffer[end] = b;
+            end = newEnd;
+            notifyAll();
+        }
+
+        @Override
+        public synchronized void close() {
+            closed = true;
+            notifyAll();
+        }
+
+        public OutputStream createOutput() {
+            return new OutputStream() {
+                @Override public void write(int b) throws IOException {
+                    PipeInputStream.this.write(b);
+                }
+                @Override
+                public void write(byte[] b, int off, int len) throws IOException {
+                    for (int i = 0 ; i < len ; i++) {
+                        write(Byte.toUnsignedInt(b[off + i]));
+                    }
+                }
+                @Override
+                public void close() throws IOException {
+                    PipeInputStream.this.close();
+                }
+            };
+        }
+
+    }
+
+}