don't hang in GroovyDebuggerTest
[idea/community.git] / plugins / groovy / test / org / jetbrains / plugins / groovy / compiler / GroovyDebuggerTest.groovy
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.plugins.groovy.compiler
17
18 import com.intellij.debugger.SourcePosition
19 import com.intellij.debugger.engine.ContextUtil
20 import com.intellij.debugger.engine.DebugProcessImpl
21 import com.intellij.debugger.engine.DebuggerUtils
22 import com.intellij.debugger.engine.SuspendContextImpl
23 import com.intellij.debugger.engine.evaluation.CodeFragmentKind
24 import com.intellij.debugger.engine.evaluation.EvaluateException
25 import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
26 import com.intellij.debugger.engine.evaluation.TextWithImportsImpl
27 import com.intellij.debugger.engine.events.DebuggerContextCommandImpl
28 import com.intellij.debugger.impl.DebuggerContextUtil
29 import com.intellij.debugger.impl.DebuggerManagerImpl
30 import com.intellij.debugger.impl.DebuggerSession
31 import com.intellij.debugger.impl.GenericDebuggerRunner
32 import com.intellij.debugger.ui.DebuggerPanelsManager
33 import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor
34 import com.intellij.debugger.ui.tree.render.DescriptorLabelListener
35 import com.intellij.execution.executors.DefaultDebugExecutor
36 import com.intellij.execution.process.ProcessAdapter
37 import com.intellij.execution.runners.ProgramRunner
38 import com.intellij.openapi.Disposable
39 import com.intellij.openapi.application.ApplicationManager
40 import com.intellij.openapi.fileEditor.FileDocumentManager
41 import com.intellij.openapi.roots.ModuleRootManager
42 import com.intellij.openapi.util.Computable
43 import com.intellij.openapi.util.io.FileUtil
44 import com.intellij.openapi.util.text.StringUtil
45 import com.intellij.openapi.vfs.VirtualFile
46 import com.intellij.testFramework.builders.JavaModuleFixtureBuilder
47 import com.intellij.testFramework.fixtures.impl.TempDirTestFixtureImpl
48 import com.intellij.util.SystemProperties
49 import com.intellij.util.concurrency.Semaphore
50 import com.intellij.execution.process.OSProcessManager
51 import com.intellij.execution.process.OSProcessHandler
52
53 /**
54  * @author peter
55  */
56 class GroovyDebuggerTest extends GroovyCompilerTestCase {
57   @Override
58   protected boolean useJps() { false }
59
60   @Override
61   protected void setUp() {
62     edt {
63       super.setUp()
64       addGroovyLibrary(myModule);
65     }
66
67   }
68
69   @Override
70   protected boolean runInDispatchThread() {
71     return false
72   }
73
74   @Override
75   protected void invokeTestRunnable(Runnable runnable) {
76     runnable.run()
77   }
78
79   @Override
80   protected void tearDown() {
81     super.tearDown()
82   }
83
84   @Override
85   protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) {
86     super.tuneFixture(moduleBuilder)
87     def javaHome = FileUtil.toSystemIndependentName(SystemProperties.getJavaHome())
88     moduleBuilder.addJdk(StringUtil.trimEnd(StringUtil.trimEnd(javaHome, '/'), '/jre'))
89   }
90
91   private void runDebugger(String mainClass, Closure cl) {
92     boolean trace = false//name == 'testClassOutOfSourceRoots'
93     make()
94     edt {
95       ProgramRunner runner = ProgramRunner.PROGRAM_RUNNER_EP.extensions.find { it.class == GenericDebuggerRunner }
96       runProcess(mainClass, myModule, DefaultDebugExecutor, [onTextAvailable: { evt, type -> if (trace) print evt.text}] as ProcessAdapter, runner)
97     }
98     cl.call()
99     def handler = debugProcess.executionResult.processHandler
100     if (trace) {
101       println "terminated1?: " + handler.isProcessTerminated()
102     }
103     resume()
104     if (!handler.waitFor(20000)) {
105       if (handler instanceof OSProcessHandler) {
106         OSProcessManager.instance.killProcessTree(handler.process)
107       } else {
108         println "can't terminate $handler"
109       }
110       fail('too long waiting for process termination')
111     }
112     if (trace) {
113       println "terminated2?: " + handler.isProcessTerminated()
114     }
115   }
116
117   public void testVariableInScript() {
118     myFixture.addFileToProject("Foo.groovy", """def a = 2
119 a""");
120     addBreakpoint 'Foo.groovy', 1
121     runDebugger 'Foo', {
122       waitForBreakpoint()
123       eval 'a', '2'
124       eval '2?:3', '2'
125       eval 'null?:3', '3'
126     }
127   }
128
129   public void testVariableInsideClosure() {
130     myFixture.addFileToProject("Foo.groovy", """def a = 2
131 Closure c = {
132   a++;
133   a    //3
134 }
135 c()
136 a++""");
137     addBreakpoint 'Foo.groovy', 3
138     runDebugger 'Foo', {
139       waitForBreakpoint()
140       eval 'a', '3'
141     }
142   }
143
144   public void testQualifyNames() {
145     myFixture.addFileToProject "com/Goo.groovy", '''
146 package com
147 interface Goo {
148   int mainConstant = 42
149   int secondConstant = 1
150 }
151 '''
152     myFixture.addFileToProject("com/Foo.groovy", """
153 package com
154 class Foo {
155   static bar = 2
156   int field = 3
157 }""")
158
159
160     myFixture.addFileToProject("com/Bar.groovy", """package com
161 import static com.Goo.*
162
163 def lst = [new Foo()] as Set
164 println 2 //4
165 """)
166
167     addBreakpoint 'com/Bar.groovy', 4
168     runDebugger 'com.Bar', {
169       waitForBreakpoint()
170       eval 'Foo.bar', '2'
171       eval 'mainConstant', '42'
172       eval 'secondConstant', '1'
173       eval 'mainConstant - secondConstant', '41'
174       eval '(lst as List<Foo>)[0].field', '3'
175     }
176   }
177
178   public void testClassOutOfSourceRoots() {
179     def tempDir = new TempDirTestFixtureImpl()
180     edt {
181       tempDir.setUp()
182       disposeOnTearDown({ tempDir.tearDown() } as Disposable)
183       ApplicationManager.application.runWriteAction {
184         def model = ModuleRootManager.getInstance(myModule).modifiableModel
185         model.addContentEntry(tempDir.getFile(''))
186         model.commit()
187       }
188     }
189
190     VirtualFile myClass = null
191
192     def mcText = """
193 package foo //1
194
195 class MyClass { //3
196 static def foo(def a) {
197   println a //5
198 }
199 }
200 """
201
202
203     edt {
204       myClass = tempDir.createFile("MyClass.groovy", mcText)
205     }
206
207     addBreakpoint(myClass, 5)
208
209     myFixture.addFileToProject("Foo.groovy", """
210 def cl = new GroovyClassLoader()
211 cl.parseClass('''$mcText''', 'MyClass.groovy').foo(2)
212     """)
213     make()
214
215     runDebugger 'Foo', {
216       waitForBreakpoint()
217       SourcePosition position = managed {
218         EvaluationContextImpl context = evaluationContext()
219         Computable<SourcePosition> a = { ContextUtil.getSourcePosition(context) } as Computable<SourcePosition>
220         SourcePosition pos = ApplicationManager.getApplication().runReadAction (a)
221         pos
222       }
223       assert myClass == position.file.virtualFile
224       eval 'a', '2'
225     }
226   }
227   
228   void testAnonymousClassInScript() {
229     myFixture.addFileToProject('Foo.groovy', '''\
230 new Runnable() {
231   void run() {
232     print 'foo'
233   }
234 }.run()
235
236 ''')
237     addBreakpoint 'Foo.groovy', 2
238     runDebugger 'Foo', {
239       waitForBreakpoint()
240       eval '1+1', '2'
241     }
242   }
243
244   private def addBreakpoint(String fileName, int line) {
245     VirtualFile file = null
246     edt {
247       file = myFixture.tempDirFixture.getFile(fileName)
248     }
249     addBreakpoint(file, line)
250   }
251
252   private def addBreakpoint(VirtualFile file, int line) {
253     edt {
254       DebuggerManagerImpl.getInstanceEx(project).breakpointManager.addLineBreakpoint(FileDocumentManager.instance.getDocument(file), line)
255     }
256   }
257
258   private def resume() {
259     debugProcess.managerThread.invoke(debugProcess.createResumeCommand(debugProcess.suspendManager.pausedContext))
260   }
261
262   private SuspendContextImpl waitForBreakpoint() {
263     int i = 0
264     def suspendManager = debugProcess.suspendManager
265     while (i++ < 1000 && !suspendManager.pausedContext && !debugProcess.executionResult.processHandler.processTerminated) {
266       Thread.sleep(10)
267     }
268
269     def context = suspendManager.pausedContext
270     assert context : "too long process, terminated=$debugProcess.executionResult.processHandler.processTerminated"
271     return context
272   }
273
274   private DebugProcessImpl getDebugProcess() {
275     return getDebugSession().process
276   }
277
278   private DebuggerSession getDebugSession() {
279     return DebuggerPanelsManager.getInstance(project).sessionTab.session
280   }
281
282   private <T> T managed(Closure cl) {
283     def result = null
284     def ctx = DebuggerContextUtil.createDebuggerContext(debugSession, debugProcess.suspendManager.pausedContext)
285     Semaphore semaphore = new Semaphore()
286     semaphore.down()
287     debugProcess.managerThread.invokeAndWait(new DebuggerContextCommandImpl(ctx) {
288       @Override
289       void threadAction() {
290         result = cl()
291         semaphore.up()
292       }
293     })
294     def finished = semaphore.waitFor(20000)
295     assert finished : 'Too long debugger action'
296     return result
297   }
298
299   private void eval(final String codeText, String expected) throws EvaluateException {
300
301     Semaphore semaphore = new Semaphore()
302     semaphore.down()
303     semaphore.down()
304
305     EvaluationContextImpl ctx
306     def item = new WatchItemDescriptor(project, new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, codeText))
307     managed {
308       ctx = evaluationContext()
309       item.setContext(ctx)
310       item.updateRepresentation(ctx, { semaphore.up() } as DescriptorLabelListener)
311     }
312     assert semaphore.waitFor(10000):  "too long evaluation: $item.label $item.evaluateException"
313
314     String result = managed { DebuggerUtils.getValueAsString(ctx, item.value) }
315     assert result == expected
316   }
317
318   private EvaluationContextImpl evaluationContext() {
319     final SuspendContextImpl suspendContext = debugProcess.suspendManager.pausedContext
320     new EvaluationContextImpl(suspendContext, suspendContext.frameProxy, suspendContext.frameProxy.thisObject())
321   }
322 }