From 83f9e045d4928397e704e90157bb7a9a0bc164b6 Mon Sep 17 00:00:00 2001 From: Sergei Vorobyov Date: Thu, 4 Aug 2022 20:28:12 +0200 Subject: [PATCH] IDEA-313445 refactored Gradle/ExternalSystem event processing major changes: - added message and IO type for message event - added attaching isolated event trees into build root - extracted all event convertors code into Gradle and ES event convertors - added logging for unhandled and unsupported events GitOrigin-RevId: 1151beb3cb7295d5786d51ec37943b249fb7cd05 --- .../testframework/JavaTestLocator.java | 14 +- .../event/ExternalSystemMessageEvent.java | 10 +- .../event/ExternalSystemMessageEventImpl.java | 42 ++- .../ExternalSystemProgressEventConverter.java | 86 +++++++ .../util/ExternalSystemUtil.java | 68 +---- .../test/runner/GradleSMTestProxy.java | 7 +- .../runner/GradleTestEventsProcessor.java | 64 ++--- .../runner/GradleTestLocationCustomizer.kt | 36 ++- .../GradleTestsExecutionConsoleManager.java | 41 +-- ...eTestsExecutionConsoleOutputProcessor.java | 50 ++-- .../events/AbstractTestEventProcessor.java | 93 +++++-- .../events/AfterSuiteEventProcessor.java | 12 +- .../events/BeforeSuiteEventProcessor.java | 104 ++++---- .../events/BeforeTestEventProcessor.java | 81 ++---- .../runner/events/OnOutputEventProcessor.java | 48 ++-- .../SpockGradleTestLocationCustomizer.kt | 87 +++++-- .../GradleProgressEventConverter.java | 241 ++++++++++++------ .../execution/GradleProgressListener.java | 195 ++++---------- 18 files changed, 678 insertions(+), 601 deletions(-) create mode 100644 platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemProgressEventConverter.java diff --git a/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java b/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java index 311af43c88f7..a3fa82e1c5f8 100644 --- a/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java +++ b/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java @@ -164,13 +164,15 @@ public class JavaTestLocator implements SMTestLocator { : new PsiLocation<>(aClass.getProject(), aClass); } - @NotNull - public static String createLocationUrl(@NotNull String protocol, @NotNull String fqClassName) { - return protocol + SCHEME_SEPARATOR + fqClassName; + public static @NotNull String createLocationUrl(@NotNull String protocol, @NotNull String fqClassName) { + return createLocationUrl(protocol, fqClassName, null); } - @NotNull - public static String createLocationUrl(@NotNull String protocol, @NotNull String fqClassName, @NotNull String methodName) { - return createLocationUrl(protocol, fqClassName) + "/" + StringUtil.trimEnd(methodName, "()"); + public static @NotNull String createLocationUrl(@NotNull String protocol, @NotNull String fqClassName, @Nullable String methodName) { + var baseUrl = protocol + SCHEME_SEPARATOR; + if (methodName == null) { + return baseUrl + fqClassName; + } + return baseUrl + fqClassName + "/" + StringUtil.trimEnd(methodName, "()"); } } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/event/ExternalSystemMessageEvent.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/event/ExternalSystemMessageEvent.java index e40cb7fde971..566bca8a3e87 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/event/ExternalSystemMessageEvent.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/event/ExternalSystemMessageEvent.java @@ -1,19 +1,23 @@ // 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 com.intellij.openapi.externalSystem.model.task.event; +import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nullable; /** * An event with textual description. * @param */ -public interface ExternalSystemMessageEvent extends ExternalSystemProgressEvent{ +public interface ExternalSystemMessageEvent extends ExternalSystemProgressEvent { + + default boolean isStdOut() { return true; } + + default @Nullable @Nls String getMessage() { return null; } /** * Textual description of event. * * @return arbitrary additional information about status update */ - @Nullable - default String getDescription() { return null; } + default @Nullable String getDescription() { return null; } } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/event/ExternalSystemMessageEventImpl.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/event/ExternalSystemMessageEventImpl.java index 8b43bb02ca59..16903ab5861a 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/event/ExternalSystemMessageEventImpl.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/event/ExternalSystemMessageEventImpl.java @@ -1,24 +1,52 @@ // 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 com.intellij.openapi.externalSystem.model.task.event; +import com.intellij.openapi.util.NlsSafe; +import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class ExternalSystemMessageEventImpl extends BaseExternalSystemProgressEvent +public class ExternalSystemMessageEventImpl + extends BaseExternalSystemProgressEvent implements ExternalSystemMessageEvent { - private final String myDescription; + + private final boolean myStdOut; + private final @Nullable @Nls String myMessage; + private final @Nullable String myDescription; + + public ExternalSystemMessageEventImpl( + @NotNull String eventId, + @Nullable String parentEventId, + @NotNull T descriptor, + boolean isStdOut, + @Nullable @Nls String message, + @Nullable String description + ) { + super(eventId, parentEventId, descriptor); + myStdOut = isStdOut; + myMessage = message; + myDescription = description; + } public ExternalSystemMessageEventImpl(@NotNull String eventId, @Nullable String parentEventId, @NotNull T descriptor, - @Nullable String message) { - super(eventId, parentEventId, descriptor); - myDescription = message; + @Nullable @NlsSafe String description) { + this(eventId, parentEventId, descriptor, true, description, description); } - @Nullable @Override - public String getDescription() { + public boolean isStdOut() { + return myStdOut; + } + + @Override + public @Nullable String getMessage() { + return myMessage; + } + + @Override + public @Nullable String getDescription() { return myDescription; } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemProgressEventConverter.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemProgressEventConverter.java new file mode 100644 index 000000000000..cec5ce6b7a16 --- /dev/null +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemProgressEventConverter.java @@ -0,0 +1,86 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.openapi.externalSystem.util; + +import com.intellij.build.events.BuildEvent; +import com.intellij.build.events.EventResult; +import com.intellij.build.events.impl.*; +import com.intellij.build.events.impl.FailureImpl; +import com.intellij.build.events.impl.FailureResultImpl; +import com.intellij.build.events.impl.SkippedResultImpl; +import com.intellij.build.events.impl.SuccessResultImpl; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.model.task.event.*; +import com.intellij.util.ObjectUtils; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ExternalSystemProgressEventConverter { + + private static final Logger LOG = Logger.getInstance("com.intellij.openapi.externalSystem.event-processing"); + + public static @Nullable BuildEvent convertBuildEvent(@NotNull ExternalSystemTaskExecutionEvent event) { + var hint = event.getProgressEvent().getDescriptor().getHint(); + var buildEvent = convertAbstractBuildEvent(event); + ObjectUtils.consumeIfNotNull(buildEvent, it -> it.setHint(hint)); + return buildEvent; + } + + private static @Nullable AbstractBuildEvent convertAbstractBuildEvent(@NotNull ExternalSystemTaskExecutionEvent event) { + var progressEvent = event.getProgressEvent(); + var eventId = progressEvent.getEventId(); + var descriptor = progressEvent.getDescriptor(); + var eventTime = descriptor.getEventTime(); + var displayName = descriptor.getDisplayName(); + var parentEventId = ObjectUtils.chooseNotNull(progressEvent.getParentEventId(), event.getId()); + + if (progressEvent instanceof ExternalSystemStartEvent) { + return new StartEventImpl(eventId, parentEventId, eventTime, displayName); + } + else if (progressEvent instanceof ExternalSystemFinishEvent finishEvent) { + var operationResult = finishEvent.getOperationResult(); + var eventResult = convertEventResult(operationResult); + if (eventResult != null) { + return new FinishEventImpl(eventId, parentEventId, eventTime, displayName, eventResult); + } + } + else if (progressEvent instanceof ExternalSystemStatusEvent statusEvent) { + var total = statusEvent.getTotal(); + var progress = statusEvent.getProgress(); + var unit = statusEvent.getUnit(); + return new ProgressBuildEventImpl(eventId, parentEventId, eventTime, displayName, total, progress, unit); + } + else if (progressEvent instanceof ExternalSystemMessageEvent messageEvent) { + var message = ObjectUtils.chooseNotNull(messageEvent.getMessage(), displayName); + var isStdOut = messageEvent.isStdOut(); + return new OutputBuildEventImpl(eventId, parentEventId, message, isStdOut); + } + LOG.warn("Undefined progress event " + event.getClass().getSimpleName() + " " + event); + return null; + } + + private static @Nullable EventResult convertEventResult(@NotNull OperationResult result) { + if (result instanceof FailureResult) { + var failures = convertFailureResult((FailureResult)result); + return new FailureResultImpl(failures); + } + else if (result instanceof SkippedResult) { + return new SkippedResultImpl(); + } + else if (result instanceof SuccessResult successResult) { + return new SuccessResultImpl(successResult.isUpToDate()); + } + return null; + } + + private static @NotNull List convertFailureResult(@NotNull FailureResult failure) { + return ContainerUtil.map(failure.getFailures(), it -> convertFailure(it)); + } + + private static @NotNull com.intellij.build.events.Failure convertFailure(@NotNull Failure failure) { + var causes = ContainerUtil.map(failure.getCauses(), it -> convertFailure(it)); + return new FailureImpl(failure.getMessage(), failure.getDescription(), causes); + } +} diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java index 1d41d0df11ee..8e668ae2a1c8 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java @@ -3,11 +3,10 @@ package com.intellij.openapi.externalSystem.util; import com.intellij.build.*; import com.intellij.build.events.BuildEvent; -import com.intellij.build.events.EventResult; +import com.intellij.build.events.FailureResult; import com.intellij.build.events.FinishBuildEvent; import com.intellij.build.events.impl.FailureImpl; import com.intellij.build.events.impl.FailureResultImpl; -import com.intellij.build.events.impl.SkippedResultImpl; import com.intellij.build.events.impl.SuccessResultImpl; import com.intellij.build.events.impl.*; import com.intellij.build.issue.BuildIssue; @@ -95,7 +94,6 @@ import com.intellij.pom.Navigatable; import com.intellij.pom.NonNavigatable; import com.intellij.util.Consumer; import com.intellij.util.ObjectUtils; -import com.intellij.util.SmartList; import com.intellij.util.ThreeState; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.HashingStrategy; @@ -457,8 +455,7 @@ public final class ExternalSystemUtil { String title = ExternalSystemBundle.message("notification.project.refresh.fail.title", externalSystemId.getReadableName(), projectName); DataContext dataContext = BuildConsoleUtils.getDataContext(id, syncViewManager); - com.intellij.build.events.FailureResult failureResult = - createFailureResult(title, e, externalSystemId, project, dataContext); + FailureResult failureResult = createFailureResult(title, e, externalSystemId, project, dataContext); finishSyncEventSupplier.set(() -> new FinishBuildEventImpl(id, null, System.currentTimeMillis(), BuildBundle.message("build.status.failed"), failureResult)); processHandler.notifyProcessTerminated(1); @@ -545,8 +542,7 @@ public final class ExternalSystemUtil { ExternalSystemTaskId id = resolveProjectTask.getId(); String title = ExternalSystemBundle.message("notification.project.refresh.fail.title", externalSystemId.getReadableName(), projectName); - com.intellij.build.events.FailureResult failureResult = - createFailureResult(title, t, externalSystemId, project, DataContext.EMPTY_CONTEXT); + FailureResult failureResult = createFailureResult(title, t, externalSystemId, project, DataContext.EMPTY_CONTEXT); finishSyncEventSupplier.set(() -> new FinishBuildEventImpl(id, null, System.currentTimeMillis(), BuildBundle.message("build.status.failed"), failureResult)); @@ -784,58 +780,20 @@ public final class ExternalSystemUtil { return navigatable == null || navigatable == NonNavigatable.INSTANCE; } - public static BuildEvent convert(ExternalSystemTaskExecutionEvent taskExecutionEvent) { - ExternalSystemProgressEvent progressEvent = taskExecutionEvent.getProgressEvent(); - String displayName = progressEvent.getDescriptor().getDisplayName(); - long eventTime = progressEvent.getDescriptor().getEventTime(); - Object parentEventId = ObjectUtils.chooseNotNull(progressEvent.getParentEventId(), taskExecutionEvent.getId()); - - AbstractBuildEvent buildEvent; - if (progressEvent instanceof ExternalSystemStartEvent) { - buildEvent = new StartEventImpl(progressEvent.getEventId(), parentEventId, eventTime, displayName); + public static @NotNull BuildEvent convert(@NotNull ExternalSystemTaskExecutionEvent event) { + var buildEvent = ExternalSystemProgressEventConverter.convertBuildEvent(event); + if (buildEvent == null) { + // Migrated old fallback from previous implementation + return new OutputBuildEventImpl( + event.getProgressEvent().getEventId(), + ObjectUtils.chooseNotNull(event.getProgressEvent().getParentEventId(), event.getId()), + event.getProgressEvent().getDescriptor().getDisplayName(), + true + ); } - else if (progressEvent instanceof ExternalSystemFinishEvent) { - final EventResult eventResult; - final OperationResult operationResult = ((ExternalSystemFinishEvent)progressEvent).getOperationResult(); - if (operationResult instanceof FailureResult) { - List failures = new SmartList<>(); - for (Failure failure : ((FailureResult)operationResult).getFailures()) { - failures.add(convert(failure)); - } - eventResult = new FailureResultImpl(failures); - } - else if (operationResult instanceof SkippedResult) { - eventResult = new SkippedResultImpl(); - } - else if (operationResult instanceof SuccessResult) { - eventResult = new SuccessResultImpl(((SuccessResult)operationResult).isUpToDate()); - } - else { - eventResult = new SuccessResultImpl(); - } - buildEvent = new FinishEventImpl(progressEvent.getEventId(), parentEventId, eventTime, displayName, eventResult); - } - else if (progressEvent instanceof ExternalSystemStatusEvent statusEvent) { - buildEvent = new ProgressBuildEventImpl(progressEvent.getEventId(), progressEvent.getParentEventId(), eventTime, displayName, - statusEvent.getTotal(), statusEvent.getProgress(), statusEvent.getUnit()); - } - else { - buildEvent = new OutputBuildEventImpl(progressEvent.getEventId(), parentEventId, displayName, true); - } - - String hint = progressEvent.getDescriptor().getHint(); - buildEvent.setHint(hint); return buildEvent; } - private static com.intellij.build.events.Failure convert(Failure failure) { - List causes = new SmartList<>(); - for (Failure cause : failure.getCauses()) { - causes.add(convert(cause)); - } - return new FailureImpl(failure.getMessage(), failure.getDescription(), causes); - } - public static void runTask(@NotNull ExternalSystemTaskExecutionSettings taskSettings, @NotNull String executorId, @NotNull Project project, diff --git a/plugins/gradle/java/src/execution/test/runner/GradleSMTestProxy.java b/plugins/gradle/java/src/execution/test/runner/GradleSMTestProxy.java index e8dfc6cc4bbf..9b48f6e1f563 100644 --- a/plugins/gradle/java/src/execution/test/runner/GradleSMTestProxy.java +++ b/plugins/gradle/java/src/execution/test/runner/GradleSMTestProxy.java @@ -3,6 +3,7 @@ package org.jetbrains.plugins.gradle.execution.test.runner; import com.intellij.execution.testframework.sm.runner.SMTestProxy; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.execution.test.runner.events.TestEventResult; @@ -11,11 +12,11 @@ import org.jetbrains.plugins.gradle.execution.test.runner.events.TestEventResult */ public class GradleSMTestProxy extends SMTestProxy { - @Nullable private final String myClassName; + @NotNull private final String myClassName; @Nullable private String myParentId; @Nullable private TestEventResult myLastResult; - public GradleSMTestProxy(String testName, boolean isSuite, @Nullable String locationUrl, @Nullable String className) { + public GradleSMTestProxy(String testName, boolean isSuite, @Nullable String locationUrl, @NotNull String className) { super(testName, isSuite, locationUrl); myClassName = className; } @@ -29,7 +30,7 @@ public class GradleSMTestProxy extends SMTestProxy { myParentId = parentId; } - @Nullable + @NotNull public String getClassName() { return myClassName; } diff --git a/plugins/gradle/java/src/execution/test/runner/GradleTestEventsProcessor.java b/plugins/gradle/java/src/execution/test/runner/GradleTestEventsProcessor.java index 664cddcfbfe0..1ed9ac2bb4ca 100644 --- a/plugins/gradle/java/src/execution/test/runner/GradleTestEventsProcessor.java +++ b/plugins/gradle/java/src/execution/test/runner/GradleTestEventsProcessor.java @@ -1,68 +1,48 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.gradle.execution.test.runner; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.model.task.event.*; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.execution.test.runner.events.*; public final class GradleTestEventsProcessor { - public static void onStatusChange(GradleTestsExecutionConsole console, ExternalSystemProgressEvent event) { - TestOperationDescriptor descriptor = event.getDescriptor(); + private static final Logger LOG = Logger.getInstance("com.intellij.openapi.externalSystem.event-processing"); - final TestEventType eventType = getEventType(descriptor, event); - if (eventType == null) { - return; - } - - TestEventProcessor testEventProcessor = null; - switch (eventType) { - case BEFORE_SUITE: - testEventProcessor = new BeforeSuiteEventProcessor(console); - break; - case BEFORE_TEST: - testEventProcessor = new BeforeTestEventProcessor(console); - break; - case ON_OUTPUT: - testEventProcessor = new OnOutputEventProcessor(console); - break; - case AFTER_TEST: - testEventProcessor = new AfterTestEventProcessor(console); - break; - case AFTER_SUITE: - testEventProcessor = new AfterSuiteEventProcessor(console); - break; - case CONFIGURATION_ERROR: - case REPORT_LOCATION: - case UNKNOWN_EVENT: - break; - } - if (testEventProcessor != null) { - testEventProcessor.process(event); + public static void onStatusChange( + @NotNull GradleTestsExecutionConsole console, + @NotNull ExternalSystemProgressEvent event + ) { + var eventProcessor = createEventProcessor(console, event); + if (eventProcessor != null) { + eventProcessor.process(event); } } - private static TestEventType getEventType(@NotNull TestOperationDescriptor descriptor, - @NotNull ExternalSystemProgressEvent event) { + private static @Nullable TestEventProcessor createEventProcessor( + @NotNull GradleTestsExecutionConsole console, + @NotNull ExternalSystemProgressEvent event + ) { + var descriptor = event.getDescriptor(); if (event instanceof ExternalSystemStartEvent) { if (StringUtil.isEmpty(descriptor.getMethodName())) { - return TestEventType.BEFORE_SUITE; - } else { - return TestEventType.BEFORE_TEST; + return new BeforeSuiteEventProcessor(console); } + return new BeforeTestEventProcessor(console); } if (event instanceof ExternalSystemFinishEvent) { if (StringUtil.isEmpty(descriptor.getMethodName())) { - return TestEventType.AFTER_SUITE; - } else { - return TestEventType.AFTER_TEST; + return new AfterSuiteEventProcessor(console); } + return new AfterTestEventProcessor(console); } - - if (event instanceof ExternalSystemStatusEvent) { - return TestEventType.ON_OUTPUT; + if (event instanceof ExternalSystemMessageEvent) { + return new OnOutputEventProcessor(console); } + LOG.warn("Undefined progress event " + event.getClass().getSimpleName() + " " + event); return null; } } diff --git a/plugins/gradle/java/src/execution/test/runner/GradleTestLocationCustomizer.kt b/plugins/gradle/java/src/execution/test/runner/GradleTestLocationCustomizer.kt index 4d7f0e00841b..8bb8644ad25e 100644 --- a/plugins/gradle/java/src/execution/test/runner/GradleTestLocationCustomizer.kt +++ b/plugins/gradle/java/src/execution/test/runner/GradleTestLocationCustomizer.kt @@ -15,20 +15,42 @@ import org.jetbrains.annotations.ApiStatus */ @ApiStatus.Internal internal interface GradleTestLocationCustomizer { - companion object { - @JvmField - val EP_NAME: ExtensionPointName = ExtensionPointName.create("org.jetbrains.plugins.gradle.testLocationCustomizer") - } - data class GradleTestLocationInfo(val fqClassName: String, val methodName : String?) + /** + * Checks that current test is framework-specific test. + * + * @param parent direct parent of a customizable test node + * @param isSuite is true if location url should be created for test suite otherwise for the test method + * @param fqClassName fully-qualified name of the class containing a test method of a customizable test node + * @param methodName name of a test method of a customizable test node + */ + fun isApplicable( + project: Project, + parent: SMTestProxy, + isSuite: Boolean, + suiteName: String, + fqClassName: String, + methodName: String? + ): Boolean /** * Produces test framework-specific test location by the data of a test node. * * @param parent direct parent of a customizable test node + * @param isSuite is true if location url should be created for test suite otherwise for the test method * @param fqClassName fully-qualified name of the class containing a test method of a customizable test node * @param methodName name of a test method of a customizable test node - * @param displayName name of a customizable test node that will be displayed in the UI */ - fun getLocationInfo(project: Project, parent: SMTestProxy, fqClassName: String, methodName: String?, displayName: String?): GradleTestLocationInfo? + fun createLocationUrl( + parent: SMTestProxy, + isSuite: Boolean, + suiteName: String, + fqClassName: String, + methodName: String? + ): String + + companion object { + @JvmField + val EP_NAME = ExtensionPointName.create("org.jetbrains.plugins.gradle.testLocationCustomizer") + } } \ No newline at end of file diff --git a/plugins/gradle/java/src/execution/test/runner/GradleTestsExecutionConsoleManager.java b/plugins/gradle/java/src/execution/test/runner/GradleTestsExecutionConsoleManager.java index 30020bd0d294..7b1bf53eb92c 100644 --- a/plugins/gradle/java/src/execution/test/runner/GradleTestsExecutionConsoleManager.java +++ b/plugins/gradle/java/src/execution/test/runner/GradleTestsExecutionConsoleManager.java @@ -59,17 +59,13 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.ToolWindow; import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.ObjectUtils; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.action.GradleRerunFailedTestsAction; import org.jetbrains.plugins.gradle.execution.filters.ReRunTaskFilter; -import org.jetbrains.plugins.gradle.service.project.GradleTasksIndices; +import org.jetbrains.plugins.gradle.service.execution.GradleTestExecutionUtil; import org.jetbrains.plugins.gradle.util.GradleBundle; import org.jetbrains.plugins.gradle.util.GradleConstants; -import org.jetbrains.plugins.gradle.util.GradleTaskData; - -import java.io.File; import static org.jetbrains.plugins.gradle.util.GradleConstants.RUN_TASK_AS_TEST; @@ -264,7 +260,12 @@ public class GradleTestsExecutionConsoleManager public boolean isApplicableFor(@NotNull ExternalSystemTask task) { if (task instanceof ExternalSystemExecuteTaskTask taskTask) { if (StringUtil.equals(taskTask.getExternalSystemId().getId(), GradleConstants.SYSTEM_ID.getId())) { - if (hasTestOption(taskTask) || hasTestTasks(taskTask)) { + var project = taskTask.getIdeProject(); + var externalProjectPath = taskTask.getExternalProjectPath(); + var tasksAndArguments = taskTask.getTasksToExecute(); + var arguments = StringUtil.notNullize(taskTask.getArguments()); + var commandLine = GradleTestExecutionUtil.parseCommandLine(tasksAndArguments, arguments); + if (GradleTestExecutionUtil.hasTestTasks(commandLine, project, externalProjectPath)) { taskTask.putUserData(RUN_TASK_AS_TEST, true); return true; } @@ -273,34 +274,6 @@ public class GradleTestsExecutionConsoleManager return false; } - private static boolean hasTestOption(@NotNull ExternalSystemExecuteTaskTask task) { - var options = task.getArguments(); - var tasksAndArguments = task.getTasksToExecute(); - return options != null && StringUtil.contains(options, GradleConstants.TESTS_ARG_NAME) - || tasksAndArguments.contains(GradleConstants.TESTS_ARG_NAME); - } - - private static boolean hasTestTasks(@NotNull ExternalSystemExecuteTaskTask task) { - var modulePath = getModulePath(task); - var tasksAndArguments = task.getTasksToExecute(); - var tasksIndices = GradleTasksIndices.getInstance(task.getIdeProject()); - var tasks = tasksIndices.findTasks(modulePath, tasksAndArguments); - return ContainerUtil.or(tasks, it -> isTestTask(it)); - } - - private static boolean isTestTask(@Nullable GradleTaskData task) { - return task != null && (task.isTest() || "check".equals(task.getName()) && "verification".equals(task.getGroup())); - } - - private static @NotNull String getModulePath(@NotNull ExternalSystemExecuteTaskTask task) { - var externalProjectPath = task.getExternalProjectPath(); - var file = new File(externalProjectPath); - if (file.isFile()) { - return StringUtil.trimEnd(externalProjectPath, "/" + file.getName()); - } - return externalProjectPath; - } - @Override public AnAction[] getRestartActions(@NotNull final GradleTestsExecutionConsole consoleView) { JavaRerunFailedTestsAction rerunFailedTestsAction = diff --git a/plugins/gradle/java/src/execution/test/runner/GradleTestsExecutionConsoleOutputProcessor.java b/plugins/gradle/java/src/execution/test/runner/GradleTestsExecutionConsoleOutputProcessor.java index 1f616b24c2f9..f9ccc60b050f 100644 --- a/plugins/gradle/java/src/execution/test/runner/GradleTestsExecutionConsoleOutputProcessor.java +++ b/plugins/gradle/java/src/execution/test/runner/GradleTestsExecutionConsoleOutputProcessor.java @@ -14,11 +14,8 @@ import org.jetbrains.plugins.gradle.execution.test.runner.events.*; */ public final class GradleTestsExecutionConsoleOutputProcessor { private static final Logger LOG = Logger.getInstance(GradleTestsExecutionConsoleOutputProcessor.class); - @SuppressWarnings("HardCodedStringLiteral") private static final String LOG_EOL = ""; - @SuppressWarnings("HardCodedStringLiteral") private static final String LOG_START = ""; - @SuppressWarnings("HardCodedStringLiteral") private static final String LOG_END = ""; public static void onOutput(@NotNull GradleTestsExecutionConsole executionConsole, @@ -31,32 +28,7 @@ public final class GradleTestsExecutionConsoleOutputProcessor { final TestEventXmlView xml = new TestEventXPPXmlView(eventMessage); final TestEventType eventType = TestEventType.fromValue(xml.getTestEventType()); - TestEventProcessor testEventProcessor = null; - switch (eventType) { - case CONFIGURATION_ERROR: - testEventProcessor = new ConfigurationErrorEventProcessor(executionConsole); - break; - case REPORT_LOCATION: - testEventProcessor = new ReportLocationEventProcessor(executionConsole); - break; - case BEFORE_TEST: - testEventProcessor = new BeforeTestEventProcessor(executionConsole); - break; - case ON_OUTPUT: - testEventProcessor = new OnOutputEventProcessor(executionConsole); - break; - case AFTER_TEST: - testEventProcessor = new AfterTestEventProcessor(executionConsole); - break; - case BEFORE_SUITE: - testEventProcessor = new BeforeSuiteEventProcessor(executionConsole); - break; - case AFTER_SUITE: - testEventProcessor = new AfterSuiteEventProcessor(executionConsole); - break; - case UNKNOWN_EVENT: - break; - } + var testEventProcessor = createTestEventProcessor(eventType, executionConsole); if (testEventProcessor != null) { testEventProcessor.process(xml); } @@ -66,6 +38,22 @@ public final class GradleTestsExecutionConsoleOutputProcessor { } } + private static @Nullable TestEventProcessor createTestEventProcessor( + @NotNull TestEventType eventType, + @NotNull GradleTestsExecutionConsole executionConsole + ) { + return switch (eventType) { + case CONFIGURATION_ERROR -> new ConfigurationErrorEventProcessor(executionConsole); + case REPORT_LOCATION -> new ReportLocationEventProcessor(executionConsole); + case BEFORE_TEST -> new BeforeTestEventProcessor(executionConsole); + case ON_OUTPUT -> new OnOutputEventProcessor(executionConsole); + case AFTER_TEST -> new AfterTestEventProcessor(executionConsole); + case BEFORE_SUITE -> new BeforeSuiteEventProcessor(executionConsole); + case AFTER_SUITE -> new AfterSuiteEventProcessor(executionConsole); + default -> null; + }; + } + @Nullable private static String getEventMessage(@NotNull GradleTestsExecutionConsole executionConsole, @NotNull String text, @@ -78,7 +66,7 @@ public final class GradleTestsExecutionConsoleOutputProcessor { return null; } else { - if (consoleBuffer.length() == 0) { + if (consoleBuffer.isEmpty()) { if (StringUtil.startsWith(trimmedText, LOG_START) && StringUtil.endsWith(trimmedText, LOG_END)) { eventMessage = text; } @@ -102,7 +90,7 @@ public final class GradleTestsExecutionConsoleOutputProcessor { } eventMessage = bufferText; } - assert consoleBuffer.length() == 0; + assert consoleBuffer.isEmpty(); return eventMessage; } } diff --git a/plugins/gradle/java/src/execution/test/runner/events/AbstractTestEventProcessor.java b/plugins/gradle/java/src/execution/test/runner/events/AbstractTestEventProcessor.java index b1c0ef85b16e..96827bf524bc 100644 --- a/plugins/gradle/java/src/execution/test/runner/events/AbstractTestEventProcessor.java +++ b/plugins/gradle/java/src/execution/test/runner/events/AbstractTestEventProcessor.java @@ -5,17 +5,18 @@ import com.intellij.execution.testframework.JavaTestLocator; import com.intellij.execution.testframework.sm.runner.SMTestProxy; import com.intellij.execution.testframework.sm.runner.ui.SMTestRunnerResultsForm; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.execution.test.runner.GradleConsoleProperties; +import org.jetbrains.plugins.gradle.execution.test.runner.GradleSMTestProxy; import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestLocationCustomizer; import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsole; import java.nio.charset.StandardCharsets; import java.util.Base64; -import static org.jetbrains.plugins.gradle.execution.test.runner.GradleTestLocationCustomizer.GradleTestLocationInfo; - /** * @author Vladislav.Soroka */ @@ -43,21 +44,24 @@ public abstract class AbstractTestEventProcessor implements TestEventProcessor { return (GradleConsoleProperties)getExecutionConsole().getProperties(); } - @NotNull - protected String findLocationUrl(@Nullable String name, @NotNull String fqClassName) { - return findLocationUrl(JavaTestLocator.TEST_PROTOCOL, name, fqClassName); + protected boolean isSuite() { + return false; } - @NotNull - protected static String findLocationUrl(@NotNull String protocol, @Nullable String name, @NotNull String fqClassName) { - return name == null - ? JavaTestLocator.createLocationUrl(protocol, fqClassName) - : JavaTestLocator.createLocationUrl(protocol, fqClassName, name); + protected @Nullable SMTestProxy findTestProxy(@Nullable String testId) { + return getExecutionConsole().getTestsMap().get(testId); } - @Nullable - protected SMTestProxy findTestProxy(final String proxyId) { - return getExecutionConsole().getTestsMap().get(proxyId); + protected @NotNull SMTestProxy findParentTestProxy(@Nullable String parentTestId) { + var rootNode = getResultsViewer().getTestsRootNode(); + if (StringUtil.isEmpty(parentTestId)) { + return rootNode; + } + var node = findTestProxy(parentTestId); + if (node == null) { + return rootNode; + } + return node; } protected void registerTestProxy(final String proxyId, SMTestProxy testProxy) { @@ -72,15 +76,62 @@ public abstract class AbstractTestEventProcessor implements TestEventProcessor { return GradleConsoleProperties.SHOW_INTERNAL_TEST_NODES.value(getProperties()); } - protected final @NotNull String computeLocationUrl(@Nullable SMTestProxy parentProxy, @NotNull String fqClassName, @Nullable String name, @Nullable String displayName) { - if (parentProxy != null) { - for (GradleTestLocationCustomizer customizer : GradleTestLocationCustomizer.EP_NAME.getExtensionList()) { - GradleTestLocationInfo location = customizer.getLocationInfo(getProject(), parentProxy, fqClassName, name, displayName); - if (location != null) { - return findLocationUrl(location.getMethodName(), location.getFqClassName()); - } + protected @NotNull GradleSMTestProxy createTestProxy( + @Nullable String parentTestId, + @NotNull String suiteName, + @NotNull String fqClassName, + @Nullable String methodName, + @Nullable String displayName + ) { + var parentTestProxy = findParentTestProxy(parentTestId); + var locationUrl = createLocationUrl(parentTestProxy, suiteName, fqClassName, methodName); + var testProxy = new GradleSMTestProxy(displayName, methodName == null, locationUrl, fqClassName); + testProxy.setLocator(getExecutionConsole().getUrlProvider()); + testProxy.setParentId(parentTestId); + return testProxy; + } + + private @NotNull String createLocationUrl( + @NotNull SMTestProxy parentProxy, + @NotNull String suiteName, + @NotNull String fqClassName, + @Nullable String methodName + ) { + var project = getProject(); + var isSuite = isSuite(); + var testLocationCustomizer = GradleTestLocationCustomizer.EP_NAME + .findFirstSafe(it -> it.isApplicable(project, parentProxy, isSuite, suiteName, fqClassName, methodName)); + if (testLocationCustomizer != null) { + return testLocationCustomizer.createLocationUrl(parentProxy, isSuite, suiteName, fqClassName, methodName); + } + var locationProtocol = isSuite ? JavaTestLocator.SUITE_PROTOCOL : JavaTestLocator.TEST_PROTOCOL; + return JavaTestLocator.createLocationUrl(locationProtocol, fqClassName, methodName); + } + + protected void setParentForAllNodesInTreePath(@NotNull GradleSMTestProxy node) { + while (node != null) { + var parentId = node.getParentId(); + var parentNode = findParentTestProxy(parentId); + if (node.getParent() == null) { + parentNode.addChild(node); } + if (!node.isInProgress()) { + node.setStarted(); + getResultsViewer().onTestStarted(node); + getExecutionConsole().getEventPublisher().onTestStarted(node); + } + node = ObjectUtils.tryCast(parentNode, GradleSMTestProxy.class); + } + } + + protected void setStartedForAllNodesInTreePath(@NotNull GradleSMTestProxy node) { + while (node != null) { + if (!node.isInProgress()) { + node.setStarted(); + getResultsViewer().onTestStarted(node); + getExecutionConsole().getEventPublisher().onTestStarted(node); + } + node = ObjectUtils.tryCast(node.getParent(), GradleSMTestProxy.class); } - return findLocationUrl(name, fqClassName); } } diff --git a/plugins/gradle/java/src/execution/test/runner/events/AfterSuiteEventProcessor.java b/plugins/gradle/java/src/execution/test/runner/events/AfterSuiteEventProcessor.java index 18b3be4db260..fdaa083d8698 100644 --- a/plugins/gradle/java/src/execution/test/runner/events/AfterSuiteEventProcessor.java +++ b/plugins/gradle/java/src/execution/test/runner/events/AfterSuiteEventProcessor.java @@ -15,13 +15,11 @@ */ package org.jetbrains.plugins.gradle.execution.test.runner.events; -import com.intellij.execution.testframework.JavaTestLocator; import com.intellij.execution.testframework.sm.runner.SMTestProxy; import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemFinishEvent; import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemProgressEvent; import com.intellij.openapi.externalSystem.model.task.event.TestOperationDescriptor; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.execution.test.runner.GradleSMTestProxy; import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsole; @@ -34,6 +32,10 @@ public class AfterSuiteEventProcessor extends AbstractTestEventProcessor { super(executionConsole); } + @Override + protected boolean isSuite() { + return true; + } @Override public void process(@NotNull ExternalSystemProgressEvent testEvent) { @@ -83,10 +85,4 @@ public class AfterSuiteEventProcessor extends AbstractTestEventProcessor { getExecutionConsole().getEventPublisher().onSuiteFinished(testProxy); } } - - @Override - @NotNull - protected String findLocationUrl(@Nullable String name, @NotNull String fqClassName) { - return findLocationUrl(JavaTestLocator.SUITE_PROTOCOL, name, fqClassName); - } } diff --git a/plugins/gradle/java/src/execution/test/runner/events/BeforeSuiteEventProcessor.java b/plugins/gradle/java/src/execution/test/runner/events/BeforeSuiteEventProcessor.java index 2ec784c67942..da108d3f5c59 100644 --- a/plugins/gradle/java/src/execution/test/runner/events/BeforeSuiteEventProcessor.java +++ b/plugins/gradle/java/src/execution/test/runner/events/BeforeSuiteEventProcessor.java @@ -1,8 +1,6 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.gradle.execution.test.runner.events; -import com.intellij.execution.testframework.JavaTestLocator; -import com.intellij.execution.testframework.sm.runner.SMTestProxy; import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemProgressEvent; import com.intellij.openapi.externalSystem.model.task.event.TestOperationDescriptor; import com.intellij.openapi.util.text.StringUtil; @@ -23,72 +21,76 @@ public class BeforeSuiteEventProcessor extends AbstractTestEventProcessor { } @Override - public void process(@NotNull final TestEventXmlView eventXml) throws TestEventXmlView.XmlParserException { - final String testId = eventXml.getTestId(); - final String parentTestId = eventXml.getTestParentId(); - final String name = eventXml.getTestDisplayName(); - final String fqClassName = eventXml.getTestClassName(); + protected boolean isSuite() { + return true; + } - doProcess(testId, parentTestId, name, fqClassName); + @Override + public void process(@NotNull final TestEventXmlView eventXml) throws TestEventXmlView.XmlParserException { + var testId = eventXml.getTestId(); + var parentTestId = eventXml.getTestParentId(); + var suiteName = ObjectUtils.coalesce(StringUtil.nullize(eventXml.getTestClassName()), eventXml.getTestDisplayName()); + var fqClassName = eventXml.getTestClassName(); + var displayName = eventXml.getTestDisplayName(); + + doProcess(testId, parentTestId, suiteName, fqClassName, displayName); } @Override public void process(@NotNull ExternalSystemProgressEvent testEvent) { - TestOperationDescriptor testDescriptor = testEvent.getDescriptor(); - final String testId = testEvent.getEventId(); - final String parentTestId = testEvent.getParentEventId(); - final String name = ObjectUtils.coalesce(testDescriptor.getDisplayName(), testDescriptor.getMethodName(), testId); - final String fqClassName = testDescriptor.getClassName(); + var testDescriptor = testEvent.getDescriptor(); + var testId = testEvent.getEventId(); + var parentTestId = testEvent.getParentEventId(); + var suiteName = StringUtil.notNullize(testDescriptor.getSuiteName()); + var fqClassName = StringUtil.notNullize(testDescriptor.getClassName()); + var displayName = testDescriptor.getDisplayName(); - doProcess(testId, parentTestId, name, fqClassName); + doProcess(testId, parentTestId, suiteName, fqClassName, displayName); } + private void doProcess( + @NotNull String testId, + @Nullable String parentTestId, + @NotNull String suiteName, + @NotNull String fqClassName, + @Nullable String displayName + ) { + var isCombineSameTests = !showInternalTestNodes(); - private void doProcess(String testId, String parentTestId, String name, String fqClassName) { - if (StringUtil.isEmpty(parentTestId)) { - registerTestProxy(testId, getResultsViewer().getTestsRootNode()); + if (isCombineSameTests && isHiddenTestNode(suiteName)) { + var parentTestProxy = findParentTestProxy(parentTestId); + registerTestProxy(testId, parentTestProxy); + return; } - else { - SMTestProxy parentTest = findTestProxy(parentTestId); - if (isHiddenTestNode(name, parentTest)) { - registerTestProxy(testId, parentTest); - } - else { - boolean combineTestsOfTheSameSuite = !showInternalTestNodes(); - String sameSuiteId = name + fqClassName; - if (combineTestsOfTheSameSuite) { - SMTestProxy testProxy = findTestProxy(sameSuiteId); - if (testProxy instanceof GradleSMTestProxy && Objects.equals(testProxy.getParent(), parentTest)) { - registerTestProxy(testId, testProxy); - if (!testProxy.isInProgress()) { - testProxy.setStarted(); - } - return; - } - } - String locationUrl = computeLocationUrl(parentTest, fqClassName, null, name); - final GradleSMTestProxy testProxy = new GradleSMTestProxy(name, true, locationUrl, null); - testProxy.setLocator(getExecutionConsole().getUrlProvider()); - testProxy.setParentId(parentTestId); - testProxy.setStarted(); + if (isCombineSameTests) { + var testProxy = findSuiteTestProxy(suiteName, parentTestId); + if (testProxy != null) { registerTestProxy(testId, testProxy); - if (combineTestsOfTheSameSuite) { - registerTestProxy(sameSuiteId, testProxy); - } + return; } } + + var testProxy = createTestProxy(parentTestId, suiteName, fqClassName, null, displayName); + registerTestProxy(testId, testProxy); + + if (isCombineSameTests) { + registerTestProxy(fqClassName, testProxy); + } } - @Override - @NotNull - protected String findLocationUrl(@Nullable String name, @NotNull String fqClassName) { - return findLocationUrl(JavaTestLocator.SUITE_PROTOCOL, name, fqClassName); + private @Nullable GradleSMTestProxy findSuiteTestProxy(@NotNull String suiteId, @Nullable String parentTestId) { + var parentTestProxy = findParentTestProxy(parentTestId); + var testProxy = findTestProxy(suiteId); + if (testProxy instanceof GradleSMTestProxy) { + if (Objects.equals(testProxy.getParent(), parentTestProxy)) { + return (GradleSMTestProxy)testProxy; + } + } + return null; } - private boolean isHiddenTestNode(String name, SMTestProxy parentTest) { - return parentTest != null && - !showInternalTestNodes() && - StringUtil.startsWith(name, "Gradle Test Executor"); + private static boolean isHiddenTestNode(@Nullable String suiteName) { + return suiteName == null || suiteName.startsWith("Gradle Test Executor") || suiteName.startsWith("Gradle Test Run"); } } diff --git a/plugins/gradle/java/src/execution/test/runner/events/BeforeTestEventProcessor.java b/plugins/gradle/java/src/execution/test/runner/events/BeforeTestEventProcessor.java index 071481a27c3c..0a04918f2fee 100644 --- a/plugins/gradle/java/src/execution/test/runner/events/BeforeTestEventProcessor.java +++ b/plugins/gradle/java/src/execution/test/runner/events/BeforeTestEventProcessor.java @@ -1,18 +1,13 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.gradle.execution.test.runner.events; -import com.intellij.execution.testframework.sm.runner.SMTestProxy; import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemProgressEvent; import com.intellij.openapi.externalSystem.model.task.event.TestOperationDescriptor; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.SmartList; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.gradle.execution.test.runner.GradleSMTestProxy; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsole; -import java.util.List; - /** * @author Vladislav.Soroka */ @@ -24,62 +19,40 @@ public class BeforeTestEventProcessor extends AbstractTestEventProcessor { @Override public void process(@NotNull final TestEventXmlView eventXml) throws TestEventXmlView.XmlParserException { - final String testId = eventXml.getTestId(); - final String parentTestId = eventXml.getTestParentId(); - final String name = eventXml.getTestName(); - final String displayName = eventXml.getTestDisplayName(); - final String fqClassName = eventXml.getTestClassName(); + var testId = eventXml.getTestId(); + var parentTestId = eventXml.getTestParentId(); + var suiteName = eventXml.getTestClassName(); + var fqClassName = eventXml.getTestClassName(); + var methodName = eventXml.getTestName(); + var displayName = eventXml.getTestDisplayName(); - doProcess(testId, parentTestId, name, displayName, fqClassName); + doProcess(testId, parentTestId, suiteName, fqClassName, methodName, displayName); } @Override public void process(@NotNull ExternalSystemProgressEvent testEvent) { - TestOperationDescriptor testDescriptor = testEvent.getDescriptor(); - final String testId = testEvent.getEventId(); - final String parentTestId = testEvent.getParentEventId(); - final String fqClassName = testDescriptor.getClassName(); + var testDescriptor = testEvent.getDescriptor(); + var testId = testEvent.getEventId(); + var parentTestId = testEvent.getParentEventId(); + var suiteName = StringUtil.notNullize(testDescriptor.getSuiteName()); + var fqClassName = StringUtil.notNullize(testDescriptor.getClassName()); + var methodName = testDescriptor.getMethodName(); + var displayName = testDescriptor.getDisplayName(); - doProcess(testId, parentTestId, testDescriptor.getMethodName(), testDescriptor.getDisplayName(), fqClassName); + doProcess(testId, parentTestId, suiteName, fqClassName, methodName, displayName); } - private void doProcess(String testId, String parentTestId, String name, String displayName, String fqClassName) { - String locationUrl = computeLocationUrl(findTestProxy(parentTestId), fqClassName, name, displayName); - final GradleSMTestProxy testProxy = new GradleSMTestProxy(displayName, false, locationUrl, fqClassName); - - testProxy.setStarted(); - testProxy.setLocator(getExecutionConsole().getUrlProvider()); + private void doProcess( + @NotNull String testId, + @Nullable String parentTestId, + @NotNull String suiteName, + @NotNull String fqClassName, + @Nullable String methodName, + @Nullable String displayName + ) { + var testProxy = createTestProxy(parentTestId, suiteName, fqClassName, methodName, displayName); registerTestProxy(testId, testProxy); - - if (StringUtil.isEmpty(parentTestId)) { - getResultsViewer().getTestsRootNode().addChild(testProxy); - } - else { - final SMTestProxy parentTestProxy = findTestProxy(parentTestId); - if (parentTestProxy != null) { - final List notYetAddedParents = new SmartList<>(); - SMTestProxy currentParentTestProxy = parentTestProxy; - while (currentParentTestProxy instanceof GradleSMTestProxy) { - final String parentId = ((GradleSMTestProxy)currentParentTestProxy).getParentId(); - if (currentParentTestProxy.getParent() == null && parentId != null) { - notYetAddedParents.add((GradleSMTestProxy)currentParentTestProxy); - } - currentParentTestProxy = findTestProxy(parentId); - } - - for (GradleSMTestProxy gradleSMTestProxy : ContainerUtil.reverse(notYetAddedParents)) { - final SMTestProxy parentTestProxy1 = findTestProxy(gradleSMTestProxy.getParentId()); - if (parentTestProxy1 != null) { - parentTestProxy1.addChild(gradleSMTestProxy); - getResultsViewer().onSuiteStarted(gradleSMTestProxy); - getExecutionConsole().getEventPublisher().onSuiteStarted(gradleSMTestProxy); - } - } - parentTestProxy.addChild(testProxy); - } - } - - getResultsViewer().onTestStarted(testProxy); - getExecutionConsole().getEventPublisher().onTestStarted(testProxy); + setParentForAllNodesInTreePath(testProxy); + setStartedForAllNodesInTreePath(testProxy); } } diff --git a/plugins/gradle/java/src/execution/test/runner/events/OnOutputEventProcessor.java b/plugins/gradle/java/src/execution/test/runner/events/OnOutputEventProcessor.java index a4c0a0c2b24a..19258fd0327b 100644 --- a/plugins/gradle/java/src/execution/test/runner/events/OnOutputEventProcessor.java +++ b/plugins/gradle/java/src/execution/test/runner/events/OnOutputEventProcessor.java @@ -2,13 +2,13 @@ package org.jetbrains.plugins.gradle.execution.test.runner.events; import com.intellij.execution.process.ProcessOutputTypes; -import com.intellij.execution.testframework.sm.runner.SMTestProxy; import com.intellij.execution.testframework.sm.runner.events.TestOutputEvent; import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemMessageEvent; import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemProgressEvent; import com.intellij.openapi.externalSystem.model.task.event.TestOperationDescriptor; -import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsole; /** @@ -16,47 +16,37 @@ import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionCo */ public class OnOutputEventProcessor extends AbstractTestEventProcessor { - private static final String OUT = "StdOut"; - private static final String ERR = "StdErr"; - public OnOutputEventProcessor(GradleTestsExecutionConsole executionConsole) { super(executionConsole); } @Override - public void process(@NotNull final TestEventXmlView eventXml) throws TestEventXmlView.XmlParserException { - final String testId = eventXml.getTestId(); - final String destinationString = eventXml.getTestEventTestDescription(); - final String output = decode(eventXml.getTestEventTest()); + public void process(@NotNull TestEventXmlView eventXml) throws TestEventXmlView.XmlParserException { + var testId = eventXml.getTestId(); + var output = decode(eventXml.getTestEventTest()); + var isStdOut = "StdOut".equals(eventXml.getTestEventTestDescription()); - Key destination = OUT.equals(destinationString) ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR; - doProcess(testId, output, destination); + doProcess(testId, output, isStdOut); } @Override public void process(@NotNull ExternalSystemProgressEvent testEvent) { - if (!(testEvent instanceof ExternalSystemMessageEvent)) { - return; - } - TestOperationDescriptor testDescriptor = testEvent.getDescriptor(); + if (testEvent instanceof ExternalSystemMessageEvent messageEvent) { + var parentTestId = testEvent.getParentEventId(); + var isStdOut = messageEvent.isStdOut(); + var message = StringUtil.notNullize(messageEvent.getMessage()); - final String testId = testEvent.getEventId(); - final String description = ((ExternalSystemMessageEvent)testEvent).getDescription(); - - if (description == null) { - doProcess(testId, "", ProcessOutputTypes.STDERR); - } else if (description.startsWith(OUT)) { - doProcess(testId, description.substring(OUT.length()), ProcessOutputTypes.STDOUT); - } else if (description.startsWith(ERR)) { - doProcess(testId, description.substring(ERR.length()), ProcessOutputTypes.STDERR); + doProcess(parentTestId, message, isStdOut); } } - private void doProcess(String testId, String output, @NotNull Key type) { - SMTestProxy testProxy = findTestProxy(testId); - if (testProxy == null) return; + private void doProcess(@Nullable String parentTestId, @NotNull String message, boolean isStdOut) { + var testProxy = findParentTestProxy(parentTestId); + var name = testProxy.getName(); + var event = new TestOutputEvent(name, message, isStdOut); + var destination = isStdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR; - testProxy.addOutput(output, type); - getExecutionConsole().getEventPublisher().onTestOutput(testProxy, new TestOutputEvent(testProxy.getName(), output, type == ProcessOutputTypes.STDOUT)); + testProxy.addOutput(message, destination); + getExecutionConsole().getEventPublisher().onTestOutput(testProxy, event); } } diff --git a/plugins/gradle/java/src/execution/test/runner/spock/SpockGradleTestLocationCustomizer.kt b/plugins/gradle/java/src/execution/test/runner/spock/SpockGradleTestLocationCustomizer.kt index 8822401d1864..e0845337edf7 100644 --- a/plugins/gradle/java/src/execution/test/runner/spock/SpockGradleTestLocationCustomizer.kt +++ b/plugins/gradle/java/src/execution/test/runner/spock/SpockGradleTestLocationCustomizer.kt @@ -1,45 +1,78 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.gradle.execution.test.runner.spock +import com.intellij.execution.testframework.JavaTestLocator import com.intellij.execution.testframework.sm.runner.SMTestProxy import com.intellij.openapi.application.runReadAction import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project +import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.JavaPsiFacade import com.intellij.psi.search.GlobalSearchScope +import com.intellij.util.io.URLUtil +import com.intellij.util.text.nullize import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestLocationCustomizer -import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestLocationCustomizer.GradleTestLocationInfo import org.jetbrains.plugins.groovy.ext.spock.isSpockSpecification internal class SpockGradleTestLocationCustomizer : GradleTestLocationCustomizer { - override fun getLocationInfo(project: Project, - parent: SMTestProxy, - fqClassName: String, - methodName: String?, - displayName: String?): GradleTestLocationInfo? { - val className = getClassName(fqClassName, parent) - if (!shouldProcessAsSpock(project, className, methodName)) return null - val actualMethodName = if (fqClassName.isEmpty() && displayName != null && methodName == null) { - displayName - } - else { - parent.name - } - return GradleTestLocationInfo(className, actualMethodName) - } -} -private fun shouldProcessAsSpock(project: Project, - className: String, - name: String?): Boolean { - return runReadAction { - DumbService.getInstance(project).computeWithAlternativeResolveEnabled { - val clazz = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project)) - clazz != null && clazz.isSpockSpecification() && (name == null || clazz.methods.all { it.name != name }) + override fun isApplicable( + project: Project, + parent: SMTestProxy, + isSuite: Boolean, + suiteName: String, + fqClassName: String, + methodName: String? + ): Boolean { + return runReadAction { + DumbService.getInstance(project).computeWithAlternativeResolveEnabled { + val patchedFqClassName = patchClassName(parent, suiteName, fqClassName) + val patchedMethodName = patchMethodName(parent, isSuite, suiteName, fqClassName, methodName) + val scope = GlobalSearchScope.allScope(project) + val psiFacade = JavaPsiFacade.getInstance(project) + val psiClass = psiFacade.findClass(patchedFqClassName, scope) + psiClass != null && psiClass.isSpockSpecification() && (patchedMethodName == null || psiClass.methods.any { it.name == patchedMethodName }) + } } } -} -private fun getClassName(fqClassName: String, parent: SMTestProxy): String { - return fqClassName.takeUnless { it.isEmpty() } ?: parent.name + override fun createLocationUrl( + parent: SMTestProxy, + isSuite: Boolean, + suiteName: String, + fqClassName: String, + methodName: String? + ): String { + val protocol = if (isSuite) JavaTestLocator.SUITE_PROTOCOL else JavaTestLocator.TEST_PROTOCOL + val patchedFqClassName = patchClassName(parent, suiteName, fqClassName) + val patchedMethodName = patchMethodName(parent, isSuite, suiteName, fqClassName, methodName) + return JavaTestLocator.createLocationUrl(protocol, patchedFqClassName, patchedMethodName) + } + + private fun patchClassName(parent: SMTestProxy, suiteName: String, fqClassName: String): String { + return when { + fqClassName.isEmpty() -> getParentClassName(parent) ?: suiteName + else -> fqClassName + } + } + + private fun patchMethodName(parent: SMTestProxy, isSuite: Boolean, suiteName: String, fqClassName: String, methodName: String?): String? { + return when { + !isSuite -> getParentMethodName(parent) ?: methodName.nullize() ?: suiteName + fqClassName.isEmpty() -> methodName.nullize() ?: getParentMethodName(parent) ?: suiteName + else -> methodName + } + } + + private fun getParentClassName(parent: SMTestProxy): String? { + val locationUrl = parent.locationUrl ?: return null + val locationPath = URLUtil.extractPath(locationUrl) + return StringUtil.substringBefore(locationPath, "/") ?: locationPath + } + + private fun getParentMethodName(parent: SMTestProxy): String? { + val locationUrl = parent.locationUrl ?: return null + val locationPath = URLUtil.extractPath(locationUrl) + return StringUtil.substringAfter(locationPath, "/") + } } \ No newline at end of file diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressEventConverter.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressEventConverter.java index 032a210289f1..aee376085ea9 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressEventConverter.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressEventConverter.java @@ -2,98 +2,197 @@ package org.jetbrains.plugins.gradle.service.execution; import com.intellij.build.events.EventResult; -import com.intellij.build.events.impl.*; +import com.intellij.build.events.impl.FinishEventImpl; +import com.intellij.build.events.impl.ProgressBuildEventImpl; +import com.intellij.build.events.impl.StartEventImpl; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationEvent; -import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemBuildEvent; +import com.intellij.openapi.externalSystem.model.task.event.OperationResult; +import com.intellij.openapi.externalSystem.model.task.event.TestOperationDescriptor; +import com.intellij.openapi.externalSystem.model.task.event.*; import com.intellij.openapi.util.NlsSafe; +import com.intellij.util.ObjectUtils; +import com.intellij.util.PathUtil; +import com.intellij.util.containers.ContainerUtil; +import org.gradle.tooling.events.FailureResult; +import org.gradle.tooling.events.OperationDescriptor; +import org.gradle.tooling.events.SkippedResult; +import org.gradle.tooling.events.SuccessResult; import org.gradle.tooling.events.*; +import org.gradle.tooling.events.task.TaskFinishEvent; import org.gradle.tooling.events.task.TaskProgressEvent; +import org.gradle.tooling.events.task.TaskStartEvent; import org.gradle.tooling.events.task.TaskSuccessResult; -import org.gradle.tooling.events.test.TestProgressEvent; +import org.gradle.tooling.events.test.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.util.GradleBundle; +import java.util.List; +import java.util.StringJoiner; + /** * @author Vladislav.Soroka */ public final class GradleProgressEventConverter { - static EventId getEventId(@NotNull ProgressEvent event, @NotNull String operationId) { - OperationDescriptor descriptor = event.getDescriptor(); - String eventId = operationId + descriptor.getDisplayName(); - String parentEventId = descriptor.getParent() == null ? null : - operationId + descriptor.getParent().getDisplayName(); - return new EventId(eventId, parentEventId); + private static final Logger LOG = Logger.getInstance("com.intellij.openapi.externalSystem.event-processing"); + + private static @NotNull String createEventId(@NotNull OperationDescriptor descriptor, @NotNull String operationId) { + var joiner = new StringJoiner(" > "); + joiner.add("[" + operationId + "]"); + var currentDescriptor = descriptor; + while (currentDescriptor != null) { + joiner.add("[" + currentDescriptor.getDisplayName() + "]"); + currentDescriptor = currentDescriptor.getParent(); + } + return joiner.toString(); } - @NotNull - public static ExternalSystemTaskNotificationEvent convert(@NotNull ExternalSystemTaskId id, - @NotNull ProgressEvent event, - @NotNull String operationId) { - return convert(id, event, getEventId(event, operationId)); + public static @Nullable ExternalSystemTaskNotificationEvent createTaskNotificationEvent( + @NotNull ExternalSystemTaskId taskId, + @NotNull String operationId, + @NotNull ProgressEvent event + ) { + if (event instanceof TaskProgressEvent taskProgressEvent) { + return convertTaskProgressEvent(taskProgressEvent, taskId, operationId); + } + return convertTestProgressEvent(event, taskId, operationId); } - @NotNull - public static ExternalSystemTaskNotificationEvent convert(@NotNull ExternalSystemTaskId id, - @NotNull ProgressEvent event, - @NotNull EventId eventId) { - @NlsSafe final String description = event.getDescriptor().getName(); + private static @NotNull ExternalSystemTaskNotificationEvent convertTaskProgressEvent( + @NotNull TaskProgressEvent event, + @NotNull ExternalSystemTaskId taskId, + @NotNull String operationId + ) { + var eventId = createEventId(event.getDescriptor(), operationId); + var eventTime = event.getEventTime(); + var message = event.getDescriptor().getName(); - if (event instanceof StartEvent) { - return new ExternalSystemBuildEvent( - id, new StartEventImpl(eventId.id, eventId.parentId, event.getEventTime(), description)); + if (event instanceof TaskStartEvent) { + return new ExternalSystemBuildEvent(taskId, new StartEventImpl(eventId, taskId, eventTime, message)); + } + else if (event instanceof TaskFinishEvent finishEvent) { + var result = finishEvent.getResult(); + var eventResult = convertTaskProgressEventResult(result); + if (eventResult != null) { + return new ExternalSystemBuildEvent(taskId, new FinishEventImpl(eventId, taskId, eventTime, message, eventResult)); + } } else if (event instanceof StatusEvent statusEvent) { - return new ExternalSystemBuildEvent(id, new ProgressBuildEventImpl( - eventId.id, eventId.parentId, event.getEventTime(), description, statusEvent.getTotal(), statusEvent.getProgress(), - statusEvent.getUnit())); - } - else if (event instanceof FinishEvent) { - return new ExternalSystemBuildEvent( - id, - new FinishEventImpl(eventId.id, eventId.parentId, event.getEventTime(), description, convert(((FinishEvent)event).getResult()))); - } - else if (event instanceof TaskProgressEvent) { - return new ExternalSystemBuildEvent( - id, new ProgressBuildEventImpl(eventId.id, eventId.parentId, event.getEventTime(), description, -1, -1, "")); - } - else { - return new ExternalSystemTaskNotificationEvent(id, description); + var total = statusEvent.getTotal(); + var progress = statusEvent.getProgress(); + var unit = statusEvent.getUnit(); + return new ExternalSystemBuildEvent(taskId, new ProgressBuildEventImpl(eventId, taskId, eventTime, message, total, progress, unit)); } + LOG.warn("Undefined Gradle event " + event.getClass().getSimpleName() + " " + event); + var description = event.getDescriptor().getName(); + return new ExternalSystemTaskNotificationEvent(taskId, description); } - @NotNull - public static ExternalSystemTaskNotificationEvent convert(ExternalSystemTaskId id, ProgressEvent event) { - return convert(id, event, ""); + private static @Nullable EventResult convertTaskProgressEventResult(@NotNull org.gradle.tooling.events.OperationResult result) { + if (result instanceof SuccessResult) { + var isUpToDate = result instanceof TaskSuccessResult && ((TaskSuccessResult)result).isUpToDate(); + return new com.intellij.build.events.impl.SuccessResultImpl(isUpToDate); + } + if (result instanceof FailureResult) { + return new com.intellij.build.events.impl.FailureResultImpl(null, null); + } + if (result instanceof SkippedResult) { + return new com.intellij.build.events.impl.SkippedResultImpl(); + } + LOG.warn("Undefined operation result " + result.getClass().getSimpleName() + " " + result); + return null; } - @NotNull - private static EventResult convert(OperationResult operationResult) { - if (operationResult instanceof FailureResult) { - return new FailureResultImpl(null, null); + private static @Nullable ExternalSystemTaskNotificationEvent convertTestProgressEvent( + @NotNull ProgressEvent event, + @NotNull ExternalSystemTaskId taskId, + @NotNull String operationId + ) { + var eventId = createEventId(event.getDescriptor(), operationId); + var parentEventId = ObjectUtils.doIfNotNull(event.getDescriptor().getParent(), it -> createEventId(it, operationId)); + var descriptor = convertDescriptor(event); + + if (event instanceof TestStartEvent) { + return new ExternalSystemTaskExecutionEvent(taskId, new ExternalSystemStartEventImpl<>(eventId, parentEventId, descriptor)); } - else if (operationResult instanceof SkippedResult) { - return new SkippedResultImpl(); + if (event instanceof TestFinishEvent finishEvent) { + var result = finishEvent.getResult(); + var operationResult = convertTestProgressEventResult(result); + if (operationResult != null) { + var esEvent = new ExternalSystemFinishEventImpl<>(eventId, parentEventId, descriptor, operationResult); + return new ExternalSystemTaskExecutionEvent(taskId, esEvent); + } } - else { - final boolean isUpToDate = operationResult instanceof TaskSuccessResult && ((TaskSuccessResult)operationResult).isUpToDate(); - return new SuccessResultImpl(isUpToDate); + if (event instanceof TestOutputEvent outputEvent) { + var outputDescriptor = outputEvent.getDescriptor(); + var destination = outputDescriptor.getDestination(); + var isStdOut = destination == Destination.StdOut; + var message = outputDescriptor.getMessage(); + var description = (isStdOut ? "StdOut" : "StdErr") + message; + var esEvent = new ExternalSystemMessageEventImpl<>(eventId, parentEventId, descriptor, isStdOut, message, description); + return new ExternalSystemTaskExecutionEvent(taskId, esEvent); } + return null; } - @Nullable - static ExternalSystemTaskNotificationEvent createProgressBuildEvent(@NotNull ExternalSystemTaskId taskId, - @NotNull Object id, - @NotNull ProgressEvent event) { + private static @Nullable OperationResult convertTestProgressEventResult(@NotNull org.gradle.tooling.events.OperationResult result) { + var startTime = result.getStartTime(); + var endTime = result.getEndTime(); + if (result instanceof SuccessResult) { + var isUpToDate = result instanceof TaskSuccessResult && ((TaskSuccessResult)result).isUpToDate(); + return new SuccessResultImpl(startTime, endTime, isUpToDate); + } + if (result instanceof FailureResult) { + var failures = convertFailureResult((FailureResult)result); + return new FailureResultImpl(startTime, endTime, failures); + } + if (result instanceof SkippedResult) { + return new SkippedResultImpl(startTime, endTime); + } + LOG.warn("Undefined operation result " + result.getClass().getSimpleName() + " " + result); + return null; + } + + private static @NotNull List convertFailureResult(@NotNull FailureResult failure) { + return ContainerUtil.map(failure.getFailures(), it -> convertFailure(it)); + } + + private static @NotNull Failure convertFailure(@NotNull org.gradle.tooling.Failure failure) { + return new FailureImpl( + failure.getMessage(), + failure.getDescription(), + ContainerUtil.map(failure.getCauses(), it -> convertFailure(it)) + ); + } + + private static @NotNull TestOperationDescriptor convertDescriptor(@NotNull ProgressEvent event) { + var descriptor = event.getDescriptor(); + var eventTime = event.getEventTime(); + var displayName = descriptor.getDisplayName(); + if (descriptor instanceof JvmTestOperationDescriptor jvmDescriptor) { + var suiteName = jvmDescriptor.getSuiteName(); + var className = jvmDescriptor.getClassName(); + var methodName = jvmDescriptor.getMethodName(); + return new TestOperationDescriptorImpl(displayName, eventTime, suiteName, className, methodName); + } + return new TestOperationDescriptorImpl(displayName, eventTime, null, null, null); + } + + public static @Nullable ExternalSystemTaskNotificationEvent createProgressBuildEvent( + @NotNull ExternalSystemTaskId taskId, + @NotNull Object id, + @NotNull ProgressEvent event + ) { long total = -1; long progress = -1; String unit = ""; @NlsSafe String operationName = event.getDescriptor().getName(); if (operationName.startsWith("Download ")) { String path = operationName.substring("Download ".length()); - operationName = GradleBundle.message("progress.title.download", getFileName(path)); + operationName = GradleBundle.message("progress.title.download", PathUtil.getFileName(path)); } else if (event instanceof TaskProgressEvent) { operationName = GradleBundle.message("progress.title.run.tasks"); @@ -116,17 +215,18 @@ public final class GradleProgressEventConverter { taskId, new ProgressBuildEventImpl(id, null, event.getEventTime(), operationName + "...", total, progress, unit)); } - @Nullable - static ExternalSystemTaskNotificationEvent legacyCreateProgressBuildEvent(@NotNull ExternalSystemTaskId taskId, - @NotNull Object id, - @NotNull String event) { + public static @Nullable ExternalSystemTaskNotificationEvent legacyCreateProgressBuildEvent( + @NotNull ExternalSystemTaskId taskId, + @NotNull Object id, + @NotNull String event + ) { long total = -1; long progress = -1; String unit = ""; @NlsSafe String operationName = event; if (operationName.startsWith("Download ")) { String path = operationName.substring("Download ".length()); - operationName = GradleBundle.message("progress.title.download", getFileName(path)); + operationName = GradleBundle.message("progress.title.download", PathUtil.getFileName(path)); } else if (operationName.startsWith("Task: ")) { operationName = GradleBundle.message("progress.title.run.tasks"); @@ -147,23 +247,10 @@ public final class GradleProgressEventConverter { taskId, new ProgressBuildEventImpl(id, null, 0, operationName + "...", total, progress, unit)); } - @NotNull - private static String getFileName(String path) { - int index = path.lastIndexOf('/'); - if (index > 0) { - String fileName = path.substring(index + 1); - if (!fileName.isEmpty()) return fileName; - } - return path; - } - - static class EventId { - Object id; - Object parentId; - - EventId(Object id, Object parentId) { - this.id = id; - this.parentId = parentId; - } + public static @NotNull ExternalSystemTaskNotificationEvent legacyCreateTaskNotificationEvent( + @NotNull ExternalSystemTaskId taskId, + @NotNull String event + ) { + return new ExternalSystemTaskNotificationEvent(taskId, event); } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressListener.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressListener.java index a6aa0e69c668..a46657f88ef8 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressListener.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressListener.java @@ -10,22 +10,17 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationEvent; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener; -import com.intellij.openapi.externalSystem.model.task.event.TestOperationDescriptor; import com.intellij.openapi.externalSystem.model.task.event.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.NlsSafe; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.Navigatable; -import com.intellij.util.containers.ContainerUtil; import org.gradle.internal.impldep.com.google.gson.GsonBuilder; -import org.gradle.tooling.ProgressEvent; -import org.gradle.tooling.ProgressListener; import org.gradle.tooling.events.FinishEvent; -import org.gradle.tooling.events.OperationResult; +import org.gradle.tooling.events.ProgressEvent; +import org.gradle.tooling.events.ProgressListener; import org.gradle.tooling.events.StatusEvent; -import org.gradle.tooling.events.task.TaskProgressEvent; -import org.gradle.tooling.events.test.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.tooling.Message; @@ -42,7 +37,7 @@ import static org.jetbrains.plugins.gradle.tooling.internal.ExtraModelBuilder.MO /** * @author Vladislav.Soroka */ -public class GradleProgressListener implements ProgressListener, org.gradle.tooling.events.ProgressListener { +public class GradleProgressListener implements ProgressListener, org.gradle.tooling.ProgressListener { private static final Logger LOG = Logger.getInstance(GradleProgressListener.class); private final ExternalSystemTaskNotificationListener myListener; @@ -53,143 +48,60 @@ public class GradleProgressListener implements ProgressListener, org.gradle.tool private static final String STARTING_GRADLE_DAEMON_EVENT = "Starting Gradle Daemon"; private ExternalSystemTaskNotificationEvent myLastStatusChange = null; - public GradleProgressListener(@NotNull ExternalSystemTaskNotificationListener listener, - @NotNull ExternalSystemTaskId taskId) { + public GradleProgressListener( + @NotNull ExternalSystemTaskNotificationListener listener, + @NotNull ExternalSystemTaskId taskId + ) { this(listener, taskId, null); } - public GradleProgressListener(@NotNull ExternalSystemTaskNotificationListener listener, - @NotNull ExternalSystemTaskId taskId, - @Nullable String buildRootDir) { + public GradleProgressListener( + @NotNull ExternalSystemTaskNotificationListener listener, + @NotNull ExternalSystemTaskId taskId, + @Nullable String buildRootDir + ) { myListener = listener; myTaskId = taskId; - myOperationId = (taskId.hashCode() + FileUtil.pathHashCode(buildRootDir == null ? UUID.randomUUID().toString() : buildRootDir)) + "_"; - } - - @Override - public void statusChanged(org.gradle.tooling.events.ProgressEvent event) { - GradleProgressEventConverter.EventId eventId = GradleProgressEventConverter.getEventId(event, myOperationId); - ExternalSystemTaskNotificationEvent progressBuildEvent = - GradleProgressEventConverter.createProgressBuildEvent(myTaskId, myTaskId, event); - sendProgressToOutputIfNeeded(event); - if (progressBuildEvent != null && event instanceof StatusEvent) { - // update IDE progress determinate indicator - myListener.onStatusChange(progressBuildEvent); - } - - maybeUpdateTaskStatus(progressBuildEvent); - - if (event instanceof TestOutputEvent) { - final ExternalSystemTaskNotificationEvent testNotificationEvent = convertToTestNotificationEvent((TestOutputEvent)event, eventId); - if (testNotificationEvent != null) { - myListener.onStatusChange(testNotificationEvent); - } - } - - if (event instanceof TestProgressEvent) { - final ExternalSystemTaskNotificationEvent testNotificationEvent = convertToTestNotificationEvent((TestProgressEvent)event, eventId); - if (testNotificationEvent != null) { - myListener.onStatusChange(testNotificationEvent); - } - } - - - if (event instanceof TaskProgressEvent) { - ExternalSystemTaskNotificationEvent notificationEvent = GradleProgressEventConverter.convert( - myTaskId, event, new GradleProgressEventConverter.EventId(eventId.id, myTaskId)); - myListener.onStatusChange(notificationEvent); - } - } - - private ExternalSystemTaskNotificationEvent convertToTestNotificationEvent(TestOutputEvent outputEvent, - GradleProgressEventConverter.EventId eventId) { - TestOutputDescriptor descriptor = outputEvent.getDescriptor(); - String prefix = outputEvent.getDescriptor().getDestination() == Destination.StdOut ? "StdOut" : "StdErr"; - String message = prefix + outputEvent.getDescriptor().getMessage(); - if (descriptor instanceof JvmTestOperationDescriptor) { - final TestOperationDescriptor operationDescriptor = convertDescriptor(outputEvent, (JvmTestOperationDescriptor)descriptor); - ExternalSystemMessageEvent event = new ExternalSystemMessageEventImpl<>(eventId.id.toString(), - eventId.parentId.toString(), - operationDescriptor, - message); - return new ExternalSystemTaskExecutionEvent(myTaskId, event); - } - - return null; - } - - private Failure convert(org.gradle.tooling.Failure gradleFailure) { - return new FailureImpl(gradleFailure.getMessage(), - gradleFailure.getDescription(), - ContainerUtil.map(gradleFailure.getCauses(), this::convert)); - } - - private ExternalSystemTaskNotificationEvent convertToTestNotificationEvent(TestProgressEvent testProgressEvent, - GradleProgressEventConverter.EventId eventId) { - org.gradle.tooling.events.test.TestOperationDescriptor descriptor = testProgressEvent.getDescriptor(); - - if (descriptor instanceof JvmTestOperationDescriptor) { - final TestOperationDescriptor operationDescriptor = convertDescriptor(testProgressEvent, (JvmTestOperationDescriptor)descriptor); - if (testProgressEvent instanceof TestStartEvent) { - ExternalSystemStartEvent event = new ExternalSystemStartEventImpl<>(eventId.id.toString(), - eventId.parentId.toString(), - operationDescriptor); - return new ExternalSystemTaskExecutionEvent(myTaskId, event); - } - - if (testProgressEvent instanceof TestFinishEvent) { - TestOperationResult gradleResult = ((TestFinishEvent)testProgressEvent).getResult(); - - com.intellij.openapi.externalSystem.model.task.event.OperationResult operationResult = null; - if (gradleResult instanceof TestSuccessResult) { - operationResult = new SuccessResultImpl(gradleResult.getStartTime(), gradleResult.getEndTime(), true); - } else if (gradleResult instanceof TestFailureResult gradleFailure) { - operationResult = new FailureResultImpl(gradleFailure.getStartTime(), gradleFailure.getEndTime(), ContainerUtil.map(gradleFailure.getFailures(), this::convert)); - } else if (gradleResult instanceof TestSkippedResult) { - operationResult = new SkippedResultImpl(gradleResult.getStartTime(), gradleResult.getEndTime()); - } - - if (operationResult != null) { - ExternalSystemFinishEvent event = - new ExternalSystemFinishEventImpl<>(eventId.id.toString(), - eventId.parentId.toString(), - operationDescriptor, - operationResult); - - return new ExternalSystemTaskExecutionEvent(myTaskId, event); - } - } - } - - return null; - } - - @NotNull - private static TestOperationDescriptor convertDescriptor(org.gradle.tooling.events.ProgressEvent testProgressEvent, - JvmTestOperationDescriptor descriptor) { - String id = descriptor.getDisplayName(); - boolean parentIsTest = descriptor.getParent() instanceof org.gradle.tooling.events.test.TestOperationDescriptor; - String parentId = parentIsTest ? descriptor.getParent().getDisplayName() : null; - - return new TestOperationDescriptorImpl(descriptor.getDisplayName(), - testProgressEvent.getEventTime(), - descriptor.getSuiteName(), - descriptor.getClassName(), - descriptor.getMethodName() - ); + myOperationId = taskId.hashCode() + ":" + FileUtil.pathHashCode(buildRootDir == null ? UUID.randomUUID().toString() : buildRootDir); } @Override public void statusChanged(ProgressEvent event) { - String eventDescription = event.getDescription(); - if (maybeReportModelBuilderMessage(eventDescription)) { - return; + sendProgressToOutputIfNeeded(event); + + var progressBuildEvent = GradleProgressEventConverter.createProgressBuildEvent(myTaskId, myTaskId, event); + if (progressBuildEvent != null) { + if (event instanceof StatusEvent) { + // update IDE progress determinate indicator + myListener.onStatusChange(progressBuildEvent); + } + else if (!progressBuildEvent.equals(myLastStatusChange)) { + myListener.onStatusChange(progressBuildEvent); + myLastStatusChange = progressBuildEvent; + } + } + + var taskNotificationEvent = GradleProgressEventConverter.createTaskNotificationEvent(myTaskId, myOperationId, event); + if (taskNotificationEvent != null) { + myListener.onStatusChange(taskNotificationEvent); + } + } + + @Override + public void statusChanged(org.gradle.tooling.ProgressEvent event) { + var eventDescription = event.getDescription(); + if (!maybeReportModelBuilderMessage(eventDescription)) { + var progressBuildEvent = GradleProgressEventConverter.legacyCreateProgressBuildEvent(myTaskId, myTaskId, eventDescription); + if (progressBuildEvent != null && !progressBuildEvent.equals(myLastStatusChange)) { + myListener.onStatusChange(progressBuildEvent); + myLastStatusChange = progressBuildEvent; + } + + var taskNotificationEvent = GradleProgressEventConverter.legacyCreateTaskNotificationEvent(myTaskId, eventDescription); + myListener.onStatusChange(taskNotificationEvent); + + reportGradleDaemonStartingEvent(eventDescription); } - ExternalSystemTaskNotificationEvent progressBuildEvent = - GradleProgressEventConverter.legacyCreateProgressBuildEvent(myTaskId, myTaskId, eventDescription); - maybeUpdateTaskStatus(progressBuildEvent); - myListener.onStatusChange(new ExternalSystemTaskNotificationEvent(myTaskId, eventDescription)); - reportGradleDaemonStartingEvent(eventDescription); } private boolean maybeReportModelBuilderMessage(String eventDescription) { @@ -221,16 +133,7 @@ public class GradleProgressListener implements ProgressListener, org.gradle.tool return false; } - private void maybeUpdateTaskStatus(@Nullable ExternalSystemTaskNotificationEvent progressBuildEvent) { - if (progressBuildEvent != null) { - if (!progressBuildEvent.equals(myLastStatusChange)) { - myListener.onStatusChange(progressBuildEvent); - myLastStatusChange = progressBuildEvent; - } - } - } - - private void sendProgressToOutputIfNeeded(org.gradle.tooling.events.ProgressEvent progressEvent) { + private void sendProgressToOutputIfNeeded(ProgressEvent progressEvent) { @NlsSafe final String operationName = progressEvent.getDescriptor().getName(); if (progressEvent instanceof StatusEvent) { StatusEvent statusEvent = ((StatusEvent)progressEvent); @@ -253,7 +156,7 @@ public class GradleProgressListener implements ProgressListener, org.gradle.tool else if (progressEvent instanceof FinishEvent finishEvent) { StatusEvent statusEvent = myDownloadStatusEventIds.remove(operationName); if (statusEvent != null) { - OperationResult operationResult = finishEvent.getResult(); + var operationResult = finishEvent.getResult(); long duration = operationResult.getEndTime() - operationResult.getStartTime(); long progress = statusEvent.getProgress() > 0 ? statusEvent.getProgress() : 0; long total = statusEvent.getTotal() > 0 ? statusEvent.getTotal() : 0;