[groovy-test] IDEA-353402 Inappropriate support test frameworks for groovy in dumb-mode

GitOrigin-RevId: 270a3cb538b2e2ed6b97f61c8302de7d12f59b21
This commit is contained in:
Mikhail Pyltsin
2024-05-13 18:02:40 +02:00
committed by intellij-monorepo-bot
parent dee166bbd3
commit 2fdade319c
10 changed files with 159 additions and 32 deletions

View File

@@ -109,14 +109,18 @@ public abstract class JavaTestFramework implements JvmTestFramework {
@Override
public boolean isTestClass(@NotNull PsiElement clazz) {
//other languages are not ready for dumb-mode
if (DumbService.isDumb(clazz.getProject()) && clazz.getLanguage() != JavaLanguage.INSTANCE) return false;
if (DumbService.isDumb(clazz.getProject()) && !supportDumbMode(clazz)) return false;
return clazz instanceof PsiClass && isFrameworkAvailable(clazz) && isTestClass((PsiClass)clazz, false);
}
private static boolean supportDumbMode(@NotNull PsiElement psiElement) {
return psiElement.getLanguage() == JavaLanguage.INSTANCE || JavaTestFrameworkInDumbMode.isSupported(psiElement);
}
@Override
public boolean isPotentialTestClass(@NotNull PsiElement clazz) {
//other languages are not ready for dumb-mode
if (DumbService.isDumb(clazz.getProject()) && clazz.getLanguage() != JavaLanguage.INSTANCE) return false;
if (DumbService.isDumb(clazz.getProject()) && !supportDumbMode(clazz)) return false;
return clazz instanceof PsiClass && isFrameworkAvailable(clazz) && isTestClass((PsiClass)clazz, true);
}

View File

@@ -0,0 +1,39 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.testIntegration;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@ApiStatus.Experimental
public interface JavaTestFrameworkInDumbMode {
ExtensionPointName<JavaTestFrameworkInDumbMode> EP = ExtensionPointName.create("com.intellij.testFramework.java.dumbMode");
/**
* Checks whether the given PsiElement can be used in dumb mode for the JavaTestFramework.
* Can be called either in dumb mode or usual mode.
* Usually, it is used to check language only.
*
* @param psiElement the PsiElement to be checked.
* @return true if the PsiElement is supported, false otherwise.
*/
boolean supportDumbMode(@NotNull PsiElement psiElement);
/**
* Checks whether the given PsiClass can be used in dumb mode for JavaTestFramework.
* Can be called either in dumb mode or usual mode.
* Usually, it is used to check language only
*
* @param psiElement the PsiElement to be checked.
* @return true if the PsiElement is supported, false otherwise.
*/
static boolean isSupported(@NotNull PsiElement psiElement) {
for (JavaTestFrameworkInDumbMode framework : EP.getExtensionList()) {
if (framework.supportDumbMode(psiElement)) {
return true;
}
}
return false;
}
}

View File

@@ -47,6 +47,7 @@
<with attribute="implementationClass" implements="com.intellij.psi.PsiAnnotationSupport"/>
</extensionPoint>
<extensionPoint qualifiedName="com.intellij.testFramework" interface="com.intellij.testIntegration.TestFramework" dynamic="true"/>
<extensionPoint qualifiedName="com.intellij.testFramework.java.dumbMode" interface="com.intellij.testIntegration.JavaTestFrameworkInDumbMode" dynamic="true"/>
<extensionPoint qualifiedName="org.jetbrains.uast.uastLanguagePlugin" interface="org.jetbrains.uast.UastLanguagePlugin" dynamic="true"/>
<extensionPoint qualifiedName="org.jetbrains.uast.analysis.uastAnalysisPlugin"
interface="org.jetbrains.uast.analysis.UastAnalysisPlugin"

View File

@@ -1,6 +1,7 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.modifiers;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.compiled.ClsClassImpl;
@@ -201,11 +202,13 @@ public final class GrAnnotationCollector {
return CachedValuesManager.getManager(project).getCachedValue(project, () -> {
Set<String> result = new HashSet<>();
GlobalSearchScope scope = GlobalSearchScope.allScope(project);
for (PsiClass collector : JavaPsiFacade.getInstance(project).findClasses(GROOVY_TRANSFORM_ANNOTATION_COLLECTOR, scope)) {
AnnotatedElementsSearch.searchPsiClasses(collector, scope).forEach(aClass -> {
ContainerUtil.addIfNotNull(result, aClass.getName());
return true;
});
if (!DumbService.isDumb(project)) {
for (PsiClass collector : JavaPsiFacade.getInstance(project).findClasses(GROOVY_TRANSFORM_ANNOTATION_COLLECTOR, scope)) {
AnnotatedElementsSearch.searchPsiClasses(collector, scope).forEach(aClass -> {
ContainerUtil.addIfNotNull(result, aClass.getName());
return true;
});
}
}
return CachedValueProvider.Result.create(result, PsiModificationTracker.MODIFICATION_COUNT);
});

View File

@@ -395,6 +395,7 @@
implementationClass="org.jetbrains.plugins.groovy.findUsages.GrFileItemPresentationProvider"/>
<testFramework implementation="org.jetbrains.plugins.groovy.testIntegration.GroovyTestFramework" order="first"/>
<testFramework.java.dumbMode implementation="org.jetbrains.plugins.groovy.testIntegration.GroovyTestFrameworkInDumbMode" order="first"/>
<testCreator language="Groovy" implementationClass="com.intellij.testIntegration.JavaTestCreator"/>
<testGenerator language="Groovy" implementationClass="org.jetbrains.plugins.groovy.testIntegration.GroovyTestGenerator"/>

View File

@@ -4,6 +4,7 @@ package org.jetbrains.plugins.groovy.ext.spock;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.execution.junit.JUnitUtil;
import com.intellij.ide.fileTemplates.FileTemplateDescriptor;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ExternalLibraryDescriptor;
import com.intellij.psi.JavaPsiFacade;
@@ -18,7 +19,7 @@ import org.jetbrains.plugins.groovy.testIntegration.GroovyTestFramework;
import static com.intellij.psi.util.InheritanceUtil.isInheritor;
public final class SpockTestFramework extends GroovyTestFramework {
public final class SpockTestFramework extends GroovyTestFramework implements DumbAware {
private static final ExternalLibraryDescriptor SPOCK_DESCRIPTOR = new ExternalLibraryDescriptor("org.spockframework", "spock-core");
@NotNull
@@ -62,7 +63,9 @@ public final class SpockTestFramework extends GroovyTestFramework {
@Override
public boolean isTestMethod(PsiElement element, boolean checkAbstract) {
if (element == null) return false;
return SpockUtils.isTestMethod(element);
return callWithAlternateResolver(element.getProject(), () -> {
return SpockUtils.isTestMethod(element);
}, false);
}
@Override
@@ -72,17 +75,22 @@ public final class SpockTestFramework extends GroovyTestFramework {
@Override
protected boolean isTestClass(PsiClass clazz, boolean canBePotential) {
return clazz.getLanguage() == GroovyLanguage.INSTANCE && isInheritor(clazz, SpockUtils.SPEC_CLASS_NAME);
if (clazz == null) return false;
return callWithAlternateResolver(clazz.getProject(), () -> {
return clazz.getLanguage() == GroovyLanguage.INSTANCE && isInheritor(clazz, SpockUtils.SPEC_CLASS_NAME);
}, false);
}
@Nullable
private PsiMethod findSpecificMethod(@NotNull PsiClass clazz, String methodName) {
if (!isTestClass(clazz, false)) return null;
return callWithAlternateResolver(clazz.getProject(), () -> {
if (!isTestClass(clazz, false)) return null;
for (PsiMethod method : clazz.findMethodsByName(methodName, false)) {
if (method.getParameterList().isEmpty()) return method;
}
return null;
for (PsiMethod method : clazz.findMethodsByName(methodName, false)) {
if (method.getParameterList().isEmpty()) return method;
}
return null;
}, null);
}
@Nullable
@@ -99,7 +107,9 @@ public final class SpockTestFramework extends GroovyTestFramework {
@Override
public boolean shouldRunSingleClassAsJUnit5(Project project, GlobalSearchScope scope) {
PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(SpockUtils.SPEC_CLASS_NAME, scope);
return aClass != null && AnnotationUtil.isAnnotated(aClass, JUnitUtil.CUSTOM_TESTABLE_ANNOTATION, 0);
return callWithAlternateResolver(project, () -> {
PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(SpockUtils.SPEC_CLASS_NAME, scope);
return aClass != null && AnnotationUtil.isAnnotated(aClass, JUnitUtil.CUSTOM_TESTABLE_ANNOTATION, 0);
}, false);
}
}

View File

@@ -29,6 +29,11 @@ import static org.jetbrains.plugins.groovy.bundled.BundledGroovy.getBundledGroov
public class GroovyTestFramework extends JUnitTestFramework {
private static final Logger LOG = Logger.getInstance(GroovyTestFramework.class);
@Override
public boolean isDumbAware() {
return this.getClass().isAssignableFrom(GroovyTestFramework.class);
}
@Override
protected String getMarkerClassFQName() {
return GroovyCommonClassNames.GROOVY_UTIL_TEST_CASE;
@@ -36,29 +41,37 @@ public class GroovyTestFramework extends JUnitTestFramework {
@Override
protected boolean isTestClass(PsiClass clazz, boolean canBePotential) {
return clazz.getLanguage() == GroovyLanguage.INSTANCE &&
//JUnitUtil.isTestClass(clazz) &&
InheritanceUtil.isInheritor(clazz, GroovyCommonClassNames.GROOVY_UTIL_TEST_CASE);
if(clazz == null) return false;
return callWithAlternateResolver(clazz.getProject(), ()->{
return clazz.getLanguage() == GroovyLanguage.INSTANCE &&
//JUnitUtil.isTestClass(clazz) &&
InheritanceUtil.isInheritor(clazz, GroovyCommonClassNames.GROOVY_UTIL_TEST_CASE);
}, false);
}
@Override
protected PsiMethod findSetUpMethod(@NotNull PsiClass clazz) {
if (!isTestClass(clazz, false)) return null;
return callWithAlternateResolver(clazz.getProject(), () -> {
if (!isTestClass(clazz, false)) return null;
for (PsiMethod method : clazz.getMethods()) {
if (method.getName().equals("setUp")) return method;
}
return null;
for (PsiMethod method : clazz.getMethods()) {
if (method.getName().equals("setUp")) return method;
}
return null;
}, null);
}
@Override
protected PsiMethod findTearDownMethod(@NotNull PsiClass clazz) {
if (!isTestClass(clazz, false)) return null;
return callWithAlternateResolver(clazz.getProject(), () -> {
for (PsiMethod method : clazz.getMethods()) {
if (method.getName().equals("tearDown")) return method;
}
return null;
if (!isTestClass(clazz, false)) return null;
for (PsiMethod method : clazz.getMethods()) {
if (method.getName().equals("tearDown")) return method;
}
return null;
}, null);
}
@Override

View File

@@ -0,0 +1,14 @@
// 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.testIntegration;
import com.intellij.psi.PsiElement;
import com.intellij.testIntegration.JavaTestFrameworkInDumbMode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.GroovyLanguage;
public class GroovyTestFrameworkInDumbMode implements JavaTestFrameworkInDumbMode {
@Override
public boolean supportDumbMode(@NotNull PsiElement psiElement) {
return psiElement.getLanguage() == GroovyLanguage.INSTANCE;
}
}

View File

@@ -55,10 +55,16 @@ public class SpockLineMarkersTest extends LightGroovyTestCase {
<caret>def 'method with spaces ok'() { expect: 1 == 1 }
}
""");
LinkedHashMap<String, Boolean> map = new LinkedHashMap<>(4);
map.put("cleanup", false);
map.put("methodWithoutLabels", false);
map.put("methodWithAnotherLabel", false);
map.put("method with spaces ok", true);
GrTypeDefinition spec = DefaultGroovyMethods.first(file.getTypeDefinitions());
DumbModeTestUtils.runInDumbModeSynchronously(getProject(), () -> {
for (GrMethod method : spec.getCodeMethods()) {
Assertions.assertFalse(TestFrameworks.getInstance().isTestMethod(method));
String name = method.getName();
Assertions.assertEquals(map.get(name), TestFrameworks.getInstance().isTestMethod(method));
}
});
}

View File

@@ -0,0 +1,36 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.execution.junit.codeInspection.naming;
import com.intellij.codeInsight.TestFrameworks;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassOwner;
import com.intellij.psi.PsiFile;
import com.intellij.testFramework.DumbModeTestUtils;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import com.intellij.testIntegration.TestFramework;
public class GroovyJUnitDetectionTest extends LightJavaCodeInsightFixtureTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
myFixture.addClass("package org.junit; public @interface Test {}");
}
public void testJUnitTestCaseInDumbMode() {
PsiFile file = myFixture.configureByText("FooTest.groovy", """
import org.junit.Test
class FooTest {
@Test
void testFoo2() {
}
}""");
DumbModeTestUtils.runInDumbModeSynchronously(getProject(), () -> {
PsiClass aClass = ((PsiClassOwner)file).getClasses()[0];
TestFramework framework = TestFrameworks.detectFramework(aClass);
assertNotNull(framework);
assertTrue(framework.isTestClass(aClass));
});
}
}