8209109: [TEST] rewrite com/sun/jdi shell tests to java version - step1
authoramenkov
Tue, 14 Aug 2018 11:56:32 -0700
changeset 51402 bfdebb29b1e5
parent 51401 58113ce90caf
child 51403 0f3bfea3acf1
8209109: [TEST] rewrite com/sun/jdi shell tests to java version - step1 Reviewed-by: sspitsyn, jcbeyler
test/jdk/com/sun/jdi/ArrayLengthDumpTest.java
test/jdk/com/sun/jdi/ArrayLengthDumpTest.sh
test/jdk/com/sun/jdi/BreakpointWithFullGC.java
test/jdk/com/sun/jdi/BreakpointWithFullGC.sh
test/jdk/com/sun/jdi/lib/jdb/Jdb.java
test/jdk/com/sun/jdi/lib/jdb/JdbCommand.java
test/jdk/com/sun/jdi/lib/jdb/JdbTest.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/ArrayLengthDumpTest.java	Tue Aug 14 11:56:32 2018 -0700
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2002, 2018, 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 4422141 4695338
+ * @summary TTY: .length field for arrays in print statements in jdb not recognized
+ *          TTY: dump <ArrayReference> command not implemented.
+ * @comment converted from test/jdk/com/sun/jdi/ArrayLengthDumpTest.sh
+ *
+ * @library /test/lib
+ * @build ArrayLengthDumpTest
+ * @run main/othervm ArrayLengthDumpTest
+ */
+
+import lib.jdb.JdbCommand;
+import lib.jdb.JdbTest;
+import jdk.test.lib.process.OutputAnalyzer;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+class ArrayLengthDumpTarg {
+    static final int [] i = {0,1,2,3,4,5,6};
+    String [] s = {"zero", "one", "two", "three", "four"};
+    String [][] t = {s, s, s, s, s, s, s, s, s, s, s};
+    int length = 5;
+
+    private void bar() {
+    }
+
+    private void foo() {
+        ArrayLengthDumpTarg u[] = { new ArrayLengthDumpTarg(),
+                                    new ArrayLengthDumpTarg(),
+                                    new ArrayLengthDumpTarg(),
+                                    new ArrayLengthDumpTarg(),
+                                    new ArrayLengthDumpTarg(),
+                                    new ArrayLengthDumpTarg() };
+        int k = u.length;
+        System.out.println("        u.length is: " + k);
+        k = this.s.length;
+        System.out.println("   this.s.length is: " + k);
+        k = this.t.length;
+        System.out.println("   this.t.length is: " + k);
+        k = this.t[1].length;
+        System.out.println("this.t[1].length is: " + k);
+        k = i.length;
+        System.out.println("        i.length is: " + k);
+        bar();                      // @1 breakpoint
+    }
+
+    public static void main(String[] args) {
+        ArrayLengthDumpTarg my = new ArrayLengthDumpTarg();
+        my.foo();
+    }
+}
+
+public class ArrayLengthDumpTest extends JdbTest {
+    public static void main(String argv[]) {
+        new ArrayLengthDumpTest().run();
+    }
+
+    public ArrayLengthDumpTest() {
+        super(DEBUGGEE_CLASS);
+    }
+
+    private static final String DEBUGGEE_CLASS = ArrayLengthDumpTarg.class.getName();
+
+    @Override
+    protected void runCases() {
+        setBreakpoints(System.getProperty("test.src") + "/ArrayLengthDumpTest.java", 1);
+
+        // Run to breakpoint #1
+        jdb.command(JdbCommand.run());
+
+        List<String> reply = new LinkedList<>();
+        reply.addAll(jdb.command(JdbCommand.dump("this")));
+        reply.addAll(jdb.command(JdbCommand.dump("this.s.length")));
+        reply.addAll(jdb.command(JdbCommand.dump("this.s")));
+        reply.addAll(jdb.command(JdbCommand.dump("this.t.length")));
+        reply.addAll(jdb.command(JdbCommand.dump("this.t[1].length")));
+        reply.addAll(jdb.command(JdbCommand.dump("ArrayLengthDumpTarg.i.length")));
+        reply.addAll(jdb.command(JdbCommand.dump("this.length")));
+
+        new OutputAnalyzer(reply.stream().collect(Collectors.joining(lineSeparator)))
+                // Test the fix for 4690242:
+                .shouldNotContain("No instance field or method with the name length in")
+                .shouldNotContain("No static field or method with the name length")
+                // Test the fix for 4695338:
+                .shouldContain("\"zero\", \"one\", \"two\", \"three\", \"four\"");
+
+        jdb.contToExit(1);
+    }
+
+}
--- a/test/jdk/com/sun/jdi/ArrayLengthDumpTest.sh	Tue Aug 14 13:16:26 2018 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright (c) 2002, 2014, 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 4422141 4695338
-#  @summary TTY: .length field for arrays in print statements in jdb not recognized
-#           TTY: dump <ArrayReference> command not implemented.
-#  @author Tim Bell
-#
-#  @key intermittent
-#  @run shell ArrayLengthDumpTest.sh
-#
-classname=ArrayLengthDumpTarg
-
-createJavaFile()
-{
-    cat <<EOF > $classname.java.1
-class $classname {
-    static final int [] i = {0,1,2,3,4,5,6};
-    String [] s = {"zero", "one", "two", "three", "four"};
-    String [][] t = {s, s, s, s, s, s, s, s, s, s, s};
-    int length = 5;
-
-    public void bar() {
-    }
-
-    public void foo() {
-        ArrayLengthDumpTarg u[] = { new ArrayLengthDumpTarg(),
-                                    new ArrayLengthDumpTarg(),
-                                    new ArrayLengthDumpTarg(),
-                                    new ArrayLengthDumpTarg(),
-                                    new ArrayLengthDumpTarg(),
-                                    new ArrayLengthDumpTarg() };
-        int k = u.length;
-        System.out.println("        u.length is: " + k);
-        k = this.s.length;
-        System.out.println("   this.s.length is: " + k);
-        k = this.t.length;
-        System.out.println("   this.t.length is: " + k);
-        k = this.t[1].length;
-        System.out.println("this.t[1].length is: " + k);
-        k = i.length;
-        System.out.println("        i.length is: " + k);
-        bar();                      // @1 breakpoint
-    }
-
-    public static void main(String[] args) {
-        ArrayLengthDumpTarg my = new ArrayLengthDumpTarg();
-        my.foo();
-    }
-}
-EOF
-}
-
-# This is called to feed cmds to jdb.
-dojdbCmds()
-{
-   setBkpts @1
-   runToBkpt @1
-   cmd dump this
-   cmd dump this.s.length
-   cmd dump this.s
-   cmd dump this.t.length
-   cmd dump this.t[1].length
-   cmd dump ArrayLengthDumpTarg.i.length
-   cmd dump this.length
-   cmd allowExit cont
-}
-
-mysetup()
-{
-    if [ -z "$TESTSRC" ] ; then
-        TESTSRC=.
-    fi
-
-    for ii in . $TESTSRC $TESTSRC/.. ; do
-        if [ -r "$ii/ShellScaffold.sh" ] ; then
-            . $ii/ShellScaffold.sh
-            break
-        fi
-    done
-}
-
-
-# You could replace this next line with the contents
-# of ShellScaffold.sh and this script will run just the same.
-mysetup
-
-runit
-#
-# Test the fix for 4690242:
-#
-jdbFailIfPresent "No instance field or method with the name length in" 50
-jdbFailIfPresent "No static field or method with the name length" 50
-#
-# Test the fix for 4695338:
-#
-jdbFailIfNotPresent "\"zero\", \"one\", \"two\", \"three\", \"four\"" 50
-pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/BreakpointWithFullGC.java	Tue Aug 14 11:56:32 2018 -0700
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009, 2018, 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 6862295
+ * @summary Verify breakpoints still work after a full GC.
+ * @comment converted from test/jdk/com/sun/jdi/BreakpointWithFullGC.sh
+ *
+ * @library /test/lib
+ * @compile -g BreakpointWithFullGC.java
+ * @run main/othervm BreakpointWithFullGC
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import lib.jdb.Jdb;
+import lib.jdb.JdbCommand;
+import lib.jdb.JdbTest;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+class BreakpointWithFullGCTarg {
+    public static List<Object> objList = new ArrayList<>();
+
+    private static void init(int numObjs) {
+        for (int i = 0; i < numObjs; i++) {
+            objList.add(new Object());
+        }
+    }
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 10; i++) {
+            System.out.println("top of loop");     // @1 breakpoint
+            init(500000);
+            objList.clear();
+            System.gc();
+            System.out.println("bottom of loop");  // @1 breakpoint
+        }
+        System.out.println("end of test");         // @1 breakpoint
+    }
+}
+
+public class BreakpointWithFullGC extends JdbTest {
+    public static void main(String argv[]) {
+        new BreakpointWithFullGC().run();
+    }
+
+    public BreakpointWithFullGC() {
+        super(new Jdb.LaunchOptions(DEBUGGEE_CLASS)
+                     .debuggeeOptions(DEBUGGEE_OPTIONS));
+    }
+
+    private static final String DEBUGGEE_CLASS = BreakpointWithFullGCTarg.class.getName();
+    // Hijacking the mode parameter to make sure we use a small amount
+    // of memory and can see what GC is doing.
+    private static final String DEBUGGEE_OPTIONS = "-Xmx32m -verbose:gc";
+
+    @Override
+    protected void runCases() {
+        setBreakpoints(System.getProperty("test.src") + "/BreakpointWithFullGC.java", 1);
+
+        List<String> reply = new LinkedList<>();
+
+        // get to the first loop breakpoint
+        reply.addAll(jdb.command(JdbCommand.run()));
+        // 19 "cont" commands gets us through all the loop breakpoints.
+        for (int i = 1; i <= 19; i++) {
+            reply.addAll(jdb.command(JdbCommand.cont()));
+        }
+        // get to the last breakpoint
+        reply.addAll(jdb.command(JdbCommand.cont()));
+
+        new OutputAnalyzer(reply.stream().collect(Collectors.joining(lineSeparator)))
+                // make sure we hit the first breakpoint at least once
+                .stdoutShouldMatch("System\\..*top of loop")
+                // make sure we hit the second breakpoint at least once
+                .stdoutShouldMatch("System\\..*bottom of loop")
+                // make sure we hit the last breakpoint
+                .stdoutShouldMatch("System\\..*end of test")
+                // make sure we had at least one full GC
+                // Prior to JDK9-B95, the pattern was 'Full GC'
+                .stdoutShouldContain("Pause Full (System.gc())")
+                // check for error message due to thread ID change
+                .stderrShouldNotContain("Exception in thread \"event-handler\" java.lang.NullPointerException");
+
+        jdb.contToExit(1);
+    }
+}
--- a/test/jdk/com/sun/jdi/BreakpointWithFullGC.sh	Tue Aug 14 13:16:26 2018 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright (c) 2009, 2013, 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 6862295
-#  @summary Verify breakpoints still work after a full GC.
-#  @author dcubed (based on the test program posted to the following
-#  Eclipse thread https://bugs.eclipse.org/bugs/show_bug.cgi?id=279137)
-#
-#  @key intermittent
-#  @run shell BreakpointWithFullGC.sh
-
-compileOptions=-g
-# Hijacking the mode parameter to make sure we use a small amount
-# of memory and can see what GC is doing.
-mode="-Xmx32m -verbose:gc"
-# Force use of a GC framework collector to see the original failure.
-#mode="$mode -XX:+UseSerialGC"
-
-# Uncomment this to see the JDI trace
-#jdbOptions=-dbgtrace
-
-createJavaFile()
-{
-    cat <<EOF > $1.java.1
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class $1 {
-    public static List<Object> objList = new ArrayList<Object>();
-
-    private static void init(int numObjs) {
-        for (int i = 0; i < numObjs; i++) {
-            objList.add(new Object());
-        }
-    }
-
-    public static void main(String[] args) {
-        for (int i = 0; i < 10; i++) {
-            System.out.println("top of loop");     // @1 breakpoint
-            init(500000);
-            objList.clear();
-            System.gc();
-            System.out.println("bottom of loop");  // @1 breakpoint
-        }
-        System.out.println("end of test");         // @1 breakpoint
-    }
-}
-
-EOF
-}
-
-# This is called to feed cmds to jdb.
-dojdbCmds()
-{
-    setBkpts @1
-
-    # get to the first loop breakpoint
-    runToBkpt
-    # 19 "cont" commands gets us through all the loop breakpoints.
-    # Use for-loop instead of while-loop to avoid creating processes
-    # for '[' and 'expr'.
-    for ii in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19; do
-        contToBkpt
-    done
-    # get to the last breakpoint
-    contToBkpt
-}
-
-
-mysetup()
-{
-    if [ -z "$TESTSRC" ] ; then
-        TESTSRC=.
-    fi
-
-    for ii in . $TESTSRC $TESTSRC/.. ; do
-        if [ -r "$ii/ShellScaffold.sh" ] ; then
-            . $ii/ShellScaffold.sh
-            break
-        fi
-    done
-}
-
-# You could replace this next line with the contents
-# of ShellScaffold.sh and this script will run just the same.
-mysetup
-
-runit
-
-# make sure we hit the first breakpoint at least once
-jdbFailIfNotPresent 'System\..*top of loop'
-
-# make sure we hit the second breakpoint at least once
-jdbFailIfNotPresent 'System\..*bottom of loop'
-
-# make sure we hit the last breakpoint
-jdbFailIfNotPresent 'System\..*end of test'
-
-# make sure we had at least one full GC
-# Prior to JDK9-B95, the pattern was 'Full GC'
-debuggeeMatchRegexp '^.*?\bPause Full\b\(System.gc\(\)\)\b.*?$'
-
-# check for error message due to thread ID change
-debuggeeFailIfPresent \
-    'Exception in thread "event-handler" java.lang.NullPointerException'
-
-pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/lib/jdb/Jdb.java	Tue Aug 14 11:56:32 2018 -0700
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2018, 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.
+ */
+
+package lib.jdb;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.process.StreamPumper;
+
+public class Jdb {
+
+    public static class LaunchOptions {
+        public final String debuggeeClass;
+        public String debuggeeOptions;
+
+        public LaunchOptions(String debuggeeClass) {
+            this.debuggeeClass = debuggeeClass;
+        }
+        public LaunchOptions debuggeeOptions(String options) {
+            debuggeeOptions = options;
+            return this;
+        }
+    }
+
+    public static Jdb launchLocal(LaunchOptions options) {
+        StringBuilder connOpts = new StringBuilder("com.sun.jdi.CommandLineLaunch:");
+        if (options.debuggeeOptions != null) {
+            connOpts.append("options=")
+                    .append(options.debuggeeOptions)
+                    .append(",");
+        }
+        connOpts.append("main=")
+                .append(options.debuggeeClass);
+
+        return new Jdb("-connect", connOpts.toString());
+    }
+
+    public static Jdb launchLocal(String debuggeeClass) {
+        return new Jdb("-connect", "com.sun.jdi.CommandLineLaunch:main=" + debuggeeClass);
+    }
+
+    public Jdb(String... args) {
+        ProcessBuilder pb = new ProcessBuilder(JDKToolFinder.getTestJDKTool("jdb"));
+        pb.command().addAll(Arrays.asList(args));
+        System.out.println("Launching jdb:" + pb.command().stream().collect(Collectors.joining(" ")));
+        try {
+            p = pb.start();
+        } catch (IOException ex) {
+            throw new RuntimeException("failed to launch pdb", ex);
+        }
+        StreamPumper stdout = new StreamPumper(p.getInputStream());
+        StreamPumper stderr = new StreamPumper(p.getErrorStream());
+
+        stdout.addPump(new StreamPumper.StreamPump(outputHandler));
+        stderr.addPump(new StreamPumper.StreamPump(outputHandler));
+
+        stdout.process();
+        stderr.process();
+
+        inputWriter = new PrintWriter(p.getOutputStream(), true);
+    }
+
+    private final Process p;
+    private final OutputHandler outputHandler = new OutputHandler();
+    private final PrintWriter inputWriter;
+
+    private static final String lineSeparator = System.getProperty("line.separator");
+    // wait time before check jdb output (in ms)
+    private static long sleepTime = 1000;
+    // max time to wait for  jdb output (in ms)
+    private static long timeout = 60000;
+
+    // jdb prompt when debuggee is not started nor suspended after breakpoint
+    public static final String SIMPLE_PROMPT = "> ";
+    // pattern for message of a breakpoint hit
+    public static final String BREAKPOINT_HIT = "Breakpoint hit:";
+    // pattern for message of an application exit
+    public static final String APPLICATION_EXIT = "The application exited";
+    // pattern for message of an application disconnect
+    public static final String APPLICATION_DISCONNECTED = "The application has been disconnected";
+
+
+    // Check whether the process has been terminated
+    public boolean terminated() {
+        try {
+            p.exitValue();
+            return true;
+        } catch (IllegalThreadStateException e) {
+            return false;
+        }
+    }
+
+    // waits until the process shutdown or crash
+    public boolean waitFor (long timeout, TimeUnit unit) {
+        try {
+            return p.waitFor(timeout, unit);
+        } catch (InterruptedException e) {
+            return false;
+        }
+    }
+
+    // ugly cleanup
+    public void terminate() {
+        p.destroy();
+    }
+
+
+    // waits until string {@pattern} appears in the jdb output, within the last {@code lines} lines.
+    /* Comment from original /test/jdk/com/sun/jdi/ShellScaffold.sh
+        # Now we have to wait for the next jdb prompt.  We wait for a pattern
+        # to appear in the last line of jdb output.  Normally, the prompt is
+        #
+        # 1) ^main[89] @
+        #
+        # where ^ means start of line, and @ means end of file with no end of line
+        # and 89 is the current command counter. But we have complications e.g.,
+        # the following jdb output can appear:
+        #
+        # 2) a[89] = 10
+        #
+        # The above form is an array assignment and not a prompt.
+        #
+        # 3) ^main[89] main[89] ...
+        #
+        # This occurs if the next cmd is one that causes no jdb output, e.g.,
+        # 'trace methods'.
+        #
+        # 4) ^main[89] [main[89]] .... > @
+        #
+        # jdb prints a > as a prompt after something like a cont.
+        # Thus, even though the above is the last 'line' in the file, it
+        # isn't the next prompt we are waiting for after the cont completes.
+        # HOWEVER, sometimes we see this for a cont command:
+        #
+        #   ^main[89] $
+        #      <lines output for hitting a bkpt>
+        #
+        # 5) ^main[89] > @
+        #
+        # i.e., the > prompt comes out AFTER the prompt we we need to wait for.
+    */
+    public List<String> waitForMsg(String pattern, boolean allowSimplePrompt, int lines, boolean allowExit) {
+        long startTime = System.currentTimeMillis();
+        while (System.currentTimeMillis() - startTime < timeout) {
+            try {
+                Thread.sleep(sleepTime);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+            synchronized (outputHandler) {
+                if (!outputHandler.updated()) {
+                    try {
+                        outputHandler.wait(sleepTime);
+                    } catch (InterruptedException e) {
+                        // ignore
+                    }
+                } else {
+                    // if something appeared in the jdb output, reset the timeout
+                    startTime = System.currentTimeMillis();
+                }
+            }
+            List<String> reply = outputHandler.get();
+            for (String line: reply.subList(Math.max(0, reply.size() - lines), reply.size())) {
+                if (line.matches(pattern) || (allowSimplePrompt && line.contains(SIMPLE_PROMPT))) {
+                    logJdb(reply);
+                    return outputHandler.reset();
+                }
+            }
+            if (terminated()) {
+                logJdb(outputHandler.get());
+                if (!allowExit) {
+                    throw new RuntimeException("waitForMsg timed out after " + (timeout/1000)
+                            + " seconds, looking for '" + pattern + "', in " + lines + " lines");
+                }
+                return new ArrayList<>(reply);
+            }
+        }
+        // timeout
+        logJdb(outputHandler.get());
+        throw new RuntimeException("waitForMsg timed out after " + (timeout/1000)
+                + " seconds, looking for '" + pattern + "', in " + lines + " lines");
+    }
+
+    //
+    public List<String> waitForSimplePrompt() {
+        return waitForMsg(SIMPLE_PROMPT, true, 1, false);
+    }
+
+    public List<String> command(JdbCommand cmd) {
+        if (terminated()) {
+            if (cmd.allowExit) {
+                // return remaining output
+                return outputHandler.reset();
+            }
+            throw new RuntimeException("Attempt to send command '" + cmd.cmd + "' to terminated jdb");
+        }
+
+        System.out.println("> " + cmd.cmd);
+
+        inputWriter.println(cmd.cmd);
+
+        if (inputWriter.checkError()) {
+            throw new RuntimeException("Unexpected IO error while writing command '" + cmd.cmd + "' to jdb stdin stream");
+        }
+
+        return waitForMsg("[a-zA-Z0-9_-][a-zA-Z0-9_-]*\\[[1-9][0-9]*\\] [ >]*$", cmd.allowSimplePrompt, 1, cmd.allowExit);
+    }
+
+    public List<String> command(String cmd) {
+        return command(new JdbCommand(cmd));
+    }
+
+    // sends "cont" command up to maxTimes until debuggee exit
+    public void contToExit(int maxTimes) {
+        boolean exited = false;
+        JdbCommand cont = JdbCommand.cont().allowExit();
+        for (int i = 0; i < maxTimes && !terminated(); i++) {
+            String reply = command(cont).stream().collect(Collectors.joining(lineSeparator));
+            if (reply.contains(APPLICATION_EXIT)) {
+                exited = true;
+                break;
+            }
+        }
+        if (!exited && !terminated()) {
+            throw new RuntimeException("Debuggee did not exit after " + maxTimes + " <cont> commands");
+        }
+    }
+
+    // quits jdb by using "quit" command
+    public void quit() {
+        command(JdbCommand.quit());
+    }
+
+    private void log(String s) {
+        System.out.println(s);
+    }
+
+    private void logJdb(List<String> reply) {
+        reply.forEach(s -> System.out.println("[jdb] " + s));
+    }
+
+    // handler for out/err of the pdb process
+    private class OutputHandler extends OutputStream {
+        // there are 2 buffers:
+        // outStream - data from the process stdout/stderr after last get() call
+        // cachedData - data collected at get(), cleared by reset()
+
+        private final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+        // if the last line in the reply had EOL, the list's last element is empty
+        private final List<String> cachedData = new ArrayList<>();
+
+        @Override
+        public synchronized void write(int b) throws IOException {
+            outStream.write((byte)(b & 0xFF));
+            notifyAll();
+        }
+        @Override
+        public synchronized void write(byte b[], int off, int len) throws IOException {
+            outStream.write(b, off, len);
+            notifyAll();
+        }
+
+        // gets output after the last {@ reset}.
+        // returned data becomes invalid after {@reset}.
+        public synchronized List<String> get() {
+            if (updated()) {
+                String[] newLines = outStream.toString().split(lineSeparator);
+                if (!cachedData.isEmpty()) {
+                    // concat the last line if previous data had no EOL
+                    newLines[0] = cachedData.remove(cachedData.size()-1) + newLines[0];
+                }
+                cachedData.addAll(Arrays.asList(newLines));
+                outStream.reset();
+            }
+            return Collections.unmodifiableList(cachedData);
+        }
+
+        // clears last replay (does not touch replyStream)
+        // returns list as the last get()
+        public synchronized List<String> reset() {
+            List<String> result = new ArrayList<>(cachedData);
+            cachedData.clear();
+            return result;
+        }
+
+        // tests if there are some new data after the last lastReply() call
+        public synchronized boolean updated() {
+            return outStream.size() > 0;
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/lib/jdb/JdbCommand.java	Tue Aug 14 11:56:32 2018 -0700
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2002, 2018, 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.
+ */
+
+package lib.jdb;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+/**
+ * Represents list of commands of <code>jdb</code> from JDK1.4:
+ *
+ *   run [class [args]]        -- start execution of application's main class
+ *
+ *   threads [threadgroup]     -- list threads
+ *   thread <thread id>        -- set default thread
+ *   suspend [thread id(s)]    -- suspend threads (default: all)
+ *   resume [thread id(s)]     -- resume threads (default: all)
+ *   where [thread id] | all   -- dump a thread's stack
+ *   wherei [thread id] | all  -- dump a thread's stack, with pc info
+ *   up [n frames]             -- move up a thread's stack
+ *   down [n frames]           -- move down a thread's stack
+ *   kill <thread> <expr>      -- kill a thread with the given exception object
+ *   interrupt <thread>        -- interrupt a thread
+ *
+ *   print <expr>              -- print value of expression
+ *   dump <expr>               -- print all object information
+ *   eval <expr>               -- evaluate expression (same as print)
+ *   set <lvalue> = <expr>     -- assign new value to field/variable/array element
+ *   locals                    -- print all local variables in current stack frame
+ *
+ *   classes                   -- list currently known classes
+ *   class <class id>          -- show details of named class
+ *   methods <class id>        -- list a class's methods
+ *   fields <class id>         -- list a class's fields
+ *
+ *   threadgroups              -- list threadgroups
+ *   threadgroup <name>        -- set current threadgroup
+ *
+ *   stop in <class id>.<method>[(argument_type,...)]
+ *                             -- set a breakpoint in a method
+ *   stop at <class id>:<line> -- set a breakpoint at a line
+ *   clear <class id>.<method>[(argument_type,...)]
+ *                             -- clear a breakpoint in a method
+ *   clear <class id>:<line>   -- clear a breakpoint at a line
+ *   clear                     -- list breakpoints
+ *   catch <class id>          -- break when specified exception thrown
+ *   ignore <class id>         -- cancel 'catch'  for the specified exception
+ *   watch [access|all] <class id>.<field name>
+ *                             -- watch access/modifications to a field
+ *   unwatch [access|all] <class id>.<field name>
+ *                             -- discontinue watching access/modifications to a field
+ *   trace methods [thread]    -- trace method entry and exit
+ *   untrace methods [thread]  -- stop tracing method entry and exit
+ *   step                      -- execute current line
+ *   step up                   -- execute until the current method returns to its caller
+ *   stepi                     -- execute current instruction
+ *   next                      -- step one line (step OVER calls)
+ *   cont                      -- continue execution from breakpoint
+ *
+ *   list [line number|method] -- print source code
+ *   use (or sourcepath) [source file path]
+ *                             -- display or change the source path
+ *   exclude [class id ... | "none"]
+ *                             -- do not report step or method events for specified classes
+ *   classpath                 -- print classpath info from target VM
+ *
+ *   monitor <command>         -- execute command each time the program stops
+ *   monitor                   -- list monitors
+ *   unmonitor <monitor#>      -- delete a monitor
+ *   read <filename>           -- read and execute a command file
+ *
+ *   lock <expr>               -- print lock info for an object
+ *   threadlocks [thread id]   -- print lock info for a thread
+ *
+ *   pop                       -- pop the stack through and including the current frame
+ *   reenter                   -- same as pop, but current frame is reentered
+ *   redefine <class id> <class file name>
+ *                             -- redefine the code for a class
+ *
+ *   disablegc <expr>          -- prevent garbage collection of an object
+ *   enablegc <expr>           -- permit garbage collection of an object
+ *
+ *   !!                        -- repeat last command
+ *   <n> <command>             -- repeat command n times
+ *   help (or ?)               -- list commands
+ *   version                   -- print version information
+ *   exit (or quit)            -- exit debugger
+ *
+ *   <class id>: full class name with package qualifiers or a
+ *   pattern with a leading or trailing wildcard ('*').
+ *   <thread id>: thread number as reported in the 'threads' command
+ *   <expr>: a Java(tm) Programming Language expression.
+ *   Most common syntax is supported.
+ *
+ *   Startup commands can be placed in either "jdb.ini" or ".jdbrc"
+ *   in user.home or user.dir
+ */
+public class JdbCommand {
+    final String cmd;
+    boolean allowSimplePrompt = false;
+    boolean allowExit = false;
+
+    public JdbCommand(String cmd) {
+        this.cmd = cmd.endsWith(ls) ? cmd.substring(0, cmd.length() - 1) : cmd;
+    }
+
+    public JdbCommand allowSimplePrompt() {
+        allowSimplePrompt = true;
+        return this;
+    }
+
+    public JdbCommand allowExit() {
+        allowExit = true;
+        return this;
+    }
+
+
+    private static final String ls = System.getProperty("line.separator");
+
+    public static JdbCommand run(String ... params) {
+        return new JdbCommand("run " + Arrays.stream(params).collect(Collectors.joining(" ")));
+    }
+    public static JdbCommand cont() {
+        return new JdbCommand("cont");
+    }
+    public static JdbCommand dump(String what) {
+        return new JdbCommand("dump " + what);
+    }
+    public static JdbCommand quit() {
+        // the command suppose jdb terminates
+        return new JdbCommand("quit").allowExit();
+    }
+    public static JdbCommand stopAt(String targetClass, int lineNum) {
+        return new JdbCommand("stop at " + targetClass + ":" + lineNum);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/lib/jdb/JdbTest.java	Tue Aug 14 11:56:32 2018 -0700
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2018, 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.
+ */
+
+package lib.jdb;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+public abstract class JdbTest {
+
+    public JdbTest(Jdb.LaunchOptions jdbOptions) {
+        this.jdbOptions= jdbOptions;
+        debuggeeClass = jdbOptions.debuggeeClass;
+    }
+    public JdbTest(String debuggeeClass) {
+        this(new Jdb.LaunchOptions(debuggeeClass));
+    }
+
+    private final Jdb.LaunchOptions jdbOptions;
+    protected Jdb jdb;
+    protected final String debuggeeClass;   // shortland for jdbOptions.debuggeeClass
+
+    public void run() {
+        try {
+            setup();
+            runCases();
+        } finally {
+            shutdown();
+        }
+    }
+
+    protected void setup() {
+        jdb = Jdb.launchLocal(jdbOptions);
+        // wait while jdb is initialized
+        jdb.waitForSimplePrompt();
+
+    }
+
+    protected abstract void runCases();
+
+    protected void shutdown() {
+        try {
+            if (!jdb.terminated()) {
+                jdb.quit();
+                // wait some time after the command for the process termination
+                jdb.waitFor(10, TimeUnit.SECONDS);
+            }
+        } finally {
+            if (!jdb.terminated()) {
+                jdb.terminate();
+            }
+        }
+    }
+
+    protected static final String lineSeparator = System.getProperty("line.separator");
+
+
+    // Parses the specified source file for "@{id} breakpoint" tags and returns
+    // list of the line numbers containing the tag.
+    // Example:
+    //   System.out.println("BP is here");  // @1 breakpoint
+    public static List<Integer> parseBreakpoints(String filePath, int id) {
+        final String pattern = "@" + id + " breakpoint";
+        int lineNum = 1;
+        List<Integer> result = new LinkedList<>();
+        try {
+            for (String line: Files.readAllLines(Paths.get(filePath))) {
+                if (line.contains(pattern)) {
+                    result.add(lineNum);
+                }
+                lineNum++;
+            }
+        } catch (IOException ex) {
+            throw new RuntimeException("failed to parse " + filePath, ex);
+        }
+        return result;
+    }
+
+    // sets breakpoints to the lines parsed by {@code parseBreakpoints}
+    // returns number of the breakpoints set.
+    public static int setBreakpoints(Jdb jdb, String debuggeeClass, String sourcePath, int id) {
+        List<Integer> bps = parseBreakpoints(sourcePath, id);
+        for (int bp : bps) {
+            // usually we set breakpoints before the debuggee is run, so we allow simple prompt
+            String reply = jdb.command(JdbCommand.stopAt(debuggeeClass, bp).allowSimplePrompt()).stream()
+                    .collect(Collectors.joining("\n"));
+            if (reply.contains("Unable to set")) {
+                throw new RuntimeException("jdb failed to set breakpoint at " + debuggeeClass + ":" + bp);
+            }
+
+        }
+        return bps.size();
+    }
+
+    protected int setBreakpoints(String debuggeeSourcePath, int id) {
+        return setBreakpoints(jdb, debuggeeClass, debuggeeSourcePath, id);
+    }
+
+}