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