92c59b747b0382f9689683146d3f58510841b12b
[idea/community.git] / plugins / groovy / test / org / jetbrains / plugins / groovy / lang / GppFunctionalTest.groovy
1 package org.jetbrains.plugins.groovy.lang
2
3 import com.intellij.codeInsight.generation.OverrideImplementUtil
4 import com.intellij.codeInsight.lookup.LookupManager
5 import com.intellij.codeInsight.navigation.GotoImplementationHandler
6 import com.intellij.openapi.module.Module
7 import com.intellij.openapi.roots.ContentEntry
8 import com.intellij.openapi.roots.ModifiableRootModel
9 import com.intellij.openapi.roots.OrderRootType
10 import com.intellij.openapi.roots.libraries.Library
11 import com.intellij.openapi.vfs.JarFileSystem
12 import com.intellij.testFramework.LightProjectDescriptor
13 import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor
14 import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
15 import junit.framework.ComparisonFailure
16 import org.jetbrains.annotations.NotNull
17 import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilityCheckInspection
18 import org.jetbrains.plugins.groovy.codeInspection.unassignedVariable.UnassignedVariableAccessInspection
19 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition
20 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod
21 import org.jetbrains.plugins.groovy.util.TestUtils
22 import com.intellij.psi.*
23
24 /**
25  * @author peter
26  */
27 class GppFunctionalTest extends LightCodeInsightFixtureTestCase {
28
29   @NotNull
30   @Override
31   protected LightProjectDescriptor getProjectDescriptor() {
32     return GppProjectDescriptor.instance;
33   }
34
35   protected void setUp() {
36     super.setUp()
37   }
38
39   public void testCastListToIterable() throws Exception {
40     myFixture.addClass("class X extends java.util.ArrayList<Integer> {}")
41     testAssignability """
42 X ints = [239, 4.2d]
43 """
44   }
45
46   public void testCastListToAnything() throws Exception {
47     testAssignability """
48 File f1 = ['path']
49 File f2 = <warning descr="Constructor 'File' in 'java.io.File' cannot be applied to '(java.lang.String, java.lang.Integer, java.lang.Boolean, java.lang.Integer)'">['path', 2, true, 42]</warning>
50 """
51   }
52
53   public void testCastMapToAnotherMap() throws Exception {
54     myFixture.addClass """
55 public class Y extends java.util.HashMap<String, String> {
56   public Y(int initialCapacity) {
57     super(initialCapacity);
58   }
59 }
60 """
61
62     testAssignability """
63 HashMap<String, File> m1 = ['a':['b']]
64 Y y = <warning descr="Constructor 'Y' in 'Y' cannot be applied to '(['a':java.lang.String])'">[a:'b']</warning>
65 """
66   }
67
68   public void testAnonymousClass() throws Exception {
69     myFixture.enableInspections new GroovyAssignabilityCheckInspection()
70     testAssignability """
71 def x = new Object() {
72   def foo() {
73     HashMap<String, File> m1 = ['a':['b']]
74     HashMap<String, File> m2 = <warning descr="Cannot assign 'File' to 'HashMap<String, File>'">new File('aaa')</warning>
75   }
76 }
77 """
78   }
79
80   public void testCastMapToObject() throws Exception {
81     myFixture.addClass("class Foo { String name; void foo() {} }")
82     testAssignability """
83 Foo f = [name: 'aaa', foo: { println 'hi' }, anotherProperty: 42 ]
84 """
85   }
86
87   void testAssignability(String text) {
88     myFixture.enableInspections new GroovyAssignabilityCheckInspection()
89     PsiFile file = configureGppScript(text)
90     myFixture.testHighlighting(true, false, false, file.virtualFile)
91   }
92
93   private PsiFile configureScript(String text) {
94     return myFixture.configureByText("a.groovy", text)
95   }
96   private PsiFile configureGppScript(String text) {
97     return myFixture.configureByText("a.gpp", text)
98   }
99
100   public void testDeclaredVariableTypeIsMoreImportantThanTheInitializerOne() throws Exception {
101     configureScript("""
102 File f = ['path']
103 f.mk<caret>
104 """)
105     myFixture.completeBasic()
106     assertSameElements myFixture.lookupElementStrings, "mkdir", "mkdirs"
107   }
108   
109   public void testDeclaredVariableTypeIsMoreImportantThanTheInitializerOne2() throws Exception {
110     myFixture.addClass """
111 public class Some {
112     public int prop
113
114     public void f_foo() {}
115     public void f_bar() {}
116 }
117 """
118
119     configureScript("""
120 Some s = [prop: 239]
121 s.f_<caret>
122 """)
123     myFixture.completeBasic()
124     assertSameElements myFixture.lookupElementStrings, "f_foo", "f_bar"
125   }
126
127   public void testResolveMethod() throws Exception {
128     myFixture.configureByText("a.groovy", """
129 def foo(File f) {}
130 @Typed def bar() {
131   fo<caret>o(['path'])
132 }
133 """)
134     def reference = findReference()
135     def target = reference.resolve()
136     assertEquals "foo", ((GrMethod)target).name
137   }
138
139   private PsiReference findReference() {
140     return myFixture.file.findReferenceAt(myFixture.editor.caretModel.offset)
141   }
142
143   public void testOverloadingWithConversion() throws Exception {
144     myFixture.configureByText("a.groovy", """
145 def foo(List l) {}
146 def foo(File f) {}
147 @Typed def bar() {
148   fo<caret>o(['path'])
149 }
150 """)
151     def reference = findReference()
152     def target = reference.resolve()
153     assertNotNull target
154     assert target.text.contains("List l")
155   }
156
157   public void testWrongProperty() throws Exception {
158 myFixture.configureByText 'a.gpp', '''
159 class ClassA {
160   def prop = 2
161   def bar() {
162     def t = new Object()
163     t.pr<caret>op
164   }
165 }'''
166     assert !findReference().resolve()
167   }
168
169   public void testCastClosureToOneMethodClass() throws Exception {
170     myFixture.addClass """
171 public abstract class Foo {
172   public abstract void foo(String s);
173   public abstract void bar(String s);
174 }
175 public interface Action {
176   void act();
177 }
178 """
179
180     testAssignability """
181 Foo f = <warning descr="Cannot assign 'Closure' to 'Foo'">{ println it }</warning>
182 Function1<String, Object> f1 = { println it }
183 Function1<String, Object> f2 = { x=42 -> println x }
184 Function1<String, Object> f3 = <warning descr="Cannot assign 'Closure' to 'Function1<String, Object>'">{ int x -> println x }</warning>
185 Runnable r = { println it }
186 Action a = { println it }
187 Action a1 = { a2 = 2 -> println a2 }
188 """
189   }
190
191   public void testClosureParameterTypesInAssignment() throws Exception {
192     configureScript "Function1<String, Object> f = { it.subs<caret> }"
193     myFixture.completeBasic()
194     assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring"
195   }
196
197   public void testClosureParameterTypesInMethodInvocation() throws Exception {
198     myFixture.configureByText "a.groovy", """
199 def foo(int a = 1, Function1<String, Object> f) {}
200 def foo(String s) {}
201 def foo(Function2<Integer, String, Object> f) {}
202
203 @Typed def bar() {
204   foo { it.subsREF }
205   foo(1, { it.subsREF })
206   foo 1, { it.subsREF }
207   foo(1) { it.subsREF }
208   foo { a -> a.subsREF }
209   foo { a, int b=2 -> a.subsREF }
210   foo { a, b -> b.subsREF }
211 }
212 """
213     def text = myFixture.file.text
214     def pos = 0
215     while (true) {
216       pos = text.indexOf("REF", pos+1)
217       if (pos < 0) {
218         break
219       }
220       myFixture.editor.caretModel.moveToOffset pos
221       myFixture.completeBasic()
222       try {
223         assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring"
224       }
225       catch (ComparisonFailure ex) {
226         println "at: " + text[0..<pos] + "<caret>" + text[pos..<text.size()]
227         throw ex
228       }
229       LookupManager.getInstance(project).hideActiveLookup()
230     }
231   }
232
233   public void testReturnTypeOneMethodInterface() throws Exception {
234     myFixture.configureByText "a.groovy", """
235 @Typed Function1<String, Integer> bar() {
236    { it.subs<caret> }
237 }
238 """
239     myFixture.completeBasic()
240     assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring"
241   }
242
243   public void testClosureInMapInstantiation() throws Exception {
244     myFixture.configureByText "a.groovy", """
245 class Foo<T> {
246   int foo(T a) {}
247 }
248
249 @Typed Foo<String> bar() {
250     return [foo: { it.subs<caret> }]
251 }
252 """
253     myFixture.completeBasic()
254     assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring"
255   }
256
257   public void testClosureInMapInstantiationBoxPrimitives() throws Exception {
258     myFixture.configureByText "a.groovy", """
259 class Foo {
260   int foo(int a) {}
261 }
262
263 @Typed Foo bar() {
264     return [foo: { it.intV<caret>V }]
265 }
266 """
267     myFixture.completeBasic()
268     assertSameElements myFixture.lookupElementStrings, "intValue"
269   }
270
271   public void testClosureInListInstantiation() throws Exception {
272     myFixture.configureByText "a.groovy", """
273 class Foo {
274   def Foo(int a, Function1<String, Integer> f) {}
275 }
276
277 @Typed Foo foo() {
278   [239, { s -> s.subs<caret> }]
279 }
280 """
281     myFixture.completeBasic()
282     assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring"
283   }
284
285   public void testTraitHighlighting() throws Exception {
286     myFixture.configureByText "a.groovy", """
287 @Trait
288 abstract class Intf {
289   abstract void foo()
290   void bar() {}
291 }
292 <error descr="Method 'foo' is not implemented">class Foo implements Intf</error> {}
293 <error descr="Method 'foo' is not implemented">class Wrong extends Foo</error> {}
294 class Bar implements Intf {
295   void foo() {}
296 }
297 """
298     myFixture.testHighlighting(true, false, false, myFixture.file.virtualFile)
299   }
300
301   public void testTraitImplementingAndNavigation() throws Exception {
302     myFixture.configureByText "a.groovy", """
303 @Trait
304 abstract class <caret>Intf {
305   abstract void foo()
306   void bar() {}
307 }
308 class Foo implements Intf {}
309 class Bar implements Intf {
310   void foo() {}
311 }
312 class BarImpl extends Bar {}
313 """
314     def facade = JavaPsiFacade.getInstance(getProject())
315     assertOneElement(OverrideImplementUtil.getMethodsToOverrideImplement(facade.findClass("Foo"), true))
316
317     GrTypeDefinition barClass = facade.findClass("Bar")
318     assertEmpty(OverrideImplementUtil.getMethodsToOverrideImplement(barClass, true))
319     assertTrue "bar" in OverrideImplementUtil.getMethodsToOverrideImplement(barClass, false).collect { ((PsiMethod) it.element).name }
320
321     assertEmpty(OverrideImplementUtil.getMethodsToOverrideImplement(facade.findClass("BarImpl"), true))
322
323     def implementations = new GotoImplementationHandler().getSourceAndTargetElements(myFixture.editor, myFixture.file).targets
324     assertEquals Arrays.toString(implementations), 3, implementations.size()
325   }
326
327   public void testResolveToStdLib() throws Exception {
328     configureScript """
329 @Typed def foo(List<String> l) {
330   l.ea<caret>ch { it.substring(1) }
331 }
332 """
333     PsiMethod method = resolveReference().navigationElement
334     assertEquals "each", method.name
335     assertEquals "groovypp.util.Iterations", method.containingClass.qualifiedName
336   }
337
338   public void testResolveToStdLibWithArrayQualifier() throws Exception {
339     configureGppScript """
340 Integer[] a = []
341 a.fol<caret>dLeft(2, { a, b -> a+b })
342 """
343     PsiMethod method = resolveReference().navigationElement
344     assertEquals "foldLeft", method.name
345     assertEquals "groovypp.util.Iterations", method.containingClass.qualifiedName
346   }
347
348   private PsiElement resolveReference() {
349     return findReference().resolve()
350   }
351
352   public void testResolveToSuperMethodClosureSyntax() {
353     configureScript """
354 abstract class Super implements Runnable {
355   def method(int bar) {}
356 }
357
358 Super s = { <caret>method(2) } as Super
359 """
360     assert resolveReference() instanceof GrMethod
361   }
362
363   public void testMethodTypeParameterInference() throws Exception {
364     configureScript """
365 @Typed package aaa
366
367 java.util.concurrent.atomic.AtomicReference<Integer> r = [2]
368 r.apply { it.intV<caret>i }
369 """
370     myFixture.completeBasic()
371     assertSameElements myFixture.getLookupElementStrings(), "intValue"
372   }
373
374   public void testMethodTypeParameterInference2() throws Exception {
375     configureScript """
376 @Typed package aaa
377
378 java.util.concurrent.atomic.AtomicReference<Integer> r = [2]
379 r.apply { it.intV<caret>i } {}
380 """
381     myFixture.completeBasic()
382     assertSameElements myFixture.getLookupElementStrings(), "intValue"
383   }
384
385   public void testGotoSuperMethodFromMapLiterals() throws Exception {
386     PsiClass point = myFixture.addClass("""
387 class Point {
388   Point() {}
389   Point(int y) {}
390   int y;
391   void setX(int x) {}
392   void move(int x, int y) {}
393   void move(int y) {}
394 }""")
395
396     configureScript "Point p = [<caret>y:2]"
397     assertEquals point.findFieldByName("y", false), resolveReference()
398
399     configureScript "Point p = [<caret>x:2]"
400     assertEquals point.findMethodsByName("setX", false)[0], resolveReference()
401
402     configureScript "Point p = [mo<caret>ve: { x, y -> z }]"
403     assertEquals point.findMethodsByName("move", false)[0], resolveReference()
404
405     configureScript "Point p = [mo<caret>ve: ]"
406     def resolveResults = multiResolveReference()
407     assertSameElements resolveResults.collect { it.element }, point.findMethodsByName("move", false)
408   }
409
410   ResolveResult[] multiResolveReference() {
411     return ((PsiPolyVariantReference) myFixture.file.findReferenceAt(myFixture.editor.caretModel.offset)).multiResolve(true)
412   }
413
414   public void testGotoSuperConstructorFromMapLiterals() throws Exception {
415     PsiClass point = myFixture.addClass("""
416 class Point {
417   Point() {}
418   Point(int y) {}
419 }""")
420
421     configureGppScript "Point p = [su<caret>per: 2]"
422     assertEquals point.constructors[1], resolveReference()
423
424     configureGppScript "Point p = [su<caret>per: [2]]"
425     assertEquals point.constructors[1], resolveReference()
426
427     configureGppScript "Point p = ['su<caret>per': []]"
428     assertEquals point.constructors[0], resolveReference()
429
430     configureGppScript "Point p = ['su<caret>per': 'a']"
431     assertEquals 1, multiResolveReference().size()
432   }
433
434   public void testGotoSuperConstructorFromLiteralOnsets() throws Exception {
435     PsiClass point = myFixture.addClass("""
436 class Point {
437   Point() {}
438   Point(int y) {}
439 }""")
440
441     configureGppScript "Point p = <caret>[super: 2]"
442     assertEquals point.constructors[1], resolveReference()
443
444     configureGppScript "Point p = <caret>[2]"
445     assertEquals point.constructors[1], resolveReference()
446
447     configureGppScript "Point p = <caret>[]"
448     assertEquals point.constructors[0], resolveReference()
449
450     configureGppScript "Point p = <caret>[:]"
451     assertEquals point.constructors[0], resolveReference()
452
453     configureGppScript "Point p = <caret>[239, 42]"
454     assertEquals 2, multiResolveReference().size()
455
456     configureGppScript """
457 def foo(Point p) {}
458 foo(<caret>[2, 3])
459 """
460     assertEquals 2, multiResolveReference().size()
461
462     configureGppScript """
463 def foo(Point... p) {}
464 foo(<caret>['super':[2, 3]])
465 """
466     assertEquals 2, multiResolveReference().size()
467   }
468
469   public void testGotoClassFromLiteralOnsetsWhenNoConstructorsPresent() throws Exception {
470     PsiClass point = myFixture.addClass(""" class Point { }""")
471     configureGppScript "Point p = <caret>[super: 2]"
472     assertEquals point, resolveReference()
473
474     configureGppScript "Point p = <caret>[]"
475     assertEquals point, resolveReference()
476   }
477
478   public void testNoGotoObjectFromLiteral() throws Exception {
479     myFixture.addClass(""" class Point { }""")
480
481     configureGppScript "def p = <caret>[]"
482     assertNull findReference()
483   }
484
485   public void testHighlightInapplicableLiteralConstructor() throws Exception {
486     myFixture.addClass("""
487 class Point {
488   Point() {}
489 }""")
490
491     configureGppScript """
492 def foo(Point p) {}
493 Point p = [:]
494 Point p2 = [super:warning descr="Cannot find constructor of 'Point'">[4, 2]</warning>]
495 foo(<warning descr="Cannot find constructor of 'Point'">[4, 2]</warning>)
496 """
497   }
498
499   public void testResolveTraitMethod() throws Exception {
500     configureScript """
501 @Trait
502 class Some {
503         public void doSmth() { println "hello" }
504 }
505 Some s
506 s.do<caret>Smth()
507 """
508     assertEquals "doSmth", ((PsiMethod) findReference().resolve()).name
509   }
510
511   public void testBaseConstructorCallInMapLiteras() throws Exception {
512     configureScript """
513 @Typed File foo() { <warning descr="Constructor 'File' in 'java.io.File' cannot be applied to '(['super':[java.lang.String]])'">['super':['a']]</warning> }
514 @Typed File goo() { <warning descr="Constructor 'File' in 'java.io.File' cannot be applied to '([:])'">[:]</warning> }
515 File bar() { <warning descr="Constructor 'File' in 'java.io.File' cannot be applied to '([:])'">[:]</warning> }
516 """
517     myFixture.enableInspections new GroovyAssignabilityCheckInspection()
518     myFixture.checkHighlighting(true, false, false)
519   }
520
521   public void testNestedLiteralConstructors() throws Exception {
522     configureGppScript """
523     class Foo {
524       def Foo(Bar b) { }
525     }
526
527     class Bar {
528       def Bar(int i) { }
529     }
530
531     Foo x = <warning descr="Constructor 'Foo' in 'Foo' cannot be applied to '([java.lang.Integer])'">[[2]]</warning>
532     println x
533 """
534     myFixture.enableInspections new GroovyAssignabilityCheckInspection()
535     myFixture.checkHighlighting(true, false, false)
536   }
537
538   public void testNoReturnTypeInferenceInTypedContext() throws Exception {
539     configureGppScript """
540 class Foo {
541   def foo() { "aaa" }
542 }
543 new Foo().foo().substr<caret>a
544 """
545     assertEmpty myFixture.completeBasic()
546   }
547
548   public void testDeclaredReturnTypeInTypedContext() throws Exception {
549     configureGppScript """
550 class Foo {
551   String getFoo() { "aaa" }
552 }
553 new Foo().foo.substr<caret>a
554 """
555     myFixture.completeBasic()
556     assertOrderedEquals myFixture.lookupElementStrings, "substring", "substring"
557   }
558
559   public void testNonInitializedVariable() throws Exception {
560     configureScript """
561
562 @Typed
563 def foo() {
564   int a
565   return a
566 }
567
568 def bar() {
569   int a
570   return <warning descr="Variable 'a' might not be assigned">a</warning>
571 }"""
572     myFixture.enableInspections new UnassignedVariableAccessInspection()
573     myFixture.checkHighlighting(true, false, false)
574   }
575
576   public void testExternalizable() throws Exception {
577     configureScript '''
578 @Typed class Foo implements Externalizable {}
579 <error descr="Method 'writeExternal' is not implemented">class Bar implements Externalizable</error> {}
580 '''
581     myFixture.checkHighlighting(true, false, false)
582   }
583
584 }
585
586 class GppProjectDescriptor extends DefaultLightProjectDescriptor {
587   static def instance = new GppProjectDescriptor()
588
589   @Override
590     public void configureModule(Module module, ModifiableRootModel model, ContentEntry contentEntry) {
591     final Library.ModifiableModel modifiableModel = model.getModuleLibraryTable().createLibrary("GROOVY++").getModifiableModel();
592     modifiableModel.addRoot(JarFileSystem.instance.refreshAndFindFileByPath(TestUtils.absoluteTestDataPath + "mockGroovypp/groovypp-0.9.0_1.8.2.jar!/"), OrderRootType.CLASSES)
593     modifiableModel.addRoot(JarFileSystem.instance.refreshAndFindFileByPath(TestUtils.mockGroovy1_7LibraryName + "!/"), OrderRootType.CLASSES);
594     modifiableModel.commit();
595   }
596 }