From a10f7971568024eb29487d7449835590435fceb9 Mon Sep 17 00:00:00 2001 From: Valery Semenchuk Date: Sun, 5 Jan 2025 16:12:53 +0400 Subject: [PATCH] add test run line markers --- ....java.execution.JavaExecutionLocalize.yaml | 2 + .../BaseTestRunLineMarkerProvider.java | 129 ++++++++++++++++++ .../TestRunLineMarkerProvider.java | 19 +++ 3 files changed, 150 insertions(+) create mode 100644 java-execution-impl/src/main/java/com/intellij/java/execution/impl/testframework/BaseTestRunLineMarkerProvider.java create mode 100644 java-execution-impl/src/main/java/com/intellij/java/execution/impl/testframework/TestRunLineMarkerProvider.java diff --git a/java-execution-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.execution.JavaExecutionLocalize.yaml b/java-execution-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.execution.JavaExecutionLocalize.yaml index 217844198..a42f01838 100644 --- a/java-execution-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.execution.JavaExecutionLocalize.yaml +++ b/java-execution-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.execution.JavaExecutionLocalize.yaml @@ -26,3 +26,5 @@ application.configuration.description: text: Java Application configuration application.configuration.name: text: Java Application +run.test: + text: Run Test diff --git a/java-execution-impl/src/main/java/com/intellij/java/execution/impl/testframework/BaseTestRunLineMarkerProvider.java b/java-execution-impl/src/main/java/com/intellij/java/execution/impl/testframework/BaseTestRunLineMarkerProvider.java new file mode 100644 index 000000000..dd02245a6 --- /dev/null +++ b/java-execution-impl/src/main/java/com/intellij/java/execution/impl/testframework/BaseTestRunLineMarkerProvider.java @@ -0,0 +1,129 @@ +// 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.java.execution.impl.testframework; + +import com.intellij.java.language.codeInsight.TestFrameworks; +import com.intellij.java.language.psi.PsiClass; +import com.intellij.java.language.psi.PsiIdentifier; +import com.intellij.java.language.psi.PsiMethod; +import com.intellij.java.language.psi.util.ClassUtil; +import com.intellij.java.language.psi.util.PsiMethodUtil; +import com.intellij.java.language.testIntegration.TestFramework; +import consulo.application.dumb.DumbAware; +import consulo.application.dumb.IndexNotReadyException; +import consulo.execution.icon.ExecutionIconGroup; +import consulo.execution.lineMarker.ExecutorAction; +import consulo.execution.lineMarker.RunLineMarkerContributor; +import consulo.execution.test.TestIconMapper; +import consulo.execution.test.TestStateInfo; +import consulo.execution.test.TestStateStorage; +import consulo.java.execution.localize.JavaExecutionLocalize; +import consulo.language.psi.PsiElement; +import consulo.language.psi.util.PsiTreeUtil; +import consulo.logging.Logger; +import consulo.project.DumbService; +import consulo.ui.ex.action.AnAction; +import consulo.ui.image.Image; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Dmitry Avdeev + */ +public abstract class BaseTestRunLineMarkerProvider extends RunLineMarkerContributor implements DumbAware { + private static final String URL_TEST_PREFIX = "java:test://"; + private static final String URL_SUITE_PREFIX = "java:suite://"; + private static final Logger LOG = Logger.getInstance(TestRunLineMarkerProvider.class); + + @Override + @Nullable + public Info getInfo(@Nonnull PsiElement e) { + if (isIdentifier(e)) { + PsiElement element = e.getParent(); + if (element instanceof PsiClass psiClass) { + if (!isTestClass(psiClass)) { + return null; + } + String url = URL_SUITE_PREFIX + ClassUtil.getJVMClassName(psiClass); + TestStateStorage.Record state = TestStateStorage.getInstance(e.getProject()).getState(url); + return getInfo(state, true, PsiMethodUtil.hasMainInClass(psiClass) ? 1 : 0); + } + if (element instanceof PsiMethod psiMethod) { + PsiClass containingClass = PsiTreeUtil.getParentOfType(psiMethod, PsiClass.class); + if (!isTestMethod(containingClass, psiMethod)) { + return null; + } + String urlSuffix = ClassUtil.getJVMClassName(containingClass) + "/" + psiMethod.getName(); + + List urlList = new ArrayList<>(); + urlList.add(URL_TEST_PREFIX + urlSuffix); + + TestStateStorage.Record state = null; + for (String url : urlList) { + state = TestStateStorage.getInstance(e.getProject()).getState(url); + if (state != null) { + break; + } + } + return getInfo(state, false, 0); + } + } + return null; + } + + private static boolean isTestClass(PsiClass clazz) { + if (clazz == null) { + return false; + } + try { + return DumbService.getInstance(clazz.getProject()).computeWithAlternativeResolveEnabled(() -> { + TestFramework framework = TestFrameworks.detectFramework(clazz); + return framework != null && framework.isTestClass(clazz); + }); + } + catch (IndexNotReadyException e) { + LOG.error(e); + return false; + } + } + + private static boolean isTestMethod(PsiClass containingClass, PsiMethod method) { + if (containingClass == null) { + return false; + } + TestFramework framework = TestFrameworks.detectFramework(containingClass); + return framework != null && framework.isTestMethod(method, false); + } + + @Nonnull + private static Info getInfo(TestStateStorage.Record state, boolean isClass, int order) { + AnAction[] actions = ExecutorAction.getActions(order); + return new Info(getTestStateIcon(state, isClass), element -> JavaExecutionLocalize.runTest().get(), actions); + } + + @Nonnull + protected static Image getTestStateIcon(@Nullable TestStateStorage.Record state, boolean isClass) { + if (state != null) { + TestStateInfo.Magnitude magnitude = TestIconMapper.getMagnitude(state.magnitude); + if (magnitude != null) { + switch (magnitude) { + case ERROR_INDEX, FAILED_INDEX -> { + return ExecutionIconGroup.gutterRunerror(); + } + case PASSED_INDEX, COMPLETE_INDEX -> { + return ExecutionIconGroup.gutterRunsuccess(); + } + default -> { + } + } + } + } + return isClass ? ExecutionIconGroup.gutterRerun() : ExecutionIconGroup.gutterRun(); + } + + protected boolean isIdentifier(PsiElement e) { + return e instanceof PsiIdentifier; + } +} diff --git a/java-execution-impl/src/main/java/com/intellij/java/execution/impl/testframework/TestRunLineMarkerProvider.java b/java-execution-impl/src/main/java/com/intellij/java/execution/impl/testframework/TestRunLineMarkerProvider.java new file mode 100644 index 000000000..5d30f8a83 --- /dev/null +++ b/java-execution-impl/src/main/java/com/intellij/java/execution/impl/testframework/TestRunLineMarkerProvider.java @@ -0,0 +1,19 @@ +package com.intellij.java.execution.impl.testframework; + +import com.intellij.java.language.JavaLanguage; +import consulo.annotation.component.ExtensionImpl; +import consulo.language.Language; +import jakarta.annotation.Nonnull; + +/** + * @author VISTALL + * @since 2025-01-05 + */ +@ExtensionImpl +public class TestRunLineMarkerProvider extends BaseTestRunLineMarkerProvider { + @Nonnull + @Override + public Language getLanguage() { + return JavaLanguage.INSTANCE; + } +}