40e002e5badb96e612a1a8e792cf21f14d7f7708
[idea/community.git] / java / java-tests / testSrc / com / intellij / navigation / ChooseByNameTest.groovy
1 /*
2  * Copyright 2000-2014 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 com.intellij.navigation
17
18 import com.intellij.ide.util.gotoByName.*
19 import com.intellij.lang.java.JavaLanguage
20 import com.intellij.openapi.Disposable
21 import com.intellij.openapi.application.ApplicationManager
22 import com.intellij.openapi.application.ModalityState
23 import com.intellij.openapi.util.Computable
24 import com.intellij.openapi.util.Disposer
25 import com.intellij.psi.PsiElement
26 import com.intellij.psi.PsiFile
27 import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
28 import com.intellij.util.Consumer
29 import com.intellij.util.concurrency.Semaphore
30 import org.jetbrains.annotations.NotNull
31 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
32
33 /**
34  * @author peter
35  */
36 class ChooseByNameTest extends LightCodeInsightFixtureTestCase {
37   ChooseByNamePopup myPopup
38
39   @Override
40   protected void tearDown() throws Exception {
41     myPopup = null
42     super.tearDown()
43   }
44
45   public void "test goto class order by matching degree"() {
46     def startMatch = myFixture.addClass("class UiUtil {}")
47     def wordSkipMatch = myFixture.addClass("class UiAbstractUtil {}")
48     def camelMatch = myFixture.addClass("class UberInstructionUxTopicInterface {}")
49     def middleMatch = myFixture.addClass("class BaseUiUtil {}")
50     def elements = getPopupElements(new GotoClassModel2(project), "uiuti")
51     assert elements == [startMatch, wordSkipMatch, camelMatch, ChooseByNameBase.NON_PREFIX_SEPARATOR, middleMatch]
52   }
53
54   public void "test annotation syntax"() {
55     def match = myFixture.addClass("@interface Anno1 {}")
56     myFixture.addClass("class Anno2 {}")
57     def elements = getPopupElements(new GotoClassModel2(project), "@Anno")
58     assert elements == [match]
59   }
60
61   public void "test no result for empty patterns"() {
62     myFixture.addClass("@interface Anno1 {}")
63     myFixture.addClass("class Anno2 {}")
64
65     def popup = createPopup(new GotoClassModel2(project))
66     assert calcPopupElements(popup, "") == []
67     popup.close(false)
68
69     popup = createPopup(new GotoClassModel2(project))
70     assert calcPopupElements(popup, "@") == []
71     popup.close(false)
72
73     popup = createPopup(new GotoFileModel(project))
74     assert calcPopupElements(popup, "foo/") == []
75     popup.close(false)
76   }
77
78   public void "test filter overridden methods from goto symbol"() {
79     def intf = myFixture.addClass("""
80 class Intf {
81   void xxx1() {}
82   void xxx2() {}
83 }""")
84     def impl = myFixture.addClass("""
85 class Impl extends Intf {
86     void xxx1() {}
87     void xxx3() {}
88 }
89 """)
90
91     def elements = getPopupElements(new GotoSymbolModel2(project), "xxx")
92
93     assert intf.findMethodsByName('xxx1', false)[0] in elements
94     assert intf.findMethodsByName('xxx2', false)[0] in elements
95
96     assert impl.findMethodsByName('xxx3', false)[0] in elements
97     assert !(impl.findMethodsByName('xxx1', false)[0] in elements)
98   }
99
100   public void "test disprefer underscore"() {
101     def intf = myFixture.addClass("""
102 class Intf {
103   void _xxx1() {}
104   void xxx2() {}
105 }""")
106
107     def elements = getPopupElements(new GotoSymbolModel2(project), "xxx")
108
109     def xxx1
110     def xxx2
111     edt {
112       xxx1 = intf.findMethodsByName('_xxx1', false)
113       xxx2 = intf.findMethodsByName('xxx2', false)
114     }
115     assert elements == [xxx2, ChooseByNameBase.NON_PREFIX_SEPARATOR, xxx1]
116   }
117
118   public void "test prefer exact extension matches"() {
119     def m = myFixture.addFileToProject("relaunch.m", "")
120     def mod = myFixture.addFileToProject("reference.mod", "")
121     def elements = getPopupElements(new GotoFileModel(project), "re*.m")
122     assert elements == [m, mod]
123   }
124
125   public void "test consider dot-idea files out of project"() {
126     def outside = myFixture.addFileToProject(".idea/workspace.xml", "")
127     def inside = myFixture.addFileToProject("workspace.txt", "")
128     assert getPopupElements(new GotoFileModel(project), "work", false) == [inside]
129     assert getPopupElements(new GotoFileModel(project), "work", true) == [inside, outside]
130   }
131
132   public void "test prefer better path matches"() {
133     def fooIndex = myFixture.addFileToProject("foo/index.html", "foo")
134     def fooBarIndex = myFixture.addFileToProject("foo/bar/index.html", "foo bar")
135     def barFooIndex = myFixture.addFileToProject("bar/foo/index.html", "bar foo")
136     def elements = getPopupElements(new GotoFileModel(project), "foo/index")
137     assert elements == [fooIndex, barFooIndex, fooBarIndex]
138   }
139
140   public void "test sort same-named items by path"() {
141     def files = (30..10).collect { i -> myFixture.addFileToProject("foo$i/index.html", "foo$i") }.reverse()
142     def elements = getPopupElements(new GotoFileModel(project), "index")
143     assert elements == files
144   }
145
146   public void "test middle matching for directories"() {
147     def fooIndex = myFixture.addFileToProject("foo/index.html", "foo")
148     def ooIndex = myFixture.addFileToProject("oo/index.html", "oo")
149     def fooBarIndex = myFixture.addFileToProject("foo/bar/index.html", "foo bar")
150     def elements = getPopupElements(new GotoFileModel(project), "oo/index")
151     assert elements == [ooIndex, fooIndex, fooBarIndex]
152   }
153
154   public void "test prefer files from current directory"() {
155     def fooIndex = myFixture.addFileToProject("foo/index.html", "foo")
156     def barIndex = myFixture.addFileToProject("bar/index.html", "bar")
157     def fooContext = myFixture.addFileToProject("foo/context.html", "")
158     def barContext = myFixture.addFileToProject("bar/context.html", "")
159
160     def popup = createPopup(new GotoFileModel(project), fooContext)
161     assert calcPopupElements(popup, "index") == [fooIndex, barIndex]
162     popup.close(false)
163
164     popup = createPopup(new GotoFileModel(project), barContext)
165     assert calcPopupElements(popup, "index") == [barIndex, fooIndex]
166
167   }
168
169   public void "test goto file can go to dir"() {
170     PsiFile fooIndex = myFixture.addFileToProject("foo/index.html", "foo")
171     PsiFile barIndex = myFixture.addFileToProject("bar.txt/bar.txt", "foo")
172
173     def popup = createPopup(new GotoFileModel(project), fooIndex)
174
175     def fooDir
176     def barDir
177     edt {
178       fooDir = fooIndex.containingDirectory
179       barDir = barIndex.containingDirectory
180     }
181
182     assert calcPopupElements(popup, "foo/") == [fooDir]
183     assert calcPopupElements(popup, "foo\\") == [fooDir]
184     assert calcPopupElements(popup, "/foo") == [fooDir]
185     assert calcPopupElements(popup, "\\foo") == [fooDir]
186     assert calcPopupElements(popup, "foo") == []
187     assert calcPopupElements(popup, "/index.html") == [fooIndex]
188     assert calcPopupElements(popup, "\\index.html") == [fooIndex]
189     assert calcPopupElements(popup, "index.html/") == [fooIndex]
190     assert calcPopupElements(popup, "index.html\\") == [fooIndex]
191
192     assert calcPopupElements(popup, "bar.txt/") == [barDir]
193     assert calcPopupElements(popup, "bar.txt\\") == [barDir]
194     assert calcPopupElements(popup, "/bar.txt") == [barDir]
195     assert calcPopupElements(popup, "\\bar.txt") == [barDir]
196     assert calcPopupElements(popup, "bar.txt") == [barIndex]
197     popup.close(false)
198   }
199
200   public void "test find method by qualified name"() {
201     def clazz = myFixture.addClass("package foo.bar; class Goo { void zzzZzz() {} }")
202     def method = ApplicationManager.application.runReadAction( { clazz.methods[0] } as Computable)
203     assert getPopupElements(new GotoSymbolModel2(project), 'zzzZzz') == [method]
204     assert getPopupElements(new GotoSymbolModel2(project), 'goo.zzzZzz') == [method]
205     assert getPopupElements(new GotoSymbolModel2(project), 'foo.bar.goo.zzzZzz') == [method]
206     assert getPopupElements(new GotoSymbolModel2(project), 'foo.zzzZzz') == [method]
207     assert getPopupElements(new GotoSymbolModel2(project), 'bar.zzzZzz') == [method]
208     assert getPopupElements(new GotoSymbolModel2(project), 'bar.goo.zzzZzz') == [method]
209   }
210
211   public void "test line and column suffix"() {
212     def c = myFixture.addClass("package foo; class Bar {}")
213     assert getPopupElements(new GotoClassModel2(project), 'Bar') == [c]
214     assert getPopupElements(new GotoClassModel2(project), 'Bar:2') == [c]
215     assert getPopupElements(new GotoClassModel2(project), 'Bar:2:3') == [c]
216     assert getPopupElements(new GotoClassModel2(project), 'Bar:[2:3]') == [c]
217     assert getPopupElements(new GotoClassModel2(project), 'Bar:[2,3]') == [c]
218   }
219
220   public void "test custom line suffixes"() {
221     def file = myFixture.addFileToProject("Bar.txt", "")
222     def model = new GotoFileModel(project)
223     assert getPopupElements(model, 'Bar:2') == [file]
224     assert getPopupElements(model, 'Bar(2)') == [file]
225     assert getPopupElements(model, 'Bar on line 2') == [file]
226     assert getPopupElements(model, 'Bar at line 2') == [file]
227   }
228
229   public void "test dollar"() {
230     def bar = myFixture.addClass("package foo; class Bar { class Foo {} }")
231     def foo = ApplicationManager.application.runReadAction( { bar.innerClasses[0] } as Computable)
232     myFixture.addClass("package goo; class Goo { }")
233     assert getPopupElements(new GotoClassModel2(project), 'Bar$Foo') == [foo]
234     assert getPopupElements(new GotoClassModel2(project), 'foo.Bar$Foo') == [foo]
235     assert getPopupElements(new GotoClassModel2(project), 'foo.B$F') == [foo]
236     assert !getPopupElements(new GotoClassModel2(project), 'foo$Foo')
237     assert !getPopupElements(new GotoClassModel2(project), 'foo$Bar')
238     assert !getPopupElements(new GotoClassModel2(project), 'foo$Bar$Foo')
239     assert !getPopupElements(new GotoClassModel2(project), 'foo$Goo')
240   }
241
242   public void "test anonymous classes"() {
243     def goo = myFixture.addClass("package goo; class Goo { Runnable r = new Runnable() {}; }")
244     assert getPopupElements(new GotoClassModel2(project), 'Goo$1') == [goo]
245   }
246
247   public void "test qualified name matching"() {
248     def bar = myFixture.addClass("package foo.bar; class Bar { }")
249     def bar2 = myFixture.addClass("package goo.baz; class Bar { }")
250     assert getPopupElements(new GotoClassModel2(project), 'foo.Bar') == [bar]
251     assert getPopupElements(new GotoClassModel2(project), 'foo.bar.Bar') == [bar]
252     assert getPopupElements(new GotoClassModel2(project), 'goo.Bar') == [bar2]
253     assert getPopupElements(new GotoClassModel2(project), 'goo.baz.Bar') == [bar2]
254   }
255
256   public void "test try lowercase pattern if nothing matches"() {
257     def match = myFixture.addClass("class IPRoi { }")
258     def nonMatch = myFixture.addClass("class InspectionProfileImpl { }")
259     assert getPopupElements(new GotoClassModel2(project), 'IPRoi') == [match]
260     assert getPopupElements(new GotoClassModel2(project), 'IproImpl') == [nonMatch]
261   }
262
263   private static filterJavaItems(List<Object> items) {
264     return ApplicationManager.application.runReadAction ({
265       return items.findAll { it instanceof PsiElement && it.language == JavaLanguage.INSTANCE }
266     } as Computable)
267   }
268
269   public void "test super method in jdk"() {
270     def clazz = myFixture.addClass("package foo.bar; class Goo implements Runnable { public void run() {} }")
271     def ourRun
272     def sdkRun
273     edt {
274       ourRun = clazz.methods[0]
275       sdkRun = ourRun.containingClass.interfaces[0].methods[0]
276     }
277
278     def withLibs = filterJavaItems(getPopupElements(new GotoSymbolModel2(project), 'run ', true))
279     assert withLibs == [sdkRun]
280     assert !(ourRun in withLibs)
281
282     def noLibs = filterJavaItems(getPopupElements(new GotoSymbolModel2(project), 'run ', false))
283     assert noLibs == [ourRun]
284     assert !(sdkRun in noLibs)
285   }
286
287   public void "test super method not matching query qualifier"() {
288     def baseClass = myFixture.addClass("class Base { void xpaint() {} }")
289     def subClass = myFixture.addClass("class Sub extends Base { void xpaint() {} }")
290     
291     def base
292     def sub
293     edt {
294       base = baseClass.methods[0]
295       sub = subClass.methods[0]
296     }
297
298     assert getPopupElements(new GotoSymbolModel2(project), 'Ba.xpai', false) == [base]
299     assert getPopupElements(new GotoSymbolModel2(project), 'Su.xpai', false) == [sub]
300   }
301
302   public void "test groovy script class with non-identifier name"() {
303     GroovyFile file1 = myFixture.addFileToProject('foo.groovy', '')
304     GroovyFile file2 = myFixture.addFileToProject('foo-bar.groovy', '')
305
306     def variants = getPopupElements(new GotoSymbolModel2(project), 'foo', false)
307     edt { assert variants == [file1.scriptClass, file2.scriptClass] }
308   }
309
310   private List<Object> getPopupElements(ChooseByNameModel model, String text, boolean checkboxState = false) {
311     return calcPopupElements(createPopup(model), text, checkboxState)
312   }
313
314   static ArrayList<Object> calcPopupElements(ChooseByNamePopup popup, String text, boolean checkboxState = false) {
315     List<Object> elements = ['empty']
316     def semaphore = new Semaphore()
317     semaphore.down()
318     edt {
319       popup.scheduleCalcElements(text, checkboxState, ModalityState.NON_MODAL, { set ->
320         elements = set as List
321         semaphore.up()
322       } as Consumer<Set<?>>)
323     }
324     if (!semaphore.waitFor(10000)) {
325       printThreadDump()
326       fail()
327     }
328     return elements
329   }
330
331   private ChooseByNamePopup createPopup(ChooseByNameModel model, PsiElement context = null) {
332     if (myPopup) {
333       myPopup.close(false)
334     }
335
336     edt {
337       def popup = myPopup = ChooseByNamePopup.createPopup(project, model, (PsiElement)context, "")
338       Disposer.register(testRootDisposable, { popup.close(false) } as Disposable)
339     }
340     myPopup
341   }
342
343   @Override
344   protected boolean runInDispatchThread() {
345     return false
346   }
347
348   @Override
349   protected void invokeTestRunnable(@NotNull Runnable runnable) throws Exception {
350     runnable.run()
351   }
352 }