From 36cc3f72992f9aa1d309326f92f9aa4aa43778df Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 7 Apr 2011 17:14:28 +0200 Subject: [PATCH] a simple groovy debugger evaluation test --- .../plugins/groovy/GroovyLoader.java | 11 +- .../debugger/GroovyPositionManager.java | 10 ++ .../compiler/GroovyCompilerTestCase.java | 59 ++++---- .../groovy/compiler/GroovyDebuggerTest.groovy | 141 ++++++++++++++++++ 4 files changed, 183 insertions(+), 38 deletions(-) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyLoader.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyLoader.java index afa92a69df21..6a2d113eac3c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyLoader.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyLoader.java @@ -17,15 +17,11 @@ package org.jetbrains.plugins.groovy; import com.intellij.codeInsight.completion.CompletionUtil; -import com.intellij.debugger.DebuggerManager; -import com.intellij.debugger.PositionManager; -import com.intellij.debugger.engine.DebugProcess; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManagerAdapter; import com.intellij.psi.impl.source.tree.ChangeUtil; -import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.debugger.GroovyPositionManager; import org.jetbrains.plugins.groovy.lang.GroovyChangeUtilSupport; @@ -50,12 +46,7 @@ public class GroovyLoader implements ApplicationComponent { ProjectManager.getInstance().addProjectManagerListener(new ProjectManagerAdapter() { public void projectOpened(final Project project) { - DebuggerManager.getInstance(project).registerPositionManagerFactory(new Function() { - public PositionManager fun(DebugProcess debugProcess) { - return new GroovyPositionManager(debugProcess); - } - }); - + GroovyPositionManager.registerPositionManager(project); } }); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java index da9f998f0a2e..f0b807067088 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java @@ -16,6 +16,7 @@ package org.jetbrains.plugins.groovy.debugger; +import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.NoDataException; import com.intellij.debugger.PositionManager; import com.intellij.debugger.SourcePosition; @@ -40,6 +41,7 @@ import com.intellij.psi.*; import com.intellij.psi.search.FilenameIndex; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.Function; import com.intellij.util.Processor; import com.intellij.util.Query; import com.intellij.util.containers.HashSet; @@ -72,6 +74,14 @@ public class GroovyPositionManager implements PositionManager { myDebugProcess = debugProcess; } + public static void registerPositionManager(Project project) { + DebuggerManager.getInstance(project).registerPositionManagerFactory(new Function() { + public PositionManager fun(DebugProcess debugProcess) { + return new GroovyPositionManager(debugProcess); + } + }); + } + public DebugProcess getDebugProcess() { return myDebugProcess; } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java index 35fde0819569..25f6562bc1c5 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java @@ -8,10 +8,7 @@ import com.intellij.execution.application.ApplicationConfigurationType; import com.intellij.execution.configurations.RunnerSettings; import com.intellij.execution.executors.DefaultRunExecutor; import com.intellij.execution.impl.DefaultJavaProgramRunner; -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.process.*; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.ui.RunContentDescriptor; @@ -26,7 +23,6 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.StdModuleTypes; import com.intellij.openapi.projectRoots.impl.JavaSdkImpl; import com.intellij.openapi.roots.*; -import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.JDOMExternalizable; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; @@ -49,6 +45,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; /** * @author peter @@ -212,23 +209,39 @@ public abstract class GroovyCompilerTestCase extends JavaCodeInsightFixtureTestC assertOutput(className, output, myModule); } - protected void assertOutput(String className, String output, final Module module) throws ExecutionException { - final ApplicationConfiguration configuration = - new ApplicationConfiguration("app", getProject(), ApplicationConfigurationType.getInstance()); + protected void assertOutput(String className, String expected, final Module module) throws ExecutionException { + final StringBuffer sb = new StringBuffer(); + ProcessHandler process = runProcess(className, module, DefaultRunExecutor.class, DefaultJavaProgramRunner.class, new ProcessAdapter() { + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + if (ProcessOutputTypes.SYSTEM != outputType) { + sb.append(event.getText()); + } + } + }); + process.waitFor(); + assertEquals(expected.trim(), StringUtil.convertLineSeparators(sb.toString().trim())); + } + + protected ProcessHandler runProcess(String className, + Module module, + final Class executorClass, + final Class runnerClass, final ProcessListener listener) throws ExecutionException { + final ApplicationConfiguration configuration = new ApplicationConfiguration("app", getProject(), ApplicationConfigurationType.getInstance()); configuration.setModule(module); configuration.setMainClassName(className); - final DefaultRunExecutor extension = Executor.EXECUTOR_EXTENSION_NAME.findExtension(DefaultRunExecutor.class); + final Executor executor = Executor.EXECUTOR_EXTENSION_NAME.findExtension(executorClass); final ExecutionEnvironment environment = new ExecutionEnvironment(configuration, getProject(), new RunnerSettings(null, null), null, null); - final DefaultJavaProgramRunner runner = ProgramRunner.PROGRAM_RUNNER_EP.findExtension(DefaultJavaProgramRunner.class); - final StringBuffer sb = new StringBuffer(); + final ProgramRunner runner = ProgramRunner.PROGRAM_RUNNER_EP.findExtension(runnerClass); final Semaphore semaphore = new Semaphore(); semaphore.down(); - runner.execute(extension, environment, new ProgramRunner.Callback() { + final AtomicReference processHandler = new AtomicReference(); + runner.execute(executor, environment, new ProgramRunner.Callback() { @Override public void processStarted(final RunContentDescriptor descriptor) { - Disposer.register(myFixture.getProject(), new Disposable() { + disposeOnTearDown(new Disposable() { @Override public void dispose() { descriptor.dispose(); @@ -236,23 +249,13 @@ public abstract class GroovyCompilerTestCase extends JavaCodeInsightFixtureTestC }); final ProcessHandler handler = descriptor.getProcessHandler(); assert handler != null; - handler.addProcessListener(new ProcessAdapter() { - @Override - public void onTextAvailable(ProcessEvent event, Key outputType) { - if (ProcessOutputTypes.SYSTEM != outputType) { - sb.append(event.getText()); - } - } - - @Override - public void processTerminated(ProcessEvent event) { - semaphore.up(); - } - }); + handler.addProcessListener(listener); + processHandler.set(handler); + semaphore.up(); } }); semaphore.waitFor(); - assertEquals(output.trim(), StringUtil.convertLineSeparators(sb.toString().trim())); + return processHandler.get(); } private static class ErrorReportingCallback implements CompileStatusNotification { @@ -267,7 +270,6 @@ public abstract class GroovyCompilerTestCase extends JavaCodeInsightFixtureTestC @Override public void finished(boolean aborted, int errors, int warnings, final CompileContext compileContext) { try { - assertFalse("Code did not compile!", aborted); for (CompilerMessageCategory category : CompilerMessageCategory.values()) { for (CompilerMessage message : compileContext.getMessages(category)) { final String msg = message.getMessage(); @@ -279,6 +281,7 @@ public abstract class GroovyCompilerTestCase extends JavaCodeInsightFixtureTestC if (errors > 0) { fail("Compiler errors occurred! " + StringUtil.join(myMessages, "\n")); } + assertFalse("Code did not compile!", aborted); } catch (Throwable t) { myError = t; diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy new file mode 100644 index 000000000000..28364913bb47 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy @@ -0,0 +1,141 @@ +/* + * Copyright 2000-2011 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 org.jetbrains.plugins.groovy.compiler + +import com.intellij.debugger.DebuggerInvocationUtil +import com.intellij.debugger.EvaluatingComputable +import com.intellij.debugger.engine.ContextUtil +import com.intellij.debugger.engine.DebugProcessImpl +import com.intellij.debugger.engine.DebuggerManagerThreadImpl +import com.intellij.debugger.engine.SuspendContextImpl +import com.intellij.debugger.engine.evaluation.CodeFragmentKind +import com.intellij.debugger.engine.evaluation.EvaluateException +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl +import com.intellij.debugger.engine.evaluation.TextWithImportsImpl +import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilder +import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl +import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator +import com.intellij.debugger.engine.events.DebuggerCommandImpl +import com.intellij.debugger.impl.DebuggerManagerImpl +import com.intellij.debugger.impl.GenericDebuggerRunner +import com.intellij.debugger.impl.PositionUtil +import com.intellij.debugger.ui.DebuggerPanelsManager +import com.intellij.execution.executors.DefaultDebugExecutor +import com.intellij.execution.process.ProcessAdapter +import org.jetbrains.plugins.groovy.debugger.GroovyPositionManager + +/** + * @author peter + */ +class GroovyDebuggerTest extends GroovyCompilerTestCase { + DebuggerManagerThreadImpl managerThread + + @Override + protected void setUp() { + edt { + super.setUp() + addGroovyLibrary(myModule); + } + + GroovyPositionManager.registerPositionManager(project) + managerThread = DebuggerManagerThreadImpl.createTestInstance() + } + + @Override + protected boolean runInDispatchThread() { + return false + } + + @Override + protected void invokeTestRunnable(Runnable runnable) { + runnable.run() + } + + @Override + protected void tearDown() { + managerThread = null + super.tearDown() + } + + private void startDebugging() { + edt { + runProcess('Foo', myModule, DefaultDebugExecutor, GenericDebuggerRunner, [onTextAvailable:{ evt, type -> /*print evt.text*/}] as ProcessAdapter) + } + } + + public void testSimpleEvaluate() { + def foo = myFixture.addFileToProject("Foo.groovy", "println 'hello'"); + make() + + edt { + DebuggerManagerImpl.getInstanceEx(project).breakpointManager.addLineBreakpoint(foo.viewProvider.document, 0) + } + + startDebugging() + def context = waitForBreakpoint() + assert eval(context, '2+2') == '4' + resume() + + debugProcess.executionResult.processHandler.waitFor() + } + + private def resume() { + managerThread.invoke(debugProcess.createResumeCommand(debugProcess.suspendManager.pausedContext)) + } + + private SuspendContextImpl waitForBreakpoint() { + int i = 0 + def suspendManager = debugProcess.suspendManager + while (i++ < 1000 && !suspendManager.pausedContext && !debugProcess.executionResult.processHandler.processTerminated) { + Thread.sleep(10) + } + + def context = suspendManager.pausedContext + assert context : 'too long process' + return context + } + + private DebugProcessImpl getDebugProcess() { + return DebuggerPanelsManager.getInstance(project).sessionTab.session.process + } + + private def managed(Closure cl) { + def result = null + managerThread.invokeAndWait(new DebuggerCommandImpl() { + @Override + protected void action() { + result = cl() + } + }) + return result + } + + private String eval(final SuspendContextImpl suspendContext, final String codeText) throws EvaluateException { + return managed({ + final ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(project, { + final EvaluatorBuilder builder = EvaluatorBuilderImpl.getInstance(); + return builder.build( + new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, codeText), + PositionUtil.getContextElement(suspendContext), + ContextUtil.getSourcePosition(suspendContext) + ); + } as EvaluatingComputable) + return evaluator.evaluate(new EvaluationContextImpl(suspendContext, suspendContext.frameProxy, suspendContext.frameProxy.thisObject())) + }) as String + } + + +}