blob: c4f7d920969dc729f5c697e32b8a9b6d3043ceb7 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed 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 com.intellij.debugger;
import com.intellij.debugger.engine.*;
import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.impl.DebuggerManagerImpl;
import com.intellij.debugger.impl.PositionUtil;
import com.intellij.debugger.impl.PrioritizedTask;
import com.intellij.debugger.impl.SynchronizationBasedSemaphore;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.ui.breakpoints.Breakpoint;
import com.intellij.debugger.ui.breakpoints.BreakpointManager;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionTestCase;
import com.intellij.execution.configurations.RemoteConnection;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.projectRoots.ProjectJdkTable;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.testFramework.CompositeException;
import com.intellij.ui.classFilter.ClassFilter;
import com.intellij.util.IJSwingUtilities;
import com.intellij.util.ui.UIUtil;
import com.sun.jdi.Method;
import com.sun.jdi.ThreadReference;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCase {
private DebugProcessListener myPauseScriptListener = null;
private final List<SuspendContextRunnable> myScriptRunnables = new ArrayList<SuspendContextRunnable>();
private final SynchronizationBasedSemaphore myScriptRunnablesSema = new SynchronizationBasedSemaphore();
protected static final int RATHER_LATER_INVOKES_N = 10;
public DebugProcessImpl myDebugProcess = null;
private final CompositeException myException = new CompositeException();
private class InvokeRatherLaterRequest {
private final DebuggerCommandImpl myDebuggerCommand;
private final DebugProcessImpl myDebugProcess;
int invokesN = 0;
public InvokeRatherLaterRequest(DebuggerCommandImpl debuggerCommand, DebugProcessImpl debugProcess) {
myDebuggerCommand = debuggerCommand;
myDebugProcess = debugProcess;
}
}
public List<InvokeRatherLaterRequest> myRatherLaterRequests = new ArrayList<InvokeRatherLaterRequest>();
protected DebugProcessImpl getDebugProcess() {
return myDebugProcess;
}
protected String readValue(String comment, String valueName) {
int valueStart = comment.indexOf(valueName);
if (valueStart == -1) return null;
int valueEnd = comment.indexOf(')', valueStart);
return comment.substring(valueStart + valueName.length() + 1, valueEnd);
}
protected void resume(SuspendContextImpl context) {
DebugProcessImpl debugProcess = context.getDebugProcess();
debugProcess.getManagerThread().schedule(debugProcess.createResumeCommand(context, PrioritizedTask.Priority.LOW));
}
protected void waitBreakpoints() {
myScriptRunnablesSema.down();
waitFor(new Runnable() {
@Override
public void run() {
myScriptRunnablesSema.waitFor();
}
});
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
synchronized (myException) {
if (!myException.isEmpty()) throw myException;
}
}
protected void onBreakpoint(SuspendContextRunnable runnable) {
if (myPauseScriptListener == null) {
final DebugProcessImpl debugProcess = getDebugProcess();
assertTrue("Debug process was not started", debugProcess != null);
myPauseScriptListener = new DelayedEventsProcessListener(
new DebugProcessAdapterImpl() {
@Override
public void paused(SuspendContextImpl suspendContext) {
try {
if (myScriptRunnables.isEmpty()) {
print("resuming ", ProcessOutputTypes.SYSTEM);
printContext(suspendContext);
resume(suspendContext);
return;
}
SuspendContextRunnable suspendContextRunnable = myScriptRunnables.remove(0);
suspendContextRunnable.run(suspendContext);
}
catch (Exception e) {
addException(e);
error(e);
}
catch (AssertionError e) {
addException(e);
}
if (myScriptRunnables.isEmpty()) {
myScriptRunnablesSema.up();
}
}
//executed in manager thread
@Override
public void resumed(SuspendContextImpl suspendContext) {
final SuspendContextImpl pausedContext = debugProcess.getSuspendManager().getPausedContext();
if (pausedContext != null) {
debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(pausedContext) {
@Override
public void contextAction() throws Exception {
paused(pausedContext);
}
});
}
}
}
);
debugProcess.addDebugProcessListener(myPauseScriptListener);
}
myScriptRunnables.add(runnable);
}
protected void printFrameProxy(StackFrameProxyImpl frameProxy) throws EvaluateException {
int frameIndex = frameProxy.getFrameIndex();
Method method = frameProxy.location().method();
println("frameProxy(" + frameIndex + ") = " + method, ProcessOutputTypes.SYSTEM);
}
protected void printContext(final StackFrameContext context) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (context.getFrameProxy() != null) {
SourcePosition sourcePosition = PositionUtil.getSourcePosition(context);
println(sourcePosition.getFile().getVirtualFile().getName() + ":" + sourcePosition.getLine(), ProcessOutputTypes.SYSTEM);
}
else {
println("Context thread is null", ProcessOutputTypes.SYSTEM);
}
}
});
}
protected void invokeRatherLater(SuspendContextImpl context, final Runnable runnable) {
invokeRatherLater(new SuspendContextCommandImpl(context) {
@Override
public void contextAction() throws Exception {
DebuggerInvocationUtil.invokeLater(myProject, runnable);
}
});
}
protected void pumpSwingThread() {
LOG.assertTrue(SwingUtilities.isEventDispatchThread());
final InvokeRatherLaterRequest request = myRatherLaterRequests.get(0);
request.invokesN++;
if (request.invokesN == RATHER_LATER_INVOKES_N) {
myRatherLaterRequests.remove(0);
if (!myRatherLaterRequests.isEmpty()) pumpSwingThread();
}
if (request.myDebuggerCommand instanceof SuspendContextCommandImpl) {
request.myDebugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(
((SuspendContextCommandImpl)request.myDebuggerCommand).getSuspendContext()) {
@Override
public void contextAction() throws Exception {
pumpDebuggerThread(request);
}
@Override
protected void commandCancelled() {
pumpDebuggerThread(request);
}
});
}
else {
request.myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() {
@Override
protected void action() throws Exception {
pumpDebuggerThread(request);
}
@Override
protected void commandCancelled() {
pumpDebuggerThread(request);
}
});
}
}
private void pumpDebuggerThread(final InvokeRatherLaterRequest request) {
if (request.invokesN == RATHER_LATER_INVOKES_N) {
request.myDebugProcess.getManagerThread().schedule(request.myDebuggerCommand);
}
else {
if (!SwingUtilities.isEventDispatchThread()) {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
pumpSwingThread();
}
});
}
else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
pumpSwingThread();
}
});
}
}
}
protected void invokeRatherLater(final DebuggerCommandImpl command) {
IJSwingUtilities.invoke(new Runnable() {
@Override
public void run() {
InvokeRatherLaterRequest request = new InvokeRatherLaterRequest(command, getDebugProcess());
myRatherLaterRequests.add(request);
if (myRatherLaterRequests.size() == 1) pumpSwingThread();
}
});
}
protected void addException(Throwable e) {
synchronized (myException) {
myException.add(e);
}
}
protected void error(Throwable th) {
fail(StringUtil.getThrowableText(th));
}
public void createBreakpoints(final PsiFile file) {
Runnable runnable = new Runnable() {
@Override
public void run() {
BreakpointManager breakpointManager = DebuggerManagerImpl.getInstanceEx(myProject).getBreakpointManager();
Document document = PsiDocumentManager.getInstance(myProject).getDocument(file);
int offset = -1;
for (; ;) {
offset = document.getText().indexOf("Breakpoint!", offset + 1);
if (offset == -1) break;
int commentLine = document.getLineNumber(offset);
String comment = document.getText().substring(document.getLineStartOffset(commentLine), document.getLineEndOffset(commentLine));
Breakpoint breakpoint;
if (comment.indexOf("Method") != -1) {
breakpoint = breakpointManager.addMethodBreakpoint(document, commentLine + 1);
if (breakpoint != null) {
println("MethodBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2),
ProcessOutputTypes.SYSTEM);
}
}
else if (comment.indexOf("Field") != -1) {
breakpoint = breakpointManager.addFieldBreakpoint(document, commentLine + 1, readValue(comment, "Field"));
if (breakpoint != null) {
println("FieldBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2), ProcessOutputTypes.SYSTEM);
}
}
else if (comment.indexOf("Exception") != -1) {
breakpoint = breakpointManager.addExceptionBreakpoint(readValue(comment, "Exception"), "");
if (breakpoint != null) {
println("ExceptionBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2),
ProcessOutputTypes.SYSTEM);
}
}
else {
breakpoint = breakpointManager.addLineBreakpoint(document, commentLine + 1);
if (breakpoint != null) {
println("LineBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2), ProcessOutputTypes.SYSTEM);
}
}
String suspendPolicy = readValue(comment, "suspendPolicy");
if (suspendPolicy != null) {
//breakpoint.setSuspend(!DebuggerSettings.SUSPEND_NONE.equals(suspendPolicy));
breakpoint.setSuspendPolicy(suspendPolicy);
println("SUSPEND_POLICY = " + suspendPolicy, ProcessOutputTypes.SYSTEM);
}
String condition = readValue(comment, "Condition");
if (condition != null) {
//breakpoint.CONDITION_ENABLED = true;
breakpoint.setCondition(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, condition));
println("Condition = " + condition, ProcessOutputTypes.SYSTEM);
}
String passCount = readValue(comment, "Pass count");
if (passCount != null) {
breakpoint.setCountFilterEnabled(true);
breakpoint.setCountFilter(Integer.parseInt(passCount));
println("Pass count = " + passCount, ProcessOutputTypes.SYSTEM);
}
String classFilters = readValue(comment, "Class filters");
if (classFilters != null) {
breakpoint.setClassFiltersEnabled(true);
StringTokenizer tokenizer = new StringTokenizer(classFilters, " ,");
ArrayList<ClassFilter> lst = new ArrayList<ClassFilter>();
while (tokenizer.hasMoreTokens()) {
ClassFilter filter = new ClassFilter();
filter.setEnabled(true);
filter.setPattern(tokenizer.nextToken());
lst.add(filter);
}
breakpoint.setClassFilters(lst.toArray(new ClassFilter[lst.size()]));
println("Class filters = " + classFilters, ProcessOutputTypes.SYSTEM);
}
}
}
};
if (!SwingUtilities.isEventDispatchThread()) {
DebuggerInvocationUtil.invokeAndWait(myProject, runnable, ModalityState.defaultModalityState());
}
else {
runnable.run();
}
}
private Sdk getTestJdk() {
try {
ProjectJdkImpl jdk = (ProjectJdkImpl)JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk().clone();
jdk.setName("JDK");
return jdk;
}
catch (CloneNotSupportedException e) {
LOG.error(e);
return null;
}
}
protected void setTestJDK() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
Sdk jdk = ProjectJdkTable.getInstance().findJdk("JDK");
if (jdk != null) {
ProjectJdkTable.getInstance().removeJdk(jdk);
}
ProjectJdkTable.getInstance().addJdk(getTestJdk());
}
});
}
private class DelayedEventsProcessListener implements DebugProcessListener {
private final DebugProcessAdapterImpl myTarget;
public DelayedEventsProcessListener(DebugProcessAdapterImpl target) {
myTarget = target;
}
@Override
public void threadStarted(DebugProcess proc, ThreadReference thread) {
}
@Override
public void threadStopped(DebugProcess proc, ThreadReference thread) {
}
@Override
public void paused(final SuspendContext suspendContext) {
pauseExecution();
myTarget.paused(suspendContext);
}
@Override
public void resumed(final SuspendContext suspendContext) {
pauseExecution();
myTarget.resumed(suspendContext);
}
@Override
public void processDetached(final DebugProcess process, final boolean closedByUser) {
myTarget.processDetached(process, closedByUser);
}
@Override
public void processAttached(final DebugProcess process) {
myTarget.processAttached(process);
}
@Override
public void connectorIsReady() {
myTarget.connectorIsReady();
}
@Override
public void attachException(final RunProfileState state, final ExecutionException exception, final RemoteConnection remoteConnection) {
myTarget.attachException(state, exception, remoteConnection);
}
private void pauseExecution() {
try {
Thread.sleep(10);
}
catch (InterruptedException ignored) {
}
}
}
}