JDWP Tests: More GetClassLoader tests

First, ensure that the returned object (in the success case) is
a classloader.

Second, add a negative test that ensures we get the right failure
value when the supplied ID is not a reference type. Note: this only
works if reference type IDs and object type IDs share their domain.
Otherwise a valid object ID may be a valid reference type ID.

Bug: 26349019
Change-Id: Ia33dac3f8b8fd158dec9f229f796263c8c985eab
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ReferenceType/ClassLoaderTest.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ReferenceType/ClassLoaderTest.java
index 5d1aa7d..7fc7d61 100644
--- a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ReferenceType/ClassLoaderTest.java
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ReferenceType/ClassLoaderTest.java
@@ -27,6 +27,7 @@
 
 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;
 import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
 import org.apache.harmony.jpda.tests.jdwp.share.JDWPSyncTestCase;
 import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
@@ -68,7 +69,44 @@
     }
 
     /**
-     * Implementation of the tests, using the given parameters.
+     * Test that supplying a general object that is not a class returns
+     * a class error.
+     */
+    public void testClassLoader003() {
+        String thisTestName = "testClassLoader003";
+        logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": START...");
+        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+        // It's easer to just ask for an instance of String (as we know the runtime has
+        // created some during startup), rather than collecting all the constructor data
+        // for a class like the debuggee.
+        long stringClassRefTypeID = getClassIDBySignature("Ljava/lang/String;");
+
+        CommandPacket stringInstanceCommand = new CommandPacket(
+                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
+                JDWPCommands.ReferenceTypeCommandSet.InstancesCommand);
+        stringInstanceCommand.setNextValueAsReferenceTypeID(stringClassRefTypeID);
+        stringInstanceCommand.setNextValueAsInt(1);  // One instance is enough.
+        ReplyPacket stringInstanceReply =
+                debuggeeWrapper.vmMirror.performCommand(stringInstanceCommand);
+        checkReplyPacket(stringInstanceReply, "ReferenceType.Instances");
+        int stringInstanceCount = stringInstanceReply.getNextValueAsInt();
+        assertTrue("Expected to get one string instance", stringInstanceCount == 1);
+        long stringInstanceID = stringInstanceReply.getNextValueAsTaggedObject().objectID;
+
+        CommandPacket classLoaderCommand = new CommandPacket(
+                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
+                JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
+        classLoaderCommand.setNextValueAsReferenceTypeID(stringInstanceID);
+        ReplyPacket classLoaderReply = debuggeeWrapper.vmMirror.performCommand(classLoaderCommand);
+        checkReplyPacket(classLoaderReply, thisCommandName, JDWPConstants.Error.INVALID_CLASS);
+
+        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+        logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": FINISH");
+    }
+
+    /**
+     * Implementation of tests 001 and 002, using the given parameters.
      */
     private void classLoaderTest(String thisTestName, String signature, boolean expectZero) {
         logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": START...");
@@ -99,7 +137,51 @@
 
         assertAllDataRead(classLoaderReply);
 
+        assertTrue("Result should be a classloader",
+                isClassLoader(returnedClassLoaderID, thisCommandName));
+
         synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
         logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": FINISH");
     }
+
+    /**
+     * Helper to check whether an object is a classloader. Works by getting the class and
+     * the reference ID of java.lang.ClassLoader, then following the superclass chain and
+     * comparing.
+     */
+    private boolean isClassLoader(long classLoaderObjectID, String thisCommandName) {
+        if (classLoaderObjectID == 0) {
+            // 0 = null = bootstrap classloader.
+            return true;
+        }
+
+        CommandPacket refTypeCommand = new CommandPacket(
+                JDWPCommands.ObjectReferenceCommandSet.CommandSetID,
+                JDWPCommands.ObjectReferenceCommandSet.ReferenceTypeCommand);
+        refTypeCommand.setNextValueAsObjectID(classLoaderObjectID);
+        ReplyPacket refTypeReply = debuggeeWrapper.vmMirror.performCommand(refTypeCommand);
+        checkReplyPacket(refTypeReply, "ObjectReference.ReferenceType");
+        refTypeReply.getNextValueAsByte();  // kind.
+        long classLoaderClassRefTypeID = refTypeReply.getNextValueAsReferenceTypeID();
+
+        long baseClassLoaderRefTypeID = getClassIDBySignature("Ljava/lang/ClassLoader;");
+
+        while (classLoaderClassRefTypeID != 0) {
+            if (classLoaderClassRefTypeID == baseClassLoaderRefTypeID) {
+                // This is a classloader...
+                return true;
+            }
+            CommandPacket superclassCommand = new CommandPacket(
+                JDWPCommands.ClassTypeCommandSet.CommandSetID,
+                JDWPCommands.ClassTypeCommandSet.SuperclassCommand);
+            superclassCommand.setNextValueAsObjectID(classLoaderClassRefTypeID);
+            ReplyPacket superclassReply =
+                    debuggeeWrapper.vmMirror.performCommand(superclassCommand);
+            checkReplyPacket(superclassReply, "ClassType.Superclass");
+            classLoaderClassRefTypeID = superclassReply.getNextValueAsClassID();
+        }
+
+        // None of the superclasses was java.lang.ClassLoader, so it's not a classloader.
+        return false;
+    }
 }