blob: b1fc0e0313271fa792dfe951fc8999d1f8ec58c3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.harmony.jpda.tests.jdwp.share;
import org.apache.harmony.jpda.tests.framework.TestErrorException;
import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
import org.apache.harmony.jpda.tests.framework.jdwp.exceptions.TimeoutException;
import org.apache.harmony.jpda.tests.jdwp.share.debuggee.InvokeMethodSuspendedTwiceDebuggee;
import org.apache.harmony.jpda.tests.jdwp.share.debuggee.InvokeMethodWithSuspensionDebuggee;
import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
import java.io.IOException;
/**
* Base class for tests checking invoke command with thread suspended more than once before the
* invoke.
*/
public abstract class JDWPInvokeMethodSuspendedTwiceTestCase extends JDWPSyncTestCase {
@Override
protected final String getDebuggeeClassName() {
return InvokeMethodSuspendedTwiceDebuggee.class.getName();
}
/**
* This methods runs the {@link InvokeMethodSuspendedTwiceDebuggee} then sets up a breakpoint
* to suspend the main thread only and signal it to continue.
* When the debuggee hits the breakpoint and we receive the event, we clear the breakpoint and
* suspend the thread with ThreadReference.Suspend command so its suspend count is greater
* than 1.
* Then, we send an invoke command for this thread and check we do not receive any reply (as
* the thread is still suspended) by catching a {@link TimeoutException}.
* Next, we resume the thread and check we do receive the reply with no timeout.
*
* @param invokedMethodName
* the name of the method to invoke
*/
protected void runInvokeMethodTest(String invokedMethodName) {
// Wait for debuggee to start.
synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
long classID = getClassIDBySignature(getDebuggeeClassSignature());
long invokeMethodID = getMethodID(classID, invokedMethodName);
// Set breakpoint with EVENT_THREAD suspend policy. We will invoke the method in the thread
// suspended on this breakpoint.
int breakpointEventThread = debuggeeWrapper.vmMirror.setBreakpointAtMethodBegin(classID,
InvokeMethodWithSuspensionDebuggee.BREAKPOINT_EVENT_THREAD_METHOD_NAME,
JDWPConstants.SuspendPolicy.EVENT_THREAD);
// Tell the debuggee to continue.
synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
// Wait for breakpoint and get id of suspended thread.
long eventThreadOne = debuggeeWrapper.vmMirror.waitForBreakpoint(breakpointEventThread);
int suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne);
assertEquals("Invalid suspend count:", 1, suspendCount);
// Clear breakpoint
debuggeeWrapper.vmMirror.clearBreakpoint(breakpointEventThread);
// Suspend thread again so it's suspend count > 1
debuggeeWrapper.vmMirror.suspendThread(eventThreadOne);
suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne);
assertEquals("Invalid suspend count:", 2, suspendCount);
// Send command but does not read the reply yet. That invoked method starts another thread
// that is going to hit a breakpoint and suspend all threads, including the thread invoking
// the method. The invoke can only complete when that new thread terminates, which requires
// we send a VirtualMachine.Resume command.
final int invoke_options = JDWPConstants.InvokeOptions.INVOKE_SINGLE_THREADED;
CommandPacket invokeMethodCommand = buildInvokeCommand(eventThreadOne, classID,
invokeMethodID, invoke_options);
String commandName = getInvokeCommandName();
logWriter.println("Send " + commandName);
int invokeMethodCommandID = -1;
try {
invokeMethodCommandID = debuggeeWrapper.vmMirror.sendCommand(invokeMethodCommand);
} catch (IOException e) {
logWriter.printError("Failed to send " + commandName, e);
fail();
}
// Check that we do not receive any reply.
ReplyPacket invokeMethodReply = null;
logWriter.println("Receiving reply for command " + invokeMethodCommandID + " ...");
try {
invokeMethodReply = debuggeeWrapper.vmMirror.receiveReply(invokeMethodCommandID);
fail("#FAILURE: received reply too early (error " +
JDWPConstants.Error.getName(invokeMethodReply.getErrorCode()) + ")");
} catch (TimeoutException e) {
logWriter.println("OK, did not receive reply for command " + invokeMethodCommandID);
} catch (InterruptedException e) {
throw new TestErrorException(e);
} catch (IOException e) {
throw new TestErrorException(e);
}
// At this point, the event thread #1 should have been resumed once for the invoke. But
// it still suspended by us.
suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne);
assertEquals("Invalid suspend count:", 1, suspendCount);
// Test that sending another invoke command in the same thread returns an error.
CommandPacket anotherInvokeMethodCommand = buildInvokeCommand(eventThreadOne, classID,
invokeMethodID, invoke_options);
ReplyPacket anotherInvokeMethodReply =
debuggeeWrapper.vmMirror.performCommand(anotherInvokeMethodCommand);
// Note: the RI returns INVALID_THREAD but ALREADY_INVOKING is more explicit.
assertTrue("Another invoke should return an error",
anotherInvokeMethodReply.getErrorCode() != JDWPConstants.Error.NONE);
// checkReplyPacket(anotherInvokeMethodReply, "Invalid error code",
// JDWPConstants.Error.ALREADY_INVOKING);
// Send a ThreadReference.Resume to resume event thread so it can invoke the method.
logWriter.println("Resume event thread");
debuggeeWrapper.vmMirror.resumeThread(eventThreadOne);
// Now we can read the invoke reply.
invokeMethodReply = null;
try {
logWriter.println("Receiving reply for command " + invokeMethodCommandID + " ...");
invokeMethodReply = debuggeeWrapper.vmMirror.receiveReply(invokeMethodCommandID);
} catch (Exception e) {
throw new TestErrorException("Did not receive invoke reply", e);
}
checkReplyPacket(invokeMethodReply, commandName + " command");
logWriter.println("Received reply for command " + invokeMethodCommandID + " OK");
checkInvokeReply(invokeMethodReply);
// The invoke is complete but the thread is still suspended: let's resume it now.
suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne);
assertEquals("Invalid suspend count:", 1, suspendCount);
logWriter.println("Resume event thread #1");
debuggeeWrapper.vmMirror.resumeThread(eventThreadOne);
}
/**
* Builds the packed for the tested JDWP command.
*
* @param threadId
* the id of the thread that will invoke the method
* @param classID
* the class ID of the invoked method
* @param methodId
* the ID of the invoke method
* @param invokeOptions
* options for the invoke
* @return a command
*/
protected abstract CommandPacket buildInvokeCommand(long threadId, long classID,
long methodId, int invokeOptions);
/**
* Returns the name of the command returned by {@link #buildInvokeCommand} for printing.
*
* @return the name of the invoke command sent to the debuggee
*/
protected abstract String getInvokeCommandName();
/**
* Checks the reply for the tested JDWP command.
*
* @param reply the reply of the invoke
*/
protected abstract void checkInvokeReply(ReplyPacket reply);
}