blob: 06ebf7875e6eeb2a4707994665deffb35854b3b8 [file] [log] [blame]
/*
* Copyright 2000-2014 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.DebugProcessImpl;
import com.intellij.debugger.engine.JavaDebugProcess;
import com.intellij.debugger.engine.RemoteStateState;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.debugger.impl.*;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.settings.NodeRendererSettings;
import com.intellij.debugger.ui.breakpoints.BreakpointManager;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.*;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ExecutionEnvironmentBuilder;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.ui.UIUtil;
import com.intellij.xdebugger.XDebugProcess;
import com.intellij.xdebugger.XDebugProcessStarter;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerManager;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.lang.reflect.InvocationTargetException;
import java.util.StringTokenizer;
public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCase {
protected DebuggerSession myDebuggerSession;
private StringBuffer myConsoleBuffer;
@Override
protected void setUp() throws Exception {
super.setUp();
}
@Override
protected void initApplication() throws Exception {
super.initApplication();
setTestJDK();
DebuggerSettings.getInstance().DEBUGGER_TRANSPORT = DebuggerSettings.SOCKET_TRANSPORT;
DebuggerSettings.getInstance().SKIP_CONSTRUCTORS = false;
DebuggerSettings.getInstance().SKIP_GETTERS = false;
NodeRendererSettings.getInstance().getClassRenderer().SHOW_DECLARED_TYPE = true;
}
@Override
protected void runTest() throws Throwable {
super.runTest();
if(getDebugProcess() != null) {
getDebugProcess().getProcessHandler().startNotify();
waitProcess(getDebugProcess().getProcessHandler());
waitForCompleted();
//disposeSession(myDebuggerSession);
assertNull(DebuggerManagerEx.getInstanceEx(myProject).getDebugProcess(getDebugProcess().getProcessHandler()));
myDebuggerSession = null;
}
if(myConsoleBuffer != null) {
//println("", b);
//println("Console output:", b);
//println(myConsoleBuffer.toString(), b);
}
checkTestOutput();
}
protected void checkTestOutput() throws Exception {
getChecker().checkValid(getTestProjectJdk());
}
protected void disposeSession(final DebuggerSession debuggerSession) throws InterruptedException, InvocationTargetException {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
debuggerSession.dispose();
}
});
}
@Override
protected void tearDown() throws Exception {
FileEditorManagerEx.getInstanceEx(getProject()).closeAllFiles();
myConsoleBuffer = null;
super.tearDown();
}
protected void createLocalProcess(String className) throws ExecutionException, InterruptedException, InvocationTargetException {
LOG.assertTrue(myDebugProcess == null);
myDebuggerSession = createLocalProcess(DebuggerSettings.SOCKET_TRANSPORT, createJavaParameters(className));
myDebugProcess = myDebuggerSession.getProcess();
}
protected DebuggerSession createLocalSession(final JavaParameters javaParameters) throws ExecutionException, InterruptedException {
createBreakpoints(javaParameters.getMainClass());
DebuggerSettings.getInstance().DEBUGGER_TRANSPORT = DebuggerSettings.SOCKET_TRANSPORT;
GenericDebuggerRunnerSettings debuggerRunnerSettings = new GenericDebuggerRunnerSettings();
debuggerRunnerSettings.LOCAL = true;
final RemoteConnection debugParameters = DebuggerManagerImpl.createDebugParameters(javaParameters, debuggerRunnerSettings, false);
ExecutionEnvironment environment = new ExecutionEnvironmentBuilder(myProject, DefaultDebugExecutor.getDebugExecutorInstance())
.runnerSettings(debuggerRunnerSettings)
.runProfile(new MockConfiguration())
.build();
final JavaCommandLineState javaCommandLineState = new JavaCommandLineState(environment){
@Override
protected JavaParameters createJavaParameters() {
return javaParameters;
}
@Override
protected GeneralCommandLine createCommandLine() throws ExecutionException {
return CommandLineBuilder.createFromJavaParameters(getJavaParameters());
}
};
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
@Override
public void run() {
try {
myDebuggerSession =
DebuggerManagerEx.getInstanceEx(myProject)
.attachVirtualMachine(new DefaultDebugEnvironment(new ExecutionEnvironmentBuilder(myProject, DefaultDebugExecutor.getDebugExecutorInstance())
.runProfile(new MockConfiguration())
.build(), javaCommandLineState, debugParameters, false));
XDebuggerManager.getInstance(myProject).startSession(javaCommandLineState.getEnvironment(), new XDebugProcessStarter() {
@Override
@NotNull
public XDebugProcess start(@NotNull XDebugSession session) {
return new JavaDebugProcess(session, myDebuggerSession);
}
});
}
catch (ExecutionException e) {
LOG.error(e);
}
}
}, ModalityState.defaultModalityState());
myDebugProcess = myDebuggerSession.getProcess();
//myConsoleBuffer = new StringBuffer();
myDebugProcess.addProcessListener(new ProcessAdapter() {
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
//myConsoleBuffer.append(event.getText());
print(event.getText(), outputType);
}
});
assertNotNull(myDebuggerSession);
assertNotNull(myDebugProcess);
return myDebuggerSession;
}
protected DebuggerSession createLocalProcess(int transport, final JavaParameters javaParameters) throws ExecutionException, InterruptedException, InvocationTargetException {
createBreakpoints(javaParameters.getMainClass());
final DebuggerSession[] debuggerSession = new DebuggerSession[]{null};
DebuggerSettings.getInstance().DEBUGGER_TRANSPORT = transport;
GenericDebuggerRunnerSettings debuggerRunnerSettings = new GenericDebuggerRunnerSettings();
debuggerRunnerSettings.LOCAL = true;
debuggerRunnerSettings.DEBUG_PORT = "3456";
ExecutionEnvironment environment = new ExecutionEnvironmentBuilder(myProject, DefaultDebugExecutor.getDebugExecutorInstance())
.runnerSettings(debuggerRunnerSettings)
.runProfile(new MockConfiguration())
.build();
final JavaCommandLineState javaCommandLineState = new JavaCommandLineState(environment) {
@Override
protected JavaParameters createJavaParameters() {
return javaParameters;
}
@Override
protected GeneralCommandLine createCommandLine() throws ExecutionException {
return CommandLineBuilder.createFromJavaParameters(getJavaParameters());
}
};
final RemoteConnection debugParameters =
DebuggerManagerImpl.createDebugParameters(javaCommandLineState.getJavaParameters(), debuggerRunnerSettings, true);
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
try {
debuggerSession[0] = attachVirtualMachine(javaCommandLineState, javaCommandLineState.getEnvironment(), debugParameters, false);
}
catch (ExecutionException e) {
fail(e.getMessage());
}
}
});
final ProcessHandler processHandler = debuggerSession[0].getProcess().getProcessHandler();
debuggerSession[0].getProcess().addProcessListener(new ProcessAdapter() {
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
print(event.getText(), outputType);
}
});
DebugProcessImpl process =
(DebugProcessImpl)DebuggerManagerEx.getInstanceEx(myProject).getDebugProcess(processHandler);
assertNotNull(process);
return debuggerSession[0];
}
protected DebuggerSession createRemoteProcess(final int transport, final boolean serverMode, JavaParameters javaParameters)
throws ExecutionException, InterruptedException, InvocationTargetException {
boolean useSockets = transport == DebuggerSettings.SOCKET_TRANSPORT;
RemoteConnection remoteConnection = new RemoteConnection(
useSockets,
"127.0.0.1",
"3456",
serverMode);
String launchCommandLine = remoteConnection.getLaunchCommandLine();
launchCommandLine = StringUtil.replace(launchCommandLine, RemoteConnection.ONTHROW, "");
launchCommandLine = StringUtil.replace(launchCommandLine, RemoteConnection.ONUNCAUGHT, "");
launchCommandLine = StringUtil.replace(launchCommandLine, "suspend=n", "suspend=y");
println(launchCommandLine, ProcessOutputTypes.SYSTEM);
for(StringTokenizer tokenizer = new StringTokenizer(launchCommandLine);tokenizer.hasMoreTokens();) {
String token = tokenizer.nextToken();
javaParameters.getVMParametersList().add(token);
}
GeneralCommandLine commandLine = CommandLineBuilder.createFromJavaParameters(javaParameters);
DebuggerSession debuggerSession;
if(serverMode) {
debuggerSession = attachVM(remoteConnection, false);
commandLine.createProcess();
} else {
commandLine.createProcess();
debuggerSession = attachVM(remoteConnection, true);
}
ProcessHandler processHandler = debuggerSession.getProcess().getProcessHandler();
DebugProcessImpl process = (DebugProcessImpl)DebuggerManagerEx.getInstanceEx(myProject)
.getDebugProcess(processHandler);
assertNotNull(process);
return debuggerSession;
}
protected DebuggerSession attachVM(final RemoteConnection remoteConnection, final boolean pollConnection)
throws InvocationTargetException, InterruptedException {
final RemoteState remoteState = new RemoteStateState(myProject, remoteConnection);
final DebuggerSession[] debuggerSession = new DebuggerSession[1];
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
try {
debuggerSession[0] = attachVirtualMachine(remoteState, new ExecutionEnvironmentBuilder(myProject, DefaultDebugExecutor.getDebugExecutorInstance())
.runProfile(new MockConfiguration())
.build(), remoteConnection, pollConnection);
}
catch (ExecutionException e) {
fail(e.getMessage());
}
}
});
debuggerSession[0].getProcess().getProcessHandler().addProcessListener(new ProcessAdapter() {
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
print(event.getText(), outputType);
}
});
return debuggerSession[0];
}
protected void createBreakpoints(final String className) {
final PsiFile psiFile = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
@Override
public PsiFile compute() {
PsiClass psiClass = JavaPsiFacade.getInstance(myProject).findClass(className, GlobalSearchScope.allScope(myProject));
return psiClass.getContainingFile();
}
});
createBreakpoints(psiFile);
}
protected EvaluationContextImpl createEvaluationContext(final SuspendContextImpl suspendContext) {
try {
return new EvaluationContextImpl(
suspendContext,
suspendContext.getFrameProxy(),
suspendContext.getFrameProxy().thisObject());
}
catch (EvaluateException e) {
error(e);
return null;
}
}
protected void waitForCompleted() {
final SynchronizationBasedSemaphore s = new SynchronizationBasedSemaphore();
s.down();
final InvokeThread.WorkerThreadRequest request = getDebugProcess().getManagerThread().getCurrentRequest();
final Thread thread = new Thread("Joining "+request) {
@Override
public void run() {
try {
request.join();
}
catch (Exception ignored) {
}
}
};
thread.start();
if(request.isDone()) {
thread.interrupt();
}
waitFor(new Runnable() {
@Override
public void run() {
try {
thread.join();
}
catch (InterruptedException ignored) {
}
}
});
invokeRatherLater(new DebuggerCommandImpl() {
@Override
protected void action() throws Exception {
LOG.assertTrue(false);
}
@Override
protected void commandCancelled() {
//We wait for invokeRatherLater's
invokeRatherLater(new DebuggerCommandImpl() {
@Override
protected void action() throws Exception {
LOG.assertTrue(false);
}
@Override
protected void commandCancelled() {
s.up();
}
});
}
});
waitFor(new Runnable() {
@Override
public void run() {
s.waitFor();
}
});
}
public DebuggerContextImpl createDebuggerContext(final SuspendContextImpl suspendContext, StackFrameProxyImpl stackFrame) {
final DebuggerSession[] session = new DebuggerSession[1];
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
session[0] = DebuggerManagerEx.getInstanceEx(myProject).getSession(suspendContext.getDebugProcess());
}
});
DebuggerContextImpl debuggerContext = DebuggerContextImpl.createDebuggerContext(
session[0],
suspendContext,
stackFrame != null ? stackFrame.threadProxy() : null,
stackFrame);
debuggerContext.initCaches();
return debuggerContext;
}
public DebuggerContextImpl createDebuggerContext(final SuspendContextImpl suspendContext) {
return createDebuggerContext(suspendContext, suspendContext.getFrameProxy());
}
protected void createBreakpointInHelloWorld() {
DebuggerInvocationUtil.invokeAndWait(myProject, new Runnable() {
@Override
public void run() {
BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
PsiClass psiClass = JavaPsiFacade.getInstance(myProject).findClass("HelloWorld", GlobalSearchScope.allScope(myProject));
Document document = PsiDocumentManager.getInstance(myProject).getDocument(psiClass.getContainingFile());
breakpointManager.addLineBreakpoint(document, 3);
}
}, ApplicationManager.getApplication().getDefaultModalityState());
}
protected void createHelloWorldProcessWithBreakpoint() throws ExecutionException, InterruptedException, InvocationTargetException {
createLocalProcess("HelloWorld");
createBreakpointInHelloWorld();
}
@Override
protected DebugProcessImpl getDebugProcess() {
return myDebuggerSession != null ? myDebuggerSession.getProcess() : null;
}
public DebuggerSession getDebuggerSession() {
return myDebuggerSession;
}
protected DebuggerSession attachVirtualMachine(RunProfileState state,
ExecutionEnvironment environment,
RemoteConnection remoteConnection,
boolean pollConnection) throws ExecutionException {
final DebuggerSession debuggerSession =
DebuggerManagerEx.getInstanceEx(myProject).attachVirtualMachine(new DefaultDebugEnvironment(environment, state, remoteConnection, pollConnection));
XDebuggerManager.getInstance(myProject).startSession(environment, new XDebugProcessStarter() {
@Override
@NotNull
public XDebugProcess start(@NotNull XDebugSession session) {
return new JavaDebugProcess(session, debuggerSession);
}
});
return debuggerSession;
}
public static class MockConfiguration implements ModuleRunConfiguration {
@Override
@NotNull
public Module[] getModules() {
return Module.EMPTY_ARRAY; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public Icon getIcon() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public ConfigurationFactory getFactory() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void setName(final String name) {
//To change body of implemented methods use File | Settings | File Templates.
}
@NotNull
@Override
public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public Project getProject() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
@NotNull
public ConfigurationType getType() {
return UnknownConfigurationType.INSTANCE;
}
@Override
public ConfigurationPerRunnerSettings createRunnerSettings(final ConfigurationInfoProvider provider) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public SettingsEditor<ConfigurationPerRunnerSettings> getRunnerSettingsEditor(final ProgramRunner runner) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public RunConfiguration clone() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public int getUniqueID() {
return 0;
}
@Override
public RunProfileState getState(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env) throws ExecutionException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public String getName() {
return ""; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void checkConfiguration() throws RuntimeConfigurationException {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void readExternal(final Element element) throws InvalidDataException {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void writeExternal(final Element element) throws WriteExternalException {
//To change body of implemented methods use File | Settings | File Templates.
}
}
}