// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.groovy.lang.resolve;

import com.intellij.psi.*;
import com.intellij.testFramework.UsefulTestCase;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.LightGroovyTestCase;
import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilityCheckInspection;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyReference;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrReflectedMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.typedef.members.GrMethodImpl;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.*;
import org.jetbrains.plugins.groovy.lang.resolve.references.GrOperatorReference;
import org.jetbrains.plugins.groovy.util.GroovyLatestTest;
import org.jetbrains.plugins.groovy.util.ResolveTest;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

public class ResolveMethodTest extends GroovyLatestTest implements ResolveTest {

  public ResolveMethodTest() {
    super("resolve/method");
  }

  @NotNull
  private PsiReference configureByFile(@NonNls String filePath) {
    getFixture().configureByFile(getTestName() + "/" + filePath);
    return referenceUnderCaret(PsiReference.class);
  }

  @Nullable
  private PsiElement resolve(String fileName) {
    return configureByFile(fileName).resolve();
  }

  @Test
  public void staticImport3() {
    getFixture().copyFileToProject(getTestName() + "/org/Shrimp.groovy", "org/Shrimp.groovy");
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrMethod);
    Assert.assertEquals(1, ((GrMethod)resolved).getParameters().length);
    Assert.assertEquals("isShrimp", ((GrMethod)resolved).getName());
  }

  @Test
  public void staticImport() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Ignore("static imports with non fully qualified names are not supported")
  @Test
  public void importStaticReverse() {
    PsiReference ref = configureByFile("ImportStaticReverse.groovy");
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void simple() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrMethod);
    Assert.assertEquals(1, ((GrMethod)resolved).getParameters().length);
  }

  @Test
  public void varargs() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrMethod);
    Assert.assertEquals(1, ((GrMethod)resolved).getParameters().length);
  }

  @Test
  public void byName() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrMethod);
  }

  @Test
  public void byName1() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrMethod);
    Assert.assertEquals(2, ((GrMethod)resolved).getParameters().length);
  }

  @Test
  public void byNameVarargs() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrMethod);
    Assert.assertEquals(1, ((GrMethod)resolved).getParameters().length);
  }

  @Test
  public void parametersNumber() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrMethod);
    Assert.assertEquals(2, ((GrMethod)resolved).getParameters().length);
  }

  @Test
  public void filterBase() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    Assert.assertNotNull(ref.resolve());
    Assert.assertEquals(1, ref.multiResolve(false).length);
  }

  @Test
  public void twoCandidates() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    Assert.assertNull(ref.resolve());
    Assert.assertEquals(2, ref.multiResolve(false).length);
  }

  @Test
  public void defaultMethod1() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrGdkMethod);
  }

  @Test
  public void defaultStaticMethod() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrGdkMethodImpl);
    Assert.assertTrue(((GrGdkMethodImpl)resolved).hasModifierProperty(PsiModifier.STATIC));
  }

  @Test
  public void primitiveSubtyping() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrGdkMethodImpl);
    Assert.assertTrue(((GrGdkMethodImpl)resolved).hasModifierProperty(PsiModifier.STATIC));
  }

  @Test
  public void defaultMethod2() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
    Assert.assertTrue(resolved instanceof GrGdkMethod);
  }

  @Test
  public void grvy111() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
    Assert.assertTrue(resolved instanceof GrGdkMethod);
    Assert.assertEquals(0, ((PsiMethod)resolved).getParameterList().getParametersCount());
    Assert.assertTrue(((PsiMethod)resolved).hasModifierProperty(PsiModifier.PUBLIC));
  }

  @Test
  public void scriptMethod() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
    Assert.assertEquals("groovy.lang.Script", ((PsiMethod)resolved).getContainingClass().getQualifiedName());
  }

  @Test
  public void arrayDefault() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
    Assert.assertTrue(resolved instanceof GrGdkMethod);
  }

  @Test
  public void arrayDefault1() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
    Assert.assertTrue(resolved instanceof GrGdkMethod);
  }

  @Test
  public void spreadOperator() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
    GrMethodCallExpression methodCall = (GrMethodCallExpression)ref.getElement().getParent();
    PsiType type = methodCall.getType();
    Assert.assertTrue(type instanceof PsiClassType);
    PsiClass clazz = ((PsiClassType)type).resolve();
    Assert.assertNotNull(clazz);
    Assert.assertEquals("java.util.ArrayList", clazz.getQualifiedName());
  }

  @Test
  public void langClass() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
    Assert.assertEquals("java.lang.Class", ((PsiMethod)resolved).getContainingClass().getQualifiedName());
  }

  @Test
  public void complexOverload() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void fromGetter() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void overload1() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
    Assert.assertEquals("java.io.Serializable", ((PsiMethod)resolved).getParameterList().getParameters()[0].getType().getCanonicalText());
  }

  @Test
  public void constructor() {
    PsiReference ref = configureByFile("A.groovy");
    PsiMethod resolved = ((GrNewExpression)ref.getElement().getParent()).resolveMethod();
    Assert.assertNotNull(resolved);
    Assert.assertTrue(resolved.isConstructor());
  }

  @Test
  public void constructor1() {
    PsiReference ref = configureByFile("A.groovy");
    PsiMethod method = ((GrNewExpression)ref.getElement().getParent()).resolveMethod();
    Assert.assertNotNull(method);
    Assert.assertTrue(method.isConstructor());
    Assert.assertEquals(0, method.getParameterList().getParameters().length);
  }

  @Test
  public void constructor2() {
    PsiReference ref = configureByFile("A.groovy");
    PsiMethod method = ((GrNewExpression)ref.getElement().getParent()).resolveMethod();
    Assert.assertNull(method);
  }

  @Test
  public void constructor3() {
    PsiReference ref = configureByFile("A.groovy");
    PsiMethod method = ((GrNewExpression)ref.getElement().getParent()).resolveMethod();
    Assert.assertNotNull(method);
    Assert.assertTrue(method.isConstructor());
    Assert.assertEquals(0, method.getParameterList().getParameters().length);
  }

  @Test
  public void wrongConstructor() {
    getFixture().addFileToProject("Classes.groovy", "class Foo { int a; int b }");
    GroovyReference ref = referenceByText("new Fo<caret>o(2, 3)");
    Assert.assertTrue(((GrNewExpression)ref.getElement().getParent()).advancedResolve().getElement() instanceof DefaultConstructor);
  }

  @Test
  public void langImmutableConstructor() {
    getFixture().addFileToProject("Classes.groovy", "@Immutable class Foo { int a; int b }");
    GroovyReference ref = referenceByText("new Fo<caret>o(2, 3)");
    Assert.assertTrue(((GrNewExpression)ref.getElement().getParent()).advancedResolve().getElement() instanceof PsiMethod);
  }

  @Test
  public void transformImmutableConstructor() {
    getFixture().addFileToProject("Classes.groovy", "@groovy.transform.Immutable class Foo { int a; int b }");
    GroovyReference ref = referenceByText("new Fo<caret>o(2, 3)");
    Assert.assertTrue(((GrNewExpression)ref.getElement().getParent()).advancedResolve().getElement() instanceof PsiMethod);
  }

  @Test
  public void tupleConstructor() {
    getFixture().addFileToProject("Classes.groovy", "@groovy.transform.TupleConstructor class Foo { int a; final int b }");
    GroovyReference ref = referenceByText("new Fo<caret>o(2, 3)");
    PsiElement target = ((GrNewExpression)ref.getElement().getParent()).advancedResolve().getElement();
    Assert.assertTrue(target instanceof PsiMethod);
    Assert.assertEquals(2, ((PsiMethod)target).getParameterList().getParametersCount());
    Assert.assertTrue(target.getNavigationElement() instanceof PsiClass);
  }

  @Test
  public void canonicalConstructor() {
    getFixture().addFileToProject("Classes.groovy", "@groovy.transform.Canonical class Foo { int a; int b }");
    GroovyReference ref = referenceByText("new Fo<caret>o(2, 3)");
    Assert.assertTrue(((GrNewExpression)ref.getElement().getParent()).advancedResolve().getElement() instanceof PsiMethod);
  }

  @Test
  public void inheritConstructors() {
    getFixture().addFileToProject("Classes.groovy", "@groovy.transform.InheritConstructors class CustomException extends Exception {}");
    GroovyReference ref = referenceByText("new Cu<caret>stomException(\"msg\")");
    Assert.assertTrue(((GrNewExpression)ref.getElement().getParent()).advancedResolve().getElement() instanceof PsiMethod);
  }

  @Test
  public void partiallyDeclaredType() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void generic1() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void notAField() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void escapedReferenceExpression() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void listOfClasses() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, PsiMethod.class);
  }

  @Test
  public void emptyVsMap() {
    PsiReference ref = configureByFile("A.groovy");
    PsiMethod resolved = ((GrNewExpression)ref.getElement().getParent()).resolveMethod();
    Assert.assertNotNull(resolved);
    Assert.assertEquals(0, resolved.getParameterList().getParametersCount());
  }

  @Test
  public void privateScriptMethod() {
    PsiReference ref = configureByFile("A.groovy");
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void aliasedConstructor() {
    PsiReference ref = configureByFile("A.groovy");
    PsiMethod resolved = ((GrNewExpression)ref.getElement().getParent()).resolveMethod();
    Assert.assertNotNull(resolved);
    Assert.assertEquals("JFrame", resolved.getName());
  }

  @Test
  public void fixedVsVarargs1() {
    PsiReference ref = configureByFile("A.groovy");
    PsiMethod resolved = ((GrNewExpression)ref.getElement().getParent()).resolveMethod();
    Assert.assertNotNull(resolved);
    final GrParameter[] parameters = ((GrMethod)resolved).getParameters();
    Assert.assertEquals(1, parameters.length);
    Assert.assertEquals("int", parameters[0].getType().getCanonicalText());
  }

  @Test
  public void fixedVsVarargs2() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
    final PsiParameter[] parameters = ((PsiMethod)resolved).getParameterList().getParameters();
    Assert.assertEquals(2, parameters.length);
    Assert.assertEquals("java.lang.Class", parameters[0].getType().getCanonicalText());
  }

  @Test
  public void reassigned1() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrMethod);
    final GrParameter[] parameters = ((GrMethod)resolved).getParameters();
    Assert.assertEquals(1, parameters.length);
    Assert.assertEquals("java.lang.String", parameters[0].getType().getCanonicalText());
  }

  @Test
  public void reassigned2() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrMethod);
    final GrParameter[] parameters = ((GrMethod)resolved).getParameters();
    Assert.assertEquals(1, parameters.length);
    Assert.assertEquals("int", parameters[0].getType().getCanonicalText());
  }

  @Test
  public void generics1() {
    PsiReference ref = configureByFile("A.groovy");
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void genericOverriding() {
    PsiReference ref = configureByFile("A.groovy");
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void useOperator() {
    PsiReference ref = configureByFile("A.groovy");
    final PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrGdkMethod);
  }

  @Test
  public void closureMethodInsideClosure() {
    PsiReference ref = configureByFile("A.groovy");
    final PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void scriptMethodInsideClosure() {
    PsiReference ref = configureByFile("A.groovy");
    final PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void explicitGetter() {
    PsiReference ref = configureByFile("A.groovy");
    final PsiElement resolved = ref.resolve();
    Assert.assertNotNull(resolved);
    Assert.assertFalse(resolved instanceof GrAccessorMethod);
  }

  @Test
  public void groovyAndJavaSamePackage() {
    getFixture().copyFileToProject(getTestName() + "/p/Hu.java", "p/Hu.java");
    PsiReference ref = configureByFile("p/Ha.groovy");
    Assert.assertTrue(ref.resolve() instanceof PsiMethod);
  }

  @Test
  public void unboxBigDecimal() {
    getFixture().addClass("package java.math; public class BigDecimal {}");
    GroovyReference ref = referenceByText("java.lang.Math.<caret>min(0, 0.0)");
    Collection<? extends GroovyResolveResult> results = ref.resolve(false);
    Assert.assertEquals(2, results.size());
  }

  @Test
  public void grvy1157() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void grvy1173() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void grvy1173_a() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void grvy1218() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void methodPointer1() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void methodPointer2() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof PsiMethod);
  }

  @Test
  public void methodCallTypeFromMultiResolve() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    Assert.assertNull(ref.resolve());
    Assert.assertTrue(((GrMethodCallExpression)ref.getParent()).getType().equalsToText("java.lang.String"));
  }

  @Test
  public void defaultOverloaded() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void defaultOverloaded2() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void defaultOverloaded3() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void multipleAssignment1() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void multipleAssignment2() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void multipleAssignment3() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void closureIntersect() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void closureCallCurry() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void superFromGString() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("SuperFromGString.groovy").getElement();
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void nominalTypeIsBetterThanNull() {
    GrReferenceExpression ref = (GrReferenceExpression)configureByFile("A.groovy").getElement();
    final PsiType type = UsefulTestCase.assertInstanceOf(ref.resolve(), GrMethod.class).getInferredReturnType();
    Assert.assertNotNull(type);
    Assert.assertTrue(type.equalsToText(CommonClassNames.JAVA_LANG_STRING));
  }

  @Test
  public void qualifiedSuperMethod() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrMethod);
    Assert.assertEquals("SuperClass", ((GrMethod)resolved).getContainingClass().getName());
  }

  @Test
  public void qualifiedThisMethod() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    Assert.assertTrue(resolved instanceof GrMethod);
    Assert.assertEquals("OuterClass", ((GrMethod)resolved).getContainingClass().getName());
  }

  @Test
  public void printMethodInAnonymousClass1() {
    PsiReference ref = configureByFile("A.groovy");
    UsefulTestCase.assertInstanceOf(ref.resolve(), GrGdkMethod.class);
  }

  @Test
  public void printMethodInAnonymousClass2() {
    PsiReference ref = configureByFile("B.groovy");
    UsefulTestCase.assertInstanceOf(ref.resolve(), GrGdkMethod.class);
  }

  @Test
  public void substituteWhenDisambiguating() {
    getFixture().configureByText("a.groovy", """
      
      class Zoo {
        def Object find(Object x) {}
        def <T> T find(Collection<T> c) {}
      
        {
          fin<caret>d(["a"])
        }
      
      }""");
    PsiReference ref = getFixture().getFile().findReferenceAt(getFixture().getEditor().getCaretModel().getOffset());
    Assert.assertEquals(1, ((PsiMethod)ref.resolve()).getTypeParameters().length);
  }

  @Test
  public void fooMethodInAnonymousClass() {
    PsiReference ref = configureByFile("A.groovy");
    final PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, PsiMethod.class);
    Assert.assertEquals("A", ((PsiMethod)resolved).getContainingClass().getName());
  }

  @Test
  public void optionalParameters1() {
    PsiReference ref = configureByFile("A.groovy");
    final PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, PsiMethod.class);
  }

  @Test
  public void optionalParameters2() {
    PsiReference ref = configureByFile("A.groovy");
    final PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, PsiMethod.class);
  }

  @Test
  public void optionalParameters3() {
    PsiReference ref = configureByFile("A.groovy");
    final PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, PsiMethod.class);
  }

  @Test
  public void optionalParameters4() {
    PsiReference ref = configureByFile("A.groovy");
    final PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, PsiMethod.class);
  }

  @Test
  public void notInitializedVariable() {
    PsiReference ref = configureByFile("A.groovy");
    final PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, PsiMethod.class);
  }

  @Test
  public void methodVsField() {
    final PsiReference ref = configureByFile("A.groovy");
    final PsiElement element = ref.resolve();
    UsefulTestCase.assertInstanceOf(element, PsiMethod.class);
  }

  @Test
  public void localVariableVsGetter() {
    final PsiReference ref = configureByFile("A.groovy");
    final PsiElement element = ref.resolve();
    UsefulTestCase.assertInstanceOf(element, GrVariable.class);
  }

  @Test
  public void invokeMethodViaThisInStaticContext() {
    final PsiReference ref = configureByFile("A.groovy");
    final PsiElement element = ref.resolve();
    Assert.assertEquals("Class", UsefulTestCase.assertInstanceOf(element, PsiMethod.class).getContainingClass().getName());
  }

  @Test
  public void invokeMethodViaClassInStaticContext() {
    final PsiReference ref = configureByFile("A.groovy");
    final PsiElement element = ref.resolve();
    UsefulTestCase.assertInstanceOf(element, PsiMethod.class);
    Assert.assertEquals("Foo", UsefulTestCase.assertInstanceOf(element, PsiMethod.class).getContainingClass().getName());
  }

  @Test
  public void useInCategory() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, PsiMethod.class);
  }

  @Test
  public void methodVsLocalVariable() {
    PsiReference ref = configureByFile("A.groovy");
    PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, GrVariable.class);
  }

  @Test
  public void commandExpressionStatement1() {
    PsiElement method = resolve("A.groovy");
    Assert.assertEquals("foo2", UsefulTestCase.assertInstanceOf(method, GrMethod.class).getName());
  }

  @Test
  public void commandExpressionStatement2() {
    PsiElement method = resolve("A.groovy");
    Assert.assertEquals("foo3", UsefulTestCase.assertInstanceOf(method, GrMethod.class).getName());
  }

  @Test
  public void upperCaseFieldAndGetter() {
    Assert.assertTrue(resolve("A.groovy") instanceof GrMethod);
  }

  @Test
  public void upperCaseFieldWithoutGetter() {
    Assert.assertTrue(resolve("A.groovy") instanceof GrAccessorMethod);
  }

  @Test
  public void spreadOperatorNotList() {
    UsefulTestCase.assertInstanceOf(resolve("A.groovy"), GrMethod.class);
  }

  @Test
  public void methodChosenCorrect() {
    final PsiElement resolved = resolve("A.groovy");
    Assert.assertEquals("map", UsefulTestCase.assertInstanceOf(resolved, GrMethod.class).getParameterList().getParameters()[0].getName());
  }

  @Test
  public void resolveCategories() {
    Assert.assertNotNull(resolve("A.groovy"));
  }

  @Test
  public void resolveValuesOnEnum() {
    Assert.assertNotNull(resolve("A.groovy"));
  }

  @Test
  public void avoidResolveLockInClosure() {
    Assert.assertNotNull(resolve("A.groovy"));
  }

  @Test
  public void asType() {
    UsefulTestCase.assertInstanceOf(resolve("A.groovy"), GrMethod.class);
  }

  @Test
  public void plusAssignment() {
    final PsiElement resolved = resolve("A.groovy");
    Assert.assertEquals("plus", UsefulTestCase.assertInstanceOf(resolved, GrMethod.class).getName());
  }

  @Test
  public void wrongGdkCallGenerics() {
    getFixture().configureByText("a.groovy", "Map<File,String> map = [:]\n" + "println map.ge<caret>t('', '')");
    PsiReference ref = getFixture().getFile().findReferenceAt(getFixture().getEditor().getCaretModel().getOffset());
    UsefulTestCase.assertInstanceOf(ref.resolve(), GrGdkMethod.class);
  }

  @Test
  public void staticImportInSamePackage() {
    getFixture().addFileToProject("pack/Foo.groovy", """
      package pack
      class Foo {
        static def foo()
      }""");
    PsiReference ref = configureByFile("A.groovy");
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void stringRefExpr1() {
    Assert.assertNotNull(resolve("a.groovy"));
  }

  @Test
  public void stringRefExpr2() {
    Assert.assertNotNull(resolve("a.groovy"));
  }

  @Test
  public void stringRefExpr3() {
    Assert.assertNotNull(resolve("a.groovy"));
  }

  @Test
  public void nestedWith() {
    Assert.assertNotNull(resolve("a.groovy"));
  }

  @Test
  public void category() {
    Assert.assertNotNull(resolve("a.groovy"));
  }

  @Test
  public void dontUseQualifierScopeInDGM() {
    Assert.assertNull(resolve("a.groovy"));
  }

  @Test
  public void inferPlusType() {
    Assert.assertNotNull(resolve("a.groovy"));
  }

  @Test
  public void mixinAndCategory() {
    GroovyReference ref = referenceByText(
      """
        @Category(B)
        class A {
          def foo() {print getName()}
        }
        
        @Mixin(A)
        class B {
          def getName('B');
        }
        
        print new B().f<caret>oo()
        """);

    PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, GrGdkMethod.class);
    UsefulTestCase.assertInstanceOf(((GrGdkMethod)resolved).getStaticMethod(), GrReflectedMethod.class);
  }

  @Test
  public void onlyMixin() {
    GroovyReference ref = referenceByText(
      """
        class A {
          def foo() {print getName()}
        }
        
        @Mixin(A)
        class B {
          def getName('B');
        }
        
        print new B().f<caret>oo()
        """);

    PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, PsiMethod.class);
  }

  @Test
  public void twoMixinsInModifierList() {
    GroovyReference ref = referenceByText(
      """
        class PersonHelper {
          def useThePerson() {
            Person person = new Person()
        
            person.getUsername()
            person.get<caret>Name()
          }
        }
        
        @Mixin(PersonMixin)
        @Mixin(OtherPersonMixin)
        class Person { }
        
        class PersonMixin {
          String getUsername() { }
        }
        
        class OtherPersonMixin {
          String getName() { }
        }
        """);

    PsiElement resolved = ref.resolve();
    UsefulTestCase.assertInstanceOf(resolved, PsiMethod.class);
  }

  @Test
  public void disjunctionType() {
    GroovyReference ref = referenceByText(
      """
        import java.sql.SQLException
        def test() {
                try {}
                catch (IOException | SQLException ex) {
                    ex.prin<caret>tStackTrace();
                }
        }""");
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void stringInjectionDontOverrideItParameter() {
    GroovyReference ref = referenceByText(
      """
        [2, 3, 4].collect {"${it.toBigDeci<caret>mal()}"}
        """);
    Assert.assertNotNull(ref.resolve());
  }

  @Test
  public void publicVsPrivateConstructor() {
    PsiMethod resolved =
      ((GrNewExpression)referenceByText("throw new Assertion<caret>Error(\"foo\")").getElement().getParent()).resolveMethod();
    Assert.assertNotNull(resolved);

    PsiParameter[] parameters = resolved.getParameterList().getParameters();
    Assert.assertEquals(1, parameters.length);
    LightGroovyTestCase.assertType("java.lang.String", parameters[0].getType());
  }

  @Test
  public void scriptMethodsInClass() {
    GroovyReference ref = referenceByText(
      """
        class X {
          def foo() {
            scriptMetho<caret>d('1')
          }
        }
        def scriptMethod(String s){}
        """);

    Assert.assertNull(ref.resolve());
  }

  @Test
  public void staticallyImportedMethodsVsDGMMethods() {
    getFixture().addClass(
      """
        package p;
        public class Matcher{}
        """);
    getFixture().addClass(
      """
        package p;
        class Other {
          public static Matcher is(Matcher m){}
          public static Matcher create(){}
        }""");

    GroovyReference ref = referenceByText(
      """
        import static p.Other.is
        import static p.Other.create
        
        i<caret>s(create())
        
        """);

    PsiMethod resolved = UsefulTestCase.assertInstanceOf(ref.resolve(), PsiMethod.class);
    Assert.assertEquals("Other", resolved.getContainingClass().getName());
  }

  @Test
  public void staticallyImportedMethodsVsCurrentClassMethod() {
    getFixture().addClass(
      """
        package p;
        class Other {
          public static Object is(Object m){}
        }""");

    GroovyReference ref = referenceByText(
      """
        import static p.Other.is
        
        class A {
          public boolean is(String o){true}
        
          public foo() {
            print i<caret>s('abc')
          }
        }
        
        """);

    PsiMethod resolved = UsefulTestCase.assertInstanceOf(ref.resolve(), PsiMethod.class);
    Assert.assertEquals("A", resolved.getContainingClass().getName());
  }

