Add tests for DDM 'connected' & 'disconnected' calls

These functions of ChunkHandler need to be called on the first
DDM.Chunk call and the 'disconnected' function is to be called when
the debugger disconnects if the connected function has been called.
This adds verification of this behavior.

Bug: 62821960
Bug: 69169846

Test: ./art/tools/run-jdwp-tests.sh --mode=host
Test: ./art/tools/run-libjdwp-tests.sh --mode=host
Change-Id: Iccb291be76c2553eabf98815703e2b13dccc0c99
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/DDM/DDMDebuggee.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/DDM/DDMDebuggee.java
index 4a10d4d..0fa81e8 100644
--- a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/DDM/DDMDebuggee.java
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/DDM/DDMDebuggee.java
@@ -52,11 +52,14 @@
     }
 
     public class TestChunkHandler extends ChunkHandler {
-        @Override
-        public void connected() {}
+        public int connectCount = 0;
+        public int disconnectCount = 0;
 
         @Override
-        public void disconnected() {}
+        public void connected() { connectCount++; }
+
+        @Override
+        public void disconnected() { disconnectCount++; }
 
         @Override
         public Chunk handleChunk(Chunk request) {
@@ -67,21 +70,38 @@
             byte[] res = calculateExpectedResult(request.data, request.offset, request.length);
             return new Chunk(DDM_RESULT_TYPE, res, 0, res.length);
         }
+
+        public String toString() {
+          return "TestChunkHandler { connectCount = " + connectCount +
+                                  ", disconnectCount = " + disconnectCount + " }";
+        }
     }
 
     @Override
     public void run() {
-        ChunkHandler h = new TestChunkHandler();
+        TestChunkHandler h = new TestChunkHandler();
         DdmServer.registerHandler(DDM_TEST_TYPE, h);
         DdmServer.registrationComplete();
         logWriter.println("-> Added chunk handler type: " + DDM_TEST_TYPE + " handler: " + h);
+
         synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
         synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
         logWriter.println("Removing handler type: " + DDM_TEST_TYPE);
         DdmServer.unregisterHandler(DDM_TEST_TYPE);
+
         synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
         synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
-        logWriter.println("Test complete");
+        // Re-register the chunk handler so we can get the disconnect count.
+        DdmServer.registerHandler(DDM_TEST_TYPE, h);
+
+        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+        // test disposes here.
+        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+
+        // Tell the tester if we saw the connected message.
+        synchronizer.sendMessage(Integer.toString(h.connectCount));
+        synchronizer.sendMessage(Integer.toString(h.disconnectCount));
+        logWriter.println("Test complete with handler: " + h);
     }
 
     /**
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/DDM/DDMTest.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/DDM/DDMTest.java
index a7a862a..05b851d 100644
--- a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/DDM/DDMTest.java
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/DDM/DDMTest.java
@@ -18,6 +18,7 @@
 
 package org.apache.harmony.jpda.tests.jdwp.DDM;
 
+import org.apache.harmony.jpda.tests.framework.TestOptions;
 import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
@@ -27,11 +28,13 @@
 import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
 
 import java.util.Arrays;
+import java.util.Random;
 
 /**
  * JDWP unit test for DDM.Chunk command.
  */
 public class DDMTest extends JDWPSyncTestCase {
+    public static final int REPS = 4;
 
     /**
      * JDWP DDM Command Set constants
@@ -51,11 +54,21 @@
         return "org.apache.harmony.jpda.tests.jdwp.DDM.DDMDebuggee";
     }
 
-    private CommandPacket makeCommand(byte[] test_values) {
+    @Override
+    protected void beforeConnectionSetUp() {
+        settings.setAttachConnectorKind();
+        if (settings.getTransportAddress() == null) {
+            settings.setTransportAddress(TestOptions.DEFAULT_ATTACHING_ADDRESS);
+        }
+        logWriter.println("ATTACH connector kind");
+        super.beforeConnectionSetUp();
+    }
+
+    private CommandPacket makeCommand(int type, byte[] test_values) {
         CommandPacket packet = new CommandPacket(
                 DDMCommandSet.CommandSetID,
                 DDMCommandSet.ChunkCommand);
-        packet.setNextValueAsInt(DDMDebuggee.DDM_TEST_TYPE);
+        packet.setNextValueAsInt(type);
         packet.setNextValueAsInt(test_values.length);
         for (byte b : test_values) {
           packet.setNextValueAsByte(b);
@@ -63,6 +76,10 @@
         return packet;
     }
 
+    private CommandPacket makeCommand(byte[] test_values) {
+        return makeCommand(DDMDebuggee.DDM_TEST_TYPE, test_values);
+    }
+
     /**
      * This testcase exercises DDM.Chunk command.
      * <BR>Starts <A HREF="./DDMDebuggee.html">DDMDebuggee</A> debuggee.
@@ -71,33 +88,91 @@
      */
     public void testChunk001() {
         synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
-        byte[] test_values = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
-        CommandPacket packet = makeCommand(test_values);
-        logWriter.println("Send Chunk message with handler");
+        Random r = new Random();
+        // Send a few messages to make sure that everything works correctly and the connected
+        // message gets sent exactly once.
+        byte[] test_values = new byte[128];
+        for (int rep = 0; rep < REPS; rep++) {
+          r.nextBytes(test_values);
+          CommandPacket packet = makeCommand(test_values);
+          logWriter.println("Send Chunk message with handler");
 
-        byte[] expected = DDMDebuggee.calculateExpectedResult(test_values);
-        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
-        checkReplyPacket(reply, "DDM::Chunk command");
-        int type = reply.getNextValueAsInt();
-        assertEquals("DDM::Chunk returned unexpected type", DDMDebuggee.DDM_RESULT_TYPE, type);
-        int len = reply.getNextValueAsInt();
-        assertEquals("DDM::Chunk returned unexpected amount of data", expected.length, len);
-        byte[] res = new byte[len];
-        for (int i = 0; i < len; i++) {
-          res[i] = reply.getNextValueAsByte();
+          byte[] expected = DDMDebuggee.calculateExpectedResult(test_values);
+          ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
+          checkReplyPacket(reply, "DDM::Chunk command");
+          int type = reply.getNextValueAsInt();
+          assertEquals("DDM::Chunk returned unexpected type", DDMDebuggee.DDM_RESULT_TYPE, type);
+          int len = reply.getNextValueAsInt();
+          assertEquals("DDM::Chunk returned unexpected amount of data", expected.length, len);
+          byte[] res = new byte[len];
+          for (int i = 0; i < len; i++) {
+            res[i] = reply.getNextValueAsByte();
+          }
+          if (!Arrays.equals(expected, res)) {
+            fail("Unexpected different value: expected " + Arrays.toString(expected) + " got " +
+                Arrays.toString(res));
+          }
+          assertAllDataRead(reply);
         }
-        if (!Arrays.equals(expected, res)) {
-          fail("Unexpected different value: expected " + Arrays.toString(expected) + " got " +
-              Arrays.toString(res));
-        }
-        assertAllDataRead(reply);
         synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
         synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
         logWriter.println("Send same message without handler");
-        packet = makeCommand(test_values);
-        reply = debuggeeWrapper.vmMirror.performCommand(packet);
+        CommandPacket packet = makeCommand(test_values);
+        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
         checkReplyPacket(reply, "DDM::Chunk command");
         assertAllDataRead(reply);
+
         synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+
+        // Wait for the debuggee to re-register the handler
+        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+        // Actually disconnect.
+        logWriter.println("");
+        logWriter.println("=> CLOSE CONNECTION");
+        closeConnection();
+        logWriter.println("=> CONNECTION CLOSED");
+
+        logWriter.println("Connecting and disconnecting " + REPS + " times with calls");
+        for (int rep = 0; rep < REPS; rep++) {
+          logWriter.println("");
+          logWriter.println("=> OPEN NEW CONNECTION");
+          openConnection();
+          logWriter.println("=> CONNECTION OPENED");
+
+          logWriter.println("Sending DDM.Chunk command with unused type.");
+          packet = makeCommand(Integer.MAX_VALUE, test_values);
+          reply = debuggeeWrapper.vmMirror.performCommand(packet);
+          checkReplyPacket(reply, "DDM::Chunk command");
+          assertAllDataRead(reply);
+
+          logWriter.println("");
+          logWriter.println("=> CLOSE CONNECTION");
+          closeConnection();
+          logWriter.println("=> CONNECTION CLOSED");
+        }
+        logWriter.println("Connecting and disconnecting " + REPS + " times without calls");
+        for (int rep = 0; rep < REPS; rep++) {
+          logWriter.println("");
+          logWriter.println("=> OPEN NEW CONNECTION");
+          openConnection();
+          logWriter.println("=> CONNECTION OPENED");
+
+          logWriter.println("Doing nothing!");
+
+          logWriter.println("");
+          logWriter.println("=> CLOSE CONNECTION");
+          closeConnection();
+          logWriter.println("=> CONNECTION CLOSED");
+        }
+        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+
+        // Get the message saying how many times we got a disconnect/connect.
+        int active_cnt = Integer.parseInt(synchronizer.receiveMessage());
+        assertEquals("Connected got called an unexpected number of times",
+            REPS + 1, active_cnt);
+        int deactive_cnt = Integer.parseInt(synchronizer.receiveMessage());
+        assertEquals("Disconnected got called an unexpected number of times",
+            REPS + 1, deactive_cnt);
     }
 }