c4cbb19cfe86549328dbee5faf3c6b11a981d5fa
[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
51 /**
52  * @author peter
53  */
54 class GroovyDebuggerTest extends GroovyCompilerTestCase {
55   @Override
56   protected boolean useJps() { false }
57
58   @Override
59   protected void setUp() {
60     edt {
61       super.setUp()
62       addGroovyLibrary(myModule);
63     }
64
65   }
66
67   @Override
68   protected boolean runInDispatchThread() {
69     return false
70   }
71
72   @Override
73   protected void invokeTestRunnable(Runnable runnable) {
74     runnable.run()
75   }
76
77   @Override
78   protected void tearDown() {
79     super.tearDown()
80   }
81
82   @Override
83   protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) {
84     super.tuneFixture(moduleBuilder)
85     def javaHome = FileUtil.toSystemIndependentName(SystemProperties.getJavaHome())
86     moduleBuilder.addJdk(StringUtil.trimEnd(StringUtil.trimEnd(javaHome, '/'), '/jre'))
87   }
88
89   private void runDebugger(String mainClass, Closure cl) {
90     boolean trace = false//name == 'testClassOutOfSourceRoots'
91     make()
92     edt {
93       ProgramRunner runner = ProgramRunner.PROGRAM_RUNNER_EP.extensions.find { it.class == GenericDebuggerRunner }
94       runProcess(mainClass, myModule, DefaultDebugExecutor, [onTextAvailable: { evt, type -> if (trace) print evt.text}] as ProcessAdapter, runner)
95     }
96     cl.call()
97     if (trace) {
98       println "terminated1?: " + debugProcess.executionResult.processHandler.isProcessTerminated()
99     }
100     resume()
101     debugProcess.executionResult.processHandler.waitFor()
102     if (trace) {
103       println "terminated2?: " + debugProcess.executionResult.processHandler.isProcessTerminated()
104     }
105   }
106
107   public void testVariableInScript() {
108     myFixture.addFileToProject("Foo.groovy", """def a = 2
109 a""");
110     addBreakpoint 'Foo.groovy', 1
111     runDebugger 'Foo', {
112       waitForBreakpoint()
113       eval 'a', '2'
114       eval '2?:3', '2'
115       eval 'null?:3', '3'
116     }
117   }
118
119   public void testVariableInsideClosure() {
120     myFixture.addFileToProject("Foo.groovy", """def a = 2
121 Closure c = {
122   a++;
123   a    //3
124 }
125 c()
126 a++""");
127     addBreakpoint 'Foo.groovy', 3
128     runDebugger 'Foo', {
129       waitForBreakpoint()
130       eval 'a', '3'
131     }
132   }
133
134   public void testQualifyNames() {
135     myFixture.addFileToProject "com/Goo.groovy", '''
136 package com
137 interface Goo {
138   int mainConstant = 42
139   int secondConstant = 1
140 }
141 '''
142     myFixture.addFileToProject("com/Foo.groovy", """
143 package com
144 class Foo {
145   static bar = 2
146   int field = 3
147 }""")
148
149
150     myFixture.addFileToProject("com/Bar.groovy", """package com
151 import static com.Goo.*
152
153 def lst = [new Foo()] as Set
154 println 2 //4
155 """)
156
157     addBreakpoint 'com/Bar.groovy', 4
158     runDebugger 'com.Bar', {
159       waitForBreakpoint()
160       eval 'Foo.bar', '2'
161       eval 'mainConstant', '42'
162       eval 'secondConstant', '1'
163       eval 'mainConstant - secondConstant', '41'
164       eval '(lst as List<Foo>)[0].field', '3'
165     }
166   }
167
168   public void testClassOutOfSourceRoots() {
169     def tempDir = new TempDirTestFixtureImpl()
170     edt {
171       tempDir.setUp()
172       disposeOnTearDown({ tempDir.tearDown() } as Disposable)
173       ApplicationManager.application.runWriteAction {
174         def model = ModuleRootManager.getInstance(myModule).modifiableModel
175         model.addContentEntry(tempDir.getFile(''))
176         model.commit()
177       }
178     }
179
180     VirtualFile myClass = null
181
182     def mcText = """
183 package foo //1
184
185 class MyClass { //3
186 static def foo(def a) {
187   println a //5
188 }
189 }
190 """
191
192
193     edt {
194       myClass = tempDir.createFile("MyClass.groovy", mcText)
195     }
196
197     addBreakpoint(myClass, 5)
198
199     myFixture.addFileToProject("Foo.groovy", """
200 def cl = new GroovyClassLoader()
201 cl.parseClass('''$mcText''', 'MyClass.groovy').foo(2)
202     """)
203     make()
204
205     runDebugger 'Foo', {
206       waitForBreakpoint()
207       SourcePosition position = managed {
208         EvaluationContextImpl context = evaluationContext()
209         Computable<SourcePosition> a = { ContextUtil.getSourcePosition(context) } as Computable<SourcePosition>
210         SourcePosition pos = ApplicationManager.getApplication().runReadAction (a)
211         pos
212       }
213       assert myClass == position.file.virtualFile
214       eval 'a', '2'
215     }
216   }
217   
218   void testAnonymousClassInScript() {
219     myFixture.addFileToProject('Foo.groovy', '''\
220 new Runnable() {
221   void run() {
222     print 'foo'
223   }
224 }.run()
225
226 ''')
227     addBreakpoint 'Foo.groovy', 2
228     runDebugger 'Foo', {
229       waitForBreakpoint()
230       eval '1+1', '2'
231     }
232   }
233
234   private def addBreakpoint(String fileName, int line) {
235     VirtualFile file = null
236     edt {
237       file = myFixture.tempDirFixture.getFile(fileName)
238     }
239     addBreakpoint(file, line)
240   }
241
242   private def addBreakpoint(VirtualFile file, int line) {
243     edt {
244       DebuggerManagerImpl.getInstanceEx(project).breakpointManager.addLineBreakpoint(FileDocumentManager.instance.getDocument(file), line)
245     }
246   }
247
248   private def resume() {
249     debugProcess.managerThread.invoke(debugProcess.createResumeCommand(debugProcess.suspendManager.pausedContext))
250   }
251
252   private SuspendContextImpl waitForBreakpoint() {
253     int i = 0
254     def suspendManager = debugProcess.suspendManager
255     while (i++ < 1000 && !suspendManager.pausedContext && !debugProcess.executionResult.processHandler.processTerminated) {
256       Thread.sleep(10)
257     }
258
259     def context = suspendManager.pausedContext
260     assert context : "too long process, terminated=$debugProcess.executionResult.processHandler.processTerminated"
261     return context
262   }
263
264   private DebugProcessImpl getDebugProcess() {
265     return getDebugSession().process
266   }
267
268   private DebuggerSession getDebugSession() {
269     return DebuggerPanelsManager.getInstance(project).sessionTab.session
270   }
271
272   private <T> T managed(Closure cl) {
273     def result = null
274     def ctx = DebuggerContextUtil.createDebuggerContext(debugSession, debugProcess.suspendManager.pausedContext)
275     Semaphore semaphore = new Semaphore()
276     semaphore.down()
277     debugProcess.managerThread.invokeAndWait(new DebuggerContextCommandImpl(ctx) {
278       @Override
279       void threadAction() {
280         result = cl()
281         semaphore.up()
282       }
283     })
284     def finished = semaphore.waitFor(20000)
285     assert finished : 'Too long debugger action'
286     return result
287   }
288
289   private void eval(final String codeText, String expected) throws EvaluateException {
290
291     Semaphore semaphore = new Semaphore()
292     semaphore.down()
293     semaphore.down()
294
295     EvaluationContextImpl ctx
296     def item = new WatchItemDescriptor(project, new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, codeText))
297     managed {
298       ctx = evaluationContext()
299       item.setContext(ctx)
300       item.updateRepresentation(ctx, { semaphore.up() } as DescriptorLabelListener)
301     }
302     assert semaphore.waitFor(10000):  "too long evaluation: $item.label $item.evaluateException"
303
304     String result = managed { DebuggerUtils.getValueAsString(ctx, item.value) }
305     assert result == expected
306   }
307
308   private EvaluationContextImpl evaluationContext() {
309     final SuspendContextImpl suspendContext = debugProcess.suspendManager.pausedContext
310     new EvaluationContextImpl(suspendContext, suspendContext.frameProxy, suspendContext.frameProxy.thisObject())
311   }
312 }