IDEA-116904 Gradle: Expand ide junit execution/report facilities to tests run via gradle

This commit is contained in:
Vladislav.Soroka
2014-03-18 12:35:50 +04:00
parent b9d805701f
commit 453c95b280
36 changed files with 2258 additions and 57 deletions
@@ -0,0 +1,53 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.externalSystem.execution;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTask;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;
/**
* @author Vladislav.Soroka
* @since 2/18/14
*/
public interface ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration extends RunConfiguration> {
ExtensionPointName<ExternalSystemExecutionConsoleManager> EP_NAME
= ExtensionPointName.create("com.intellij.externalSystemExecutionConsoleManager");
@NotNull
ProjectSystemId getExternalSystemId();
@NotNull
ExecutionConsole attachExecutionConsole(@NotNull ExternalSystemTask task,
@NotNull Project project,
@NotNull ExternalSystemRunConfiguration configuration,
@NotNull Executor executor,
@NotNull ExecutionEnvironment env,
@NotNull ProcessHandler processHandler) throws ExecutionException;
void onOutput(@NotNull String text, @NotNull Key processOutputType);
boolean isApplicableFor(@NotNull ExternalSystemTask task);
}
@@ -17,6 +17,7 @@ package com.intellij.openapi.externalSystem.util;
import com.intellij.execution.rmi.RemoteUtil;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
@@ -27,7 +28,6 @@ import com.intellij.openapi.externalSystem.model.ExternalSystemException;
import com.intellij.openapi.externalSystem.model.Key;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.project.LibraryData;
import com.intellij.openapi.externalSystem.model.project.LibraryDependencyData;
import com.intellij.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings;
import com.intellij.openapi.externalSystem.service.ParametersEnhancer;
import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
@@ -37,22 +37,22 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.BooleanFunction;
import com.intellij.util.NullableFunction;
import com.intellij.util.PathUtil;
import com.intellij.util.PathsList;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.containers.TransferToEDTQueue;
import com.intellij.util.lang.UrlClassLoader;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -120,6 +120,15 @@ public class ExternalSystemApiUtil {
}
};
@NotNull private static final TransferToEDTQueue<Runnable> TRANSFER_TO_EDT_QUEUE =
new TransferToEDTQueue<Runnable>("External System queue", new Processor<Runnable>() {
@Override
public boolean process(Runnable runnable) {
runnable.run();
return true;
}
}, Condition.FALSE, 300);
private ExternalSystemApiUtil() {
}
@@ -382,6 +391,26 @@ public class ExternalSystemApiUtil {
}
}
/**
* Adds runnable to Event Dispatch Queue
* if we aren't in UnitTest of Headless environment mode
*
* @param runnable Runnable
*/
public static void addToInvokeLater(final Runnable runnable) {
final Application application = ApplicationManager.getApplication();
final boolean unitTestMode = application.isUnitTestMode();
if (unitTestMode) {
UIUtil.invokeLaterIfNeeded(runnable);
}
else if (application.isHeadlessEnvironment() || SwingUtilities.isEventDispatchThread()) {
runnable.run();
}
else {
TRANSFER_TO_EDT_QUEUE.offer(runnable);
}
}
/**
* Configures given classpath to reference target i18n bundle file(s).
*
@@ -19,6 +19,8 @@
<orderEntry type="module" module-name="execution-openapi" />
<orderEntry type="module" module-name="vcs-api" />
<orderEntry type="library" name="Groovy" level="project" />
<orderEntry type="module" module-name="testRunner" />
<orderEntry type="module" module-name="smRunner" />
</component>
</module>
@@ -0,0 +1,70 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.externalSystem.service.execution;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.filters.TextConsoleBuilderImpl;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTask;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;
/**
* @author Vladislav.Soroka
* @since 2/18/14
*/
public class DefaultExternalSystemExecutionConsoleManager implements ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration> {
private ProcessHandler myProcessHandler;
@NotNull
@Override
public ProjectSystemId getExternalSystemId() {
return ProjectSystemId.IDE;
}
@NotNull
@Override
public ExecutionConsole attachExecutionConsole(@NotNull ExternalSystemTask task,
@NotNull Project project,
@NotNull ExternalSystemRunConfiguration configuration,
@NotNull Executor executor,
@NotNull ExecutionEnvironment env,
@NotNull ProcessHandler processHandler) throws ExecutionException {
myProcessHandler = processHandler;
ConsoleView executionConsole = new TextConsoleBuilderImpl(project).getConsole();
executionConsole.attachToProcess(processHandler);
return executionConsole;
}
@Override
public void onOutput(@NotNull String text, @NotNull Key processOutputType) {
assert myProcessHandler != null;
myProcessHandler.notifyTextAvailable(text, processOutputType);
}
@Override
public boolean isApplicableFor(@NotNull ExternalSystemTask task) {
return true;
}
}
@@ -9,18 +9,19 @@ import com.intellij.execution.configurations.LocatableConfigurationBase;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.filters.TextConsoleBuilderImpl;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTask;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter;
import com.intellij.openapi.externalSystem.service.internal.ExternalSystemExecuteTaskTask;
@@ -29,6 +30,7 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
@@ -58,8 +60,7 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase {
public ExternalSystemRunConfiguration(@NotNull ProjectSystemId externalSystemId,
Project project,
ConfigurationFactory factory,
String name)
{
String name) {
super(project, factory, name);
mySettings.setExternalSystemIdString(externalSystemId.getId());
}
@@ -105,19 +106,27 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase {
@Nullable
@Override
public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment env) throws ExecutionException {
return new MyRunnableState(mySettings, getProject(), DefaultDebugExecutor.EXECUTOR_ID.equals(executor.getId()));
return new MyRunnableState(mySettings, getProject(), DefaultDebugExecutor.EXECUTOR_ID.equals(executor.getId()), this, env);
}
public static class MyRunnableState implements RunProfileState {
@NotNull private final ExternalSystemTaskExecutionSettings mySettings;
@NotNull private final Project myProject;
@NotNull private final ExternalSystemRunConfiguration myConfiguration;
@NotNull private final ExecutionEnvironment myEnv;
private final int myDebugPort;
public MyRunnableState(@NotNull ExternalSystemTaskExecutionSettings settings, @NotNull Project project, boolean debug) {
public MyRunnableState(@NotNull ExternalSystemTaskExecutionSettings settings,
@NotNull Project project,
boolean debug,
@NotNull ExternalSystemRunConfiguration configuration,
@NotNull ExecutionEnvironment env) {
mySettings = settings;
myProject = project;
myConfiguration = configuration;
myEnv = env;
int port;
if (debug) {
try {
@@ -141,10 +150,9 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase {
@Nullable
@Override
public ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
if(myProject.isDisposed()) return null;
if (myProject.isDisposed()) return null;
ExternalSystemUtil.updateRecentTasks(new ExternalTaskExecutionInfo(mySettings.clone(), executor.getId()), myProject);
ConsoleView console = new TextConsoleBuilderImpl(myProject).getConsole();
final List<ExternalTaskPojo> tasks = ContainerUtilRt.newArrayList();
for (String taskName : mySettings.getTaskNames()) {
tasks.add(new ExternalTaskPojo(taskName, mySettings.getExternalProjectPath(), null));
@@ -168,7 +176,11 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase {
debuggerSetup);
final MyProcessHandler processHandler = new MyProcessHandler(task);
console.attachToProcess(processHandler);
final ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration> consoleManager = getConsoleManagerFor(task);
final ExecutionConsole consoleView =
consoleManager.attachExecutionConsole(task, myProject, myConfiguration, executor, myEnv, processHandler);
Disposer.register(myProject, consoleView);
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
@@ -176,10 +188,12 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase {
final String startDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
final String greeting;
if (mySettings.getTaskNames().size() > 1) {
greeting = ExternalSystemBundle.message("run.text.starting.multiple.task", startDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
greeting = ExternalSystemBundle
.message("run.text.starting.multiple.task", startDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
}
else {
greeting = ExternalSystemBundle.message("run.text.starting.single.task", startDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
greeting =
ExternalSystemBundle.message("run.text.starting.single.task", startDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
}
processHandler.notifyTextAvailable(greeting, ProcessOutputTypes.SYSTEM);
task.execute(new ExternalSystemTaskNotificationListenerAdapter() {
@@ -192,7 +206,8 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase {
processHandler.notifyTextAvailable("\r", ProcessOutputTypes.SYSTEM);
myResetGreeting = false;
}
processHandler.notifyTextAvailable(text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
consoleManager.onOutput(text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
}
@Override
@@ -208,10 +223,12 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase {
final String endDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
final String farewell;
if (mySettings.getTaskNames().size() > 1) {
farewell = ExternalSystemBundle.message("run.text.ended.multiple.task", endDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
farewell = ExternalSystemBundle
.message("run.text.ended.multiple.task", endDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
}
else {
farewell = ExternalSystemBundle.message("run.text.ended.single.task", endDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
farewell =
ExternalSystemBundle.message("run.text.ended.single.task", endDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
}
processHandler.notifyTextAvailable(farewell, ProcessOutputTypes.SYSTEM);
processHandler.notifyProcessTerminated(0);
@@ -219,7 +236,7 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase {
});
}
});
return new DefaultExecutionResult(console, processHandler);
return new DefaultExecutionResult(consoleView, processHandler);
}
}
@@ -268,4 +285,16 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase {
super.notifyProcessTerminated(exitCode);
}
}
@NotNull
private static ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration> getConsoleManagerFor(@NotNull ExternalSystemTask task) {
for (ExternalSystemExecutionConsoleManager executionConsoleManager : ExternalSystemExecutionConsoleManager.EP_NAME.getExtensions()) {
if (executionConsoleManager.isApplicableFor(task))
//noinspection unchecked
return executionConsoleManager;
}
return new DefaultExternalSystemExecutionConsoleManager();
}
}
@@ -66,6 +66,11 @@ public class ExternalSystemExecuteTaskTask extends AbstractExternalSystemTask {
myDebuggerSetup = debuggerSetup;
}
@NotNull
public List<ExternalTaskPojo> getTasksToExecute() {
return myTasksToExecute;
}
@NotNull
private static String getLinkedExternalProjectPath(@NotNull Collection<ExternalTaskPojo> tasks) throws IllegalArgumentException {
if (tasks.isEmpty()) {
@@ -9,5 +9,7 @@
interface="com.intellij.openapi.externalSystem.service.notification.ExternalSystemNotificationExtension"/>
<extensionPoint name="externalSystemTaskNotificationListener"
interface="com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener"/>
<extensionPoint name="externalSystemExecutionConsoleManager"
interface="com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager"/>
</extensionPoints>
</idea-plugin>
+2
View File
@@ -115,6 +115,8 @@
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
<orderEntry type="module" module-name="testFramework" scope="TEST" />
<orderEntry type="module" module-name="testFramework-java" scope="TEST" />
<orderEntry type="module" module-name="junit" />
<orderEntry type="module" module-name="smRunner" />
</component>
</module>
@@ -33,4 +33,9 @@ gradle.codeInsight.action.apply_plugin.description=Add selected gradle plugin to
gradle.codeInsight.action.apply_plugin.popup.title=Choose plugin
gradle.codeInsight.action.add_maven_dependency.text=Add maven artifact dependency
gradle.codeInsight.action.add_maven_dependency.description=Add selected maven artifact dependency to the project
gradle.codeInsight.action.add_maven_dependency.description=Add selected maven artifact dependency to the project
gradle.test.runner.ui.tests.tree.presentation.labels.waiting.tests=Waiting for tests...
gradle.test.runner.ui.tests.tree.presentation.labels.no.tests.were.found=Test events were not received
gradle.test.runner.ui.tests.actions.open.gradle.report.text=Open Gradle test report
gradle.test.runner.ui.tests.actions.open.gradle.report.desc=Open Gradle reports produced by the Test task
+8
View File
@@ -69,6 +69,14 @@
<postStartupActivity implementation="org.jetbrains.plugins.gradle.service.project.GradleStartupActivity"/>
<errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
<testActionProvider implementation="org.jetbrains.plugins.gradle.execution.test.runner.OpenGradleTestResultActionProvider"/>
<runConfigurationProducer implementation="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer"
order="first"/>
<runConfigurationProducer implementation="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer"
order="first"/>
<runConfigurationProducer implementation="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer"
order="first"/>
<externalSystemExecutionConsoleManager implementation="org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsoleManager"/>
<projectImportProvider implementation="org.jetbrains.plugins.gradle.service.project.wizard.GradleProjectImportProvider"/>
<projectImportBuilder implementation="org.jetbrains.plugins.gradle.service.project.wizard.GradleProjectImportBuilder"/>
<moduleBuilder builderClass="org.jetbrains.plugins.gradle.service.project.wizard.GradleModuleBuilder"/>
@@ -0,0 +1,112 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner;
import com.intellij.execution.ExecutionBundle;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.actions.RunConfigurationProducer;
import com.intellij.execution.junit.JavaRuntimeConfigurationProducerBase;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiPackage;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.service.GradleInstallationManager;
import org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import java.io.File;
import java.util.List;
import java.util.regex.Matcher;
/**
* @author Vladislav.Soroka
* @since 2/14/14
*/
public class AllInPackageGradleConfigurationProducer extends RunConfigurationProducer<ExternalSystemRunConfiguration> {
private static final List<String> TASKS_TO_RUN = ContainerUtil.newArrayList("cleanTest", "test");
public AllInPackageGradleConfigurationProducer() {
super(GradleExternalTaskConfigurationType.getInstance());
}
@Override
protected boolean setupConfigurationFromContext(ExternalSystemRunConfiguration configuration,
ConfigurationContext context,
Ref<PsiElement> sourceElement) {
final PsiPackage psiPackage = JavaRuntimeConfigurationProducerBase.checkPackage(context.getPsiLocation());
if (psiPackage == null) return false;
sourceElement.set(psiPackage);
final Module module = context.getModule();
if (module == null) return false;
if (!StringUtil.equals(
module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY),
GradleConstants.SYSTEM_ID.toString())) {
return false;
}
final String linkedGradleProject = module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY);
if (linkedGradleProject == null) return false;
configuration.getSettings().setExternalProjectPath(linkedGradleProject);
configuration.getSettings().setTaskNames(TASKS_TO_RUN);
if (psiPackage.getQualifiedName().isEmpty()) {
configuration.getSettings().setScriptParameters("--tests *");
}
else {
configuration.getSettings()
.setScriptParameters(String.format("--tests %s.*", psiPackage.getQualifiedName()));
}
configuration.setName(suggestName(psiPackage, module));
return true;
}
@Override
public boolean isConfigurationFromContext(ExternalSystemRunConfiguration configuration, ConfigurationContext context) {
if (configuration == null) return false;
if (!GradleConstants.SYSTEM_ID.equals(configuration.getSettings().getExternalSystemId())) return false;
final PsiPackage psiPackage = JavaRuntimeConfigurationProducerBase.checkPackage(context.getPsiLocation());
if (psiPackage == null) return false;
if (context.getModule() == null) return false;
if (!StringUtil.equals(
context.getModule().getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY),
configuration.getSettings().getExternalProjectPath())) {
return false;
}
if (!configuration.getSettings().getTaskNames().containsAll(TASKS_TO_RUN)) return false;
final String scriptParameters = configuration.getSettings().getScriptParameters() + ' ';
return psiPackage.getQualifiedName().isEmpty()
? scriptParameters.contains("--tests * ")
: scriptParameters.contains(String.format("--tests %s.* ", psiPackage.getQualifiedName()));
}
private static String suggestName(@NotNull PsiPackage aPackage, @NotNull Module module) {
return aPackage.getQualifiedName().isEmpty()
? ExecutionBundle.message("test.in.scope.presentable.text", module.getName())
: ExecutionBundle.message("test.in.scope.presentable.text", aPackage.getQualifiedName());
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner;
import com.intellij.execution.Executor;
import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
import org.jetbrains.annotations.Nullable;
import java.io.File;
/**
* @author Vladislav.Soroka
* @since 2/17/14
*/
public class GradleConsoleProperties extends SMTRunnerConsoleProperties {
@Nullable private File gradleTestReport;
public GradleConsoleProperties(final ExternalSystemRunConfiguration configuration, Executor executor) {
super(configuration, configuration.getSettings().getExternalSystemId().getReadableName(), executor);
}
public void setGradleTestReport(@Nullable File gradleTestReport) {
this.gradleTestReport = gradleTestReport;
}
@Nullable
public File getGradleTestReport() {
return gradleTestReport;
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner;
import com.intellij.execution.Location;
import com.intellij.execution.stacktrace.StackTraceLine;
import com.intellij.execution.testframework.sm.runner.SMTestProxy;
import com.intellij.openapi.diff.LineTokenizer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Vladislav.Soroka
* @since 2/25/14
*/
public class GradleSMTestProxy extends SMTestProxy {
@Nullable private final String myClassName;
@Nullable private String myStacktrace;
public GradleSMTestProxy(String testName, boolean isSuite, @Nullable String locationUrl, @Nullable String className) {
super(testName, isSuite, locationUrl);
myClassName = className;
}
@Override
public void setTestFailed(@NotNull String localizedMessage, @Nullable String stackTrace, boolean testError) {
setStacktraceIfNotSet(stackTrace);
super.setTestFailed(localizedMessage, stackTrace, testError);
}
@Override
public void setTestComparisonFailed(@NotNull String localizedMessage,
@Nullable String stackTrace,
@NotNull String actualText,
@NotNull String expectedText) {
setStacktraceIfNotSet(stackTrace);
super.setTestComparisonFailed(localizedMessage, stackTrace, actualText, expectedText);
}
@Override
public void setTestIgnored(@Nullable String ignoreComment, @Nullable String stackTrace) {
setStacktraceIfNotSet(stackTrace);
super.setTestIgnored(ignoreComment, stackTrace);
}
@Nullable
@Override
public Location getLocation(Project project, GlobalSearchScope searchScope) {
if (getLocationUrl() != null) {
if (isDefect() && myStacktrace != null) {
final String[] stackTrace = new LineTokenizer(myStacktrace).execute();
for (String aStackTrace : stackTrace) {
final StackTraceLine line = new StackTraceLine(project, aStackTrace);
if (getName().equals(line.getMethodName()) && StringUtil.equals(myClassName, line.getClassName())) {
return line.getMethodLocation(project);
}
}
}
}
return super.getLocation(project, searchScope);
}
private void setStacktraceIfNotSet(@Nullable String stacktrace) {
if (myStacktrace == null) myStacktrace = stacktrace;
}
}
@@ -0,0 +1,196 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil;
import com.intellij.execution.testframework.sm.runner.SMTestProxy;
import com.intellij.execution.testframework.sm.runner.states.TestStateInfo;
import com.intellij.execution.testframework.sm.runner.ui.SMRootTestProxyFormatter;
import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView;
import com.intellij.execution.testframework.sm.runner.ui.TestTreeRenderer;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTask;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
import com.intellij.openapi.externalSystem.service.internal.ExternalSystemExecuteTaskTask;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.util.XmlXpathHelper;
import org.jetbrains.plugins.gradle.execution.test.runner.events.*;
import org.jetbrains.plugins.gradle.util.GradleBundle;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import java.util.Map;
/**
* @author Vladislav.Soroka
* @since 2/18/14
*/
public class GradleTestsExecutionConsoleManager implements ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration> {
private static final Logger LOG = Logger.getInstance(GradleTestsExecutionConsoleManager.class);
private Map<String, SMTestProxy> testsMap = ContainerUtil.newHashMap();
private StringBuilder myBuffer = new StringBuilder();
private final GradleUrlProvider myUrlProvider = new GradleUrlProvider();
private SMTRunnerConsoleView myExecutionConsole;
@NotNull
@Override
public ProjectSystemId getExternalSystemId() {
return GradleConstants.SYSTEM_ID;
}
public Map<String, SMTestProxy> getTestsMap() {
return testsMap;
}
public GradleUrlProvider getUrlProvider() {
return myUrlProvider;
}
public SMTRunnerConsoleView getExecutionConsole() {
return myExecutionConsole;
}
@NotNull
@Override
public ExecutionConsole attachExecutionConsole(@NotNull final ExternalSystemTask task,
@NotNull final Project project,
@NotNull final ExternalSystemRunConfiguration configuration,
@NotNull final Executor executor,
@NotNull final ExecutionEnvironment env,
@NotNull final ProcessHandler processHandler) throws ExecutionException {
final GradleConsoleProperties properties = new GradleConsoleProperties(configuration, executor);
myExecutionConsole = (SMTRunnerConsoleView)SMTestRunnerConnectionUtil.createAndAttachConsole(
configuration.getSettings().getExternalSystemId().getReadableName(), processHandler, properties, env);
TestTreeRenderer originalRenderer =
ObjectUtils.tryCast(myExecutionConsole.getResultsViewer().getTreeView().getCellRenderer(), TestTreeRenderer.class);
if (originalRenderer != null) {
originalRenderer.setAdditionalRootFormatter(new SMRootTestProxyFormatter() {
@Override
public void format(@NotNull SMTestProxy.SMRootTestProxy testProxy, @NotNull TestTreeRenderer renderer) {
final TestStateInfo.Magnitude magnitude = testProxy.getMagnitudeInfo();
if (magnitude == TestStateInfo.Magnitude.RUNNING_INDEX) {
renderer.clear();
renderer.append(GradleBundle.message(
"gradle.test.runner.ui.tests.tree.presentation.labels.waiting.tests"),
SimpleTextAttributes.REGULAR_ATTRIBUTES
);
}
else if (!testProxy.isInProgress() && testProxy.isEmptySuite()) {
renderer.clear();
renderer.append(GradleBundle.message(
"gradle.test.runner.ui.tests.tree.presentation.labels.no.tests.were.found"),
SimpleTextAttributes.REGULAR_ATTRIBUTES
);
}
}
});
}
return myExecutionConsole;
}
@Override
public void onOutput(@NotNull String text, @NotNull Key processOutputType) {
if (StringUtil.endsWith(text, "<ijLogEol/>\n")) {
myBuffer.append(StringUtil.trimEnd(text, "<ijLogEol/>\n")).append('\n');
return;
}
else {
myBuffer.append(text);
}
String trimmedText = myBuffer.toString().trim();
myBuffer.setLength(0);
if (!StringUtil.startsWith(trimmedText, "<ijLog>") || !StringUtil.endsWith(trimmedText, "</ijLog>")) {
if (text.trim().isEmpty()) return;
myExecutionConsole.print(text, ConsoleViewContentType.getConsoleViewType(processOutputType));
return;
}
try {
final XmlXpathHelper xml = new XmlXpathHelper(trimmedText);
final TestEventType eventType = TestEventType.fromValue(xml.queryXml("/ijLog/event/@type"));
TestEvent testEvent = null;
switch (eventType) {
case CONFIGURATION_ERROR:
testEvent = new ConfigurationErrorEvent(this);
break;
case REPORT_LOCATION:
testEvent = new ReportLocationEvent(this);
break;
case BEFORE_TEST:
testEvent = new BeforeTestEvent(this);
break;
case ON_OUTPUT:
testEvent = new OnOutputEvent(this);
break;
case AFTER_TEST:
testEvent = new AfterTestEvent(this);
break;
case BEFORE_SUITE:
testEvent = new BeforeSuiteEvent(this);
break;
case AFTER_SUITE:
testEvent = new AfterSuiteEvent(this);
break;
case UNKNOWN_EVENT:
break;
}
if (testEvent != null) {
testEvent.process(xml);
}
}
catch (XmlXpathHelper.XmlParserException e) {
LOG.error("Gradle test events parser error", e);
}
}
@Override
public boolean isApplicableFor(@NotNull ExternalSystemTask task) {
if (task instanceof ExternalSystemExecuteTaskTask) {
ExternalSystemExecuteTaskTask taskTask = (ExternalSystemExecuteTaskTask)task;
if (!StringUtil.equals(taskTask.getExternalSystemId().getId(), GradleConstants.SYSTEM_ID.getId())) return false;
return ContainerUtil.find(taskTask.getTasksToExecute(), new Condition<ExternalTaskPojo>() {
@Override
public boolean value(ExternalTaskPojo pojo) {
return "test".equals(pojo.getName());
}
}) != null;
}
return false;
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner;
import com.intellij.execution.Location;
import com.intellij.execution.PsiLocation;
import com.intellij.openapi.project.Project;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.testIntegration.TestLocationProvider;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Vladislav.Soroka
* @since 2/24/14
*/
public class GradleUrlProvider implements TestLocationProvider {
public static final String PROTOCOL_ID = "gradle";
public static final String METHOD_PREF = "methodName";
public static final String CLASS_PREF = "className";
@NotNull
public List<Location> getLocation(@NotNull String protocolId, @NotNull String locationData, Project project) {
if (!PROTOCOL_ID.equals(protocolId)) return Collections.emptyList();
final String className = extractFullClassName(locationData);
if (className == null) return Collections.emptyList();
final PsiClass testClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));
if (testClass == null) return Collections.emptyList();
final String methodName = extractMethodName(locationData);
if (methodName == null) {
return Collections.<Location>singletonList(new PsiLocation<PsiClass>(project, testClass));
}
final PsiMethod[] methods = testClass.findMethodsByName(methodName, true);
List<Location> list = new ArrayList<Location>(methods.length);
for (PsiMethod method : methods) {
list.add(new PsiLocation<PsiMethod>(project, method));
}
return list;
}
@Nullable
private static String extractFullClassName(String locationData) {
final int i = locationData.indexOf("::");
final String pref = locationData.substring(0, i);
final String qualifiedName = locationData.substring(i + 2);
if (METHOD_PREF.equals(pref)) {
final int dot = qualifiedName.lastIndexOf('.');
return qualifiedName.substring(0, dot);
}
else if (CLASS_PREF.equals(pref)) {
return qualifiedName;
}
return null;
}
@Nullable
private static String extractMethodName(String locationData) {
final int i = locationData.indexOf("::");
final String pref = locationData.substring(0, i);
final String qualifiedName = locationData.substring(i + 2);
if (METHOD_PREF.equals(pref)) {
final int dot = qualifiedName.lastIndexOf('.');
return qualifiedName.substring(dot+1);
}
return null;
}
}
@@ -0,0 +1,80 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner;
import com.intellij.execution.testframework.TestConsoleProperties;
import com.intellij.execution.testframework.TestFrameworkRunningModel;
import com.intellij.execution.testframework.ToggleModelAction;
import com.intellij.execution.testframework.ToggleModelActionProvider;
import com.intellij.ide.BrowserUtil;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.util.config.AbstractProperty;
import com.intellij.util.config.BooleanProperty;
import icons.GradleIcons;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.util.GradleBundle;
import java.io.File;
/**
* @author Vladislav.Soroka
* @since 2/26/14
*/
public class OpenGradleTestResultActionProvider implements ToggleModelActionProvider {
public static final BooleanProperty OPEN_GRADLE_REPORT = new BooleanProperty("openGradleReport", false);
public ToggleModelAction createToggleModelAction(TestConsoleProperties properties) {
return new MyToggleModelAction(properties);
}
private static class MyToggleModelAction extends ToggleModelAction {
public MyToggleModelAction(TestConsoleProperties properties) {
super(GradleBundle.message("gradle.test.runner.ui.tests.actions.open.gradle.report.text"),
GradleBundle.message("gradle.test.runner.ui.tests.actions.open.gradle.report.desc"),
GradleIcons.GradleNavigate, properties, OPEN_GRADLE_REPORT);
}
@Override
public void setModel(TestFrameworkRunningModel model) {
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
final String reportFilePath = getReportFilePath();
if (reportFilePath != null) {
BrowserUtil.browse(reportFilePath);
}
}
@Override
protected boolean isEnabled() {
final String reportFilePath = getReportFilePath();
return reportFilePath != null;
}
@Nullable
private String getReportFilePath() {
final AbstractProperty.AbstractPropertyContainer properties = getProperties();
if (properties instanceof GradleConsoleProperties) {
GradleConsoleProperties gradleConsoleProperties = (GradleConsoleProperties)properties;
final File testReport = gradleConsoleProperties.getGradleTestReport();
if (testReport != null && testReport.isFile()) return testReport.getPath();
}
return null;
}
}
}
@@ -0,0 +1,188 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner;
import com.intellij.execution.JavaExecutionUtil;
import com.intellij.execution.JavaRunConfigurationExtensionManager;
import com.intellij.execution.Location;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.actions.ConfigurationFromContext;
import com.intellij.execution.actions.RunConfigurationProducer;
import com.intellij.execution.junit.InheritorChooser;
import com.intellij.execution.junit.JUnitUtil;
import com.intellij.execution.junit.PatternConfigurationProducer;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import java.util.List;
import static org.jetbrains.plugins.gradle.execution.test.runner.TestRunnerUtils.getMethodLocation;
/**
* @author Vladislav.Soroka
* @since 2/14/14
*/
public class TestClassGradleConfigurationProducer extends RunConfigurationProducer<ExternalSystemRunConfiguration> {
private static final List<String> TASKS_TO_RUN = ContainerUtil.newArrayList("cleanTest", "test");
public TestClassGradleConfigurationProducer() {
super(GradleExternalTaskConfigurationType.getInstance());
}
@Override
protected boolean setupConfigurationFromContext(ExternalSystemRunConfiguration configuration,
ConfigurationContext context,
Ref<PsiElement> sourceElement) {
final Location contextLocation = context.getLocation();
assert contextLocation != null;
final Location location = JavaExecutionUtil.stepIntoSingleClass(contextLocation);
if (location == null) return false;
if (PatternConfigurationProducer.isMultipleElementsSelected(context)) {
return false;
}
PsiClass testClass = JUnitUtil.getTestClass(location);
if (testClass == null) return false;
sourceElement.set(testClass);
if (context.getModule() == null) return false;
if (!StringUtil.equals(
context.getModule().getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY),
GradleConstants.SYSTEM_ID.toString())) {
return false;
}
configuration.getSettings().setExternalProjectPath(context.getModule().getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY));
configuration.getSettings().setTaskNames(TASKS_TO_RUN);
configuration.getSettings()
.setScriptParameters(String.format("--tests %s", testClass.getQualifiedName()));
configuration.setName(testClass.getName());
JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, contextLocation);
return true;
}
@Override
public boolean isConfigurationFromContext(ExternalSystemRunConfiguration configuration, ConfigurationContext context) {
if (configuration == null) return false;
if (!GradleConstants.SYSTEM_ID.equals(configuration.getSettings().getExternalSystemId())) return false;
final Location contextLocation = context.getLocation();
assert contextLocation != null;
final Location location = JavaExecutionUtil.stepIntoSingleClass(contextLocation);
if (location == null) return false;
if (PatternConfigurationProducer.isMultipleElementsSelected(context)) {
return false;
}
Location<PsiMethod> methodLocation = getMethodLocation(contextLocation);
if (methodLocation != null) return false;
PsiClass testClass = JUnitUtil.getTestClass(location);
if (testClass == null) return false;
if (context.getModule() == null) return false;
if (!StringUtil.equals(
context.getModule().getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY),
configuration.getSettings().getExternalProjectPath())) {
return false;
}
if (!configuration.getSettings().getTaskNames().containsAll(TASKS_TO_RUN)) return false;
final String scriptParameters = configuration.getSettings().getScriptParameters() + ' ';
return scriptParameters.contains(String.format("--tests %s ", testClass.getQualifiedName()));
}
@Override
public void onFirstRun(final ConfigurationFromContext fromContext, final ConfigurationContext context, final Runnable performRunnable) {
final PsiClass psiClass = (PsiClass)fromContext.getSourceElement();
final InheritorChooser inheritorChooser = new InheritorChooser() {
@Override
protected void runForClasses(List<PsiClass> classes, PsiMethod method, ConfigurationContext context, Runnable performRunnable) {
if (!StringUtil.equals(
context.getModule().getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY),
GradleConstants.SYSTEM_ID.toString())) {
return;
}
ExternalSystemRunConfiguration configuration = (ExternalSystemRunConfiguration)fromContext.getConfiguration();
if (!applyTestConfiguration(configuration, context, ArrayUtil.toObjectArray(classes, PsiClass.class))) return;
super.runForClasses(classes, method, context, performRunnable);
}
@Override
protected void runForClass(PsiClass aClass,
PsiMethod psiMethod,
ConfigurationContext context,
Runnable performRunnable) {
if (!StringUtil.equals(
context.getModule().getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY),
GradleConstants.SYSTEM_ID.toString())) {
return;
}
ExternalSystemRunConfiguration configuration = (ExternalSystemRunConfiguration)fromContext.getConfiguration();
if (!applyTestConfiguration(configuration, context, aClass)) return;
super.runForClass(aClass, psiMethod, context, performRunnable);
}
};
if (inheritorChooser.runMethodInAbstractClass(context, performRunnable, null, (PsiClass)fromContext.getSourceElement())) return;
super.onFirstRun(fromContext, context, performRunnable);
}
private static boolean applyTestConfiguration(@NotNull ExternalSystemRunConfiguration configuration,
@NotNull ConfigurationContext context,
@NotNull PsiClass... containingClasses) {
if (!StringUtil.equals(
context.getModule().getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY),
GradleConstants.SYSTEM_ID.toString())) {
return false;
}
configuration.getSettings().setExternalProjectPath(context.getModule().getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY));
configuration.getSettings().setTaskNames(TASKS_TO_RUN);
StringBuilder buf = new StringBuilder();
for (PsiClass aClass : containingClasses) {
buf.append(String.format("--tests %s ", aClass.getQualifiedName()));
}
configuration.getSettings().setScriptParameters(buf.toString());
configuration.setName(StringUtil.join(containingClasses, new Function<PsiClass, String>() {
@Override
public String fun(PsiClass aClass) {
return aClass.getName();
}
},"|"));
return true;
}
}
@@ -0,0 +1,173 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner;
import com.intellij.execution.JavaRunConfigurationExtensionManager;
import com.intellij.execution.Location;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.actions.ConfigurationFromContext;
import com.intellij.execution.actions.RunConfigurationProducer;
import com.intellij.execution.junit.InheritorChooser;
import com.intellij.execution.junit.PatternConfigurationProducer;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import java.util.List;
import static org.jetbrains.plugins.gradle.execution.test.runner.TestRunnerUtils.getMethodLocation;
/**
* @author Vladislav.Soroka
* @since 2/14/14
*/
public class TestMethodGradleConfigurationProducer extends RunConfigurationProducer<ExternalSystemRunConfiguration> {
private static final List<String> TASKS_TO_RUN = ContainerUtil.newArrayList("cleanTest", "test");
public TestMethodGradleConfigurationProducer() {
super(GradleExternalTaskConfigurationType.getInstance());
}
@Override
protected boolean setupConfigurationFromContext(ExternalSystemRunConfiguration configuration,
ConfigurationContext context,
Ref<PsiElement> sourceElement) {
if (PatternConfigurationProducer.isMultipleElementsSelected(context)) {
return false;
}
final Location contextLocation = context.getLocation();
assert contextLocation != null;
Location<PsiMethod> methodLocation = getMethodLocation(contextLocation);
if (methodLocation == null) return false;
final PsiMethod psiMethod = methodLocation.getPsiElement();
sourceElement.set(psiMethod);
final PsiClass containingClass = psiMethod.getContainingClass();
if (containingClass == null) return false;
if (context.getModule() == null) return false;
if (!applyTestMethodConfiguration(configuration, context, psiMethod, containingClass)) return false;
JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, contextLocation);
return true;
}
@Override
public boolean isConfigurationFromContext(ExternalSystemRunConfiguration configuration, ConfigurationContext context) {
if (configuration == null) return false;
if (!GradleConstants.SYSTEM_ID.equals(configuration.getSettings().getExternalSystemId())) return false;
if (PatternConfigurationProducer.isMultipleElementsSelected(context)) {
return false;
}
final Location contextLocation = context.getLocation();
assert contextLocation != null;
Location<PsiMethod> methodLocation = getMethodLocation(contextLocation);
if (methodLocation == null) return false;
final PsiMethod psiMethod = methodLocation.getPsiElement();
final PsiClass containingClass = psiMethod.getContainingClass();
if (containingClass == null) return false;
if (context.getModule() == null) return false;
if (!StringUtil.equals(
context.getModule().getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY),
configuration.getSettings().getExternalProjectPath())) {
return false;
}
if (!configuration.getSettings().getTaskNames().containsAll(TASKS_TO_RUN)) return false;
final String scriptParameters = configuration.getSettings().getScriptParameters() + ' ';
return scriptParameters.contains(String.format("--tests %s.%s ", containingClass.getQualifiedName(), psiMethod.getName()));
}
@Override
public void onFirstRun(final ConfigurationFromContext fromContext, final ConfigurationContext context, final Runnable performRunnable) {
final PsiMethod psiMethod = (PsiMethod)fromContext.getSourceElement();
final PsiClass containingClass = psiMethod.getContainingClass();
final InheritorChooser inheritorChooser = new InheritorChooser() {
@Override
protected void runForClasses(List<PsiClass> classes, PsiMethod method, ConfigurationContext context, Runnable performRunnable) {
if (!StringUtil.equals(
context.getModule().getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY),
GradleConstants.SYSTEM_ID.toString())) {
return;
}
ExternalSystemRunConfiguration configuration = (ExternalSystemRunConfiguration)fromContext.getConfiguration();
if (!applyTestMethodConfiguration(configuration, context, psiMethod, ArrayUtil.toObjectArray(classes, PsiClass.class))) return;
super.runForClasses(classes, method, context, performRunnable);
}
@Override
protected void runForClass(PsiClass aClass,
PsiMethod psiMethod,
ConfigurationContext context,
Runnable performRunnable) {
if (!StringUtil.equals(
context.getModule().getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY),
GradleConstants.SYSTEM_ID.toString())) {
return;
}
ExternalSystemRunConfiguration configuration = (ExternalSystemRunConfiguration)fromContext.getConfiguration();
if (!applyTestMethodConfiguration(configuration, context, psiMethod, aClass)) return;
super.runForClass(aClass, psiMethod, context, performRunnable);
}
};
if (inheritorChooser.runMethodInAbstractClass(context, performRunnable, psiMethod, containingClass)) return;
super.onFirstRun(fromContext, context, performRunnable);
}
private static boolean applyTestMethodConfiguration(@NotNull ExternalSystemRunConfiguration configuration,
@NotNull ConfigurationContext context,
@NotNull PsiMethod psiMethod,
@NotNull PsiClass... containingClasses) {
if (!StringUtil.equals(
context.getModule().getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY),
GradleConstants.SYSTEM_ID.toString())) {
return false;
}
configuration.getSettings().setExternalProjectPath(context.getModule().getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY));
configuration.getSettings().setTaskNames(TASKS_TO_RUN);
StringBuilder buf = new StringBuilder();
for (PsiClass aClass : containingClasses) {
buf.append(String.format("--tests %s.%s ", aClass.getQualifiedName(), psiMethod.getName()));
}
configuration.getSettings().setScriptParameters(buf.toString());
configuration.setName(psiMethod.getName());
return true;
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner;
import com.intellij.execution.Location;
import com.intellij.execution.junit.JUnitUtil;
import com.intellij.execution.junit2.PsiMemberParameterizedLocation;
import com.intellij.execution.junit2.info.MethodLocation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
/**
* @author Vladislav.Soroka
* @since 2/24/14
*/
public class TestRunnerUtils {
@Nullable
static Location<PsiMethod> getMethodLocation(@NotNull Location contextLocation) {
Location<PsiMethod> methodLocation = getTestMethod(contextLocation);
if (methodLocation == null) return null;
if (contextLocation instanceof PsiMemberParameterizedLocation) {
PsiClass containingClass = ((PsiMemberParameterizedLocation)contextLocation).getContainingClass();
if (containingClass != null) {
methodLocation = MethodLocation.elementInClass(methodLocation.getPsiElement(), containingClass);
}
}
return methodLocation;
}
@Nullable
static Location<PsiMethod> getTestMethod(final Location<?> location) {
for (Iterator<Location<PsiMethod>> iterator = location.getAncestors(PsiMethod.class, false); iterator.hasNext(); ) {
final Location<PsiMethod> methodLocation = iterator.next();
if (JUnitUtil.isTestMethod(methodLocation, false)) return methodLocation;
}
return null;
}
}
@@ -0,0 +1,82 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner.events;
import com.intellij.execution.testframework.sm.runner.ui.SMTestRunnerResultsForm;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.util.XmlXpathHelper;
import org.jetbrains.plugins.gradle.execution.test.runner.GradleConsoleProperties;
import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsoleManager;
/**
* @author Vladislav.Soroka
* @since 2/28/14
*/
public abstract class AbstractTestEvent implements TestEvent {
private final GradleTestsExecutionConsoleManager myConsoleManager;
public AbstractTestEvent(GradleTestsExecutionConsoleManager consoleManager) {
this.myConsoleManager = consoleManager;
}
public GradleTestsExecutionConsoleManager getConsoleManager() {
return myConsoleManager;
}
protected SMTestRunnerResultsForm getResultsViewer() {
return myConsoleManager.getExecutionConsole().getResultsViewer();
}
protected Project getProject() {
return myConsoleManager.getExecutionConsole().getProperties().getProject();
}
protected GradleConsoleProperties getProperties() {
return (GradleConsoleProperties)getConsoleManager().getExecutionConsole().getProperties();
}
@Nullable
protected String findLocationUrl(@Nullable String name, @NotNull String fqClassName) {
return name == null ? "gradle://className::" + fqClassName : "gradle://methodName::" + fqClassName + '.' + name;
}
protected String getTestName(@NotNull XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException {
return eventXml.queryXml("/ijLog/event/test/descriptor/@name");
}
protected String getParentTestId(@NotNull XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException {
return eventXml.queryXml("/ijLog/event/test/@parentId");
}
protected String getTestId(@NotNull XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException {
return eventXml.queryXml("/ijLog/event/test/@id");
}
protected String getTestClassName(@NotNull XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException {
return eventXml.queryXml("/ijLog/event/test/descriptor/@className");
}
protected TestEventResult getTestEventResultType(@NotNull XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException {
return TestEventResult.fromValue(eventXml.queryXml("/ijLog/event/test/result/@resultType"));
}
protected void addToInvokeLater(final Runnable runnable) {
ExternalSystemApiUtil.addToInvokeLater(runnable);
}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner.events;
import com.intellij.execution.testframework.sm.runner.SMTestProxy;
import org.jetbrains.plugins.gradle.util.XmlXpathHelper;
import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsoleManager;
/**
* @author Vladislav.Soroka
* @since 2/28/14
*/
public class AfterSuiteEvent extends AbstractTestEvent {
public AfterSuiteEvent(GradleTestsExecutionConsoleManager consoleManager) {
super(consoleManager);
}
@Override
public void process(XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException {
final String testId = getTestId(eventXml);
final SMTestProxy testProxy = getConsoleManager().getTestsMap().get(testId);
if (testProxy == null) return;
final TestEventResult result = getTestEventResultType(eventXml);
switch (result) {
case SUCCESS:
testProxy.setFinished();
break;
case FAILURE:
testProxy.setTestFailed("", null, false);
break;
case SKIPPED:
testProxy.setTestIgnored(null, null);
break;
case UNKNOWN_RESULT:
break;
}
if (testProxy.isEmptySuite() && !(testProxy instanceof SMTestProxy.SMRootTestProxy)) {
testProxy.getParent().getChildren().remove(testProxy);
}
addToInvokeLater(new Runnable() {
@Override
public void run() {
getResultsViewer().onSuiteFinished(testProxy);
}
});
}
}
@@ -0,0 +1,126 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner.events;
import com.intellij.execution.testframework.sm.runner.SMTestProxy;
import com.intellij.openapi.util.Pair;
import org.jetbrains.plugins.gradle.util.XmlXpathHelper;
import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsoleManager;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Vladislav.Soroka
* @since 2/28/14
*/
public class AfterTestEvent extends AbstractTestEvent {
public AfterTestEvent(GradleTestsExecutionConsoleManager consoleManager) {
super(consoleManager);
}
@Override
public void process(XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException {
final String testId = getTestId(eventXml);
final String startTime = eventXml.queryXml("/ijLog/event/test/result/@startTime");
final String endTime = eventXml.queryXml("/ijLog/event/test/result/@endTime");
final String exceptionMsg = eventXml.queryXml("/ijLog/event/test/result/errorMsg");
final String stackTrace = eventXml.queryXml("/ijLog/event/test/result/stackTrace");
final SMTestProxy testProxy = getConsoleManager().getTestsMap().get(testId);
if (testProxy == null) return;
try {
testProxy.setDuration(Long.valueOf(endTime) - Long.valueOf(startTime));
}
catch (NumberFormatException ignored) {
}
final TestEventResult result = getTestEventResultType(eventXml);
switch (result) {
case SUCCESS:
testProxy.setFinished();
addToInvokeLater(new Runnable() {
@Override
public void run() {
getResultsViewer().onTestFinished(testProxy);
}
});
break;
case FAILURE:
final String failureType = eventXml.queryXml("/ijLog/event/test/result/failureType");
if ("comparison".equals(failureType)) {
String actualText = eventXml.queryXml("/ijLog/event/test/result/actual");
String expectedText = eventXml.queryXml("/ijLog/event/test/result/expected");
testProxy.setTestComparisonFailed(exceptionMsg, stackTrace, actualText, expectedText);
}
else {
Pair<String/*expected*/, String/*actual*/> comparisonPair =
parseComparisonMessage(exceptionMsg, "\nExpected: is \"(.*)\"\n\\s*got: \"(.*)\"\n");
if (comparisonPair == null) {
comparisonPair = parseComparisonMessage(exceptionMsg, "\nExpected: is \"(.*)\"\n\\s*but: was \"(.*)\"");
}
if (comparisonPair == null) {
comparisonPair = parseComparisonMessage(exceptionMsg, "\nExpected: (.*)\n\\s*got: (.*)");
}
if (comparisonPair == null) {
comparisonPair = parseComparisonMessage(exceptionMsg, "\\s*expected same:<(.*)> was not:<(.*)>");
}
if (comparisonPair == null) {
comparisonPair = parseComparisonMessage(exceptionMsg, ".*\\s*expected:<(.*)> but was:<(.*)>");
}
if (comparisonPair == null) {
comparisonPair = parseComparisonMessage(exceptionMsg, "\nExpected: \"(.*)\"\n\\s*but: was \"(.*)\"");
}
if (comparisonPair != null) {
testProxy.setTestComparisonFailed(exceptionMsg, stackTrace, comparisonPair.second, comparisonPair.first);
}
else {
testProxy.setTestFailed(exceptionMsg, stackTrace, "error".equals(failureType));
}
}
addToInvokeLater(new Runnable() {
@Override
public void run() {
getResultsViewer().onTestFailed(testProxy);
}
});
break;
case SKIPPED:
testProxy.setTestIgnored(null, null);
addToInvokeLater(new Runnable() {
@Override
public void run() {
getResultsViewer().onTestIgnored(testProxy);
}
});
break;
case UNKNOWN_RESULT:
break;
}
}
private static Pair<String/*expected*/, String/*actual*/> parseComparisonMessage(String message, final String regex) {
final Matcher matcher = Pattern.compile(regex, Pattern.DOTALL | Pattern.CASE_INSENSITIVE).matcher(message);
if (matcher.matches()) {
return Pair.create(matcher.group(1).replaceAll("\\\\n", "\n"), matcher.group(2).replaceAll("\\\\n", "\n"));
}
return null;
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner.events;
import com.intellij.execution.testframework.sm.runner.SMTestProxy;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.plugins.gradle.util.XmlXpathHelper;
import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsoleManager;
/**
* @author Vladislav.Soroka
* @since 2/28/14
*/
public class BeforeSuiteEvent extends AbstractTestEvent {
public BeforeSuiteEvent(GradleTestsExecutionConsoleManager consoleManager) {
super(consoleManager);
}
@Override
public void process(XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException {
final String testId = getTestId(eventXml);
final String parentTestId = getParentTestId(eventXml);
final String name = getTestName(eventXml);
final String fqClassName = getTestClassName(eventXml);
if (StringUtil.isEmpty(parentTestId)) {
getConsoleManager().getTestsMap().put(testId, getResultsViewer().getTestsRootNode());
}
else {
String locationUrl = findLocationUrl(null, fqClassName);
final SMTestProxy testProxy = new SMTestProxy(name, true, locationUrl);
testProxy.setLocator(getConsoleManager().getUrlProvider());
testProxy.setStarted();
getConsoleManager().getTestsMap().put(testId, testProxy);
final SMTestProxy parentTestProxy = getConsoleManager().getTestsMap().get(parentTestId);
if (parentTestProxy != null) {
parentTestProxy.addChild(testProxy);
addToInvokeLater(new Runnable() {
@Override
public void run() {
getResultsViewer().onSuiteStarted(testProxy);
}
});
}
}
}
}
@@ -0,0 +1,65 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner.events;
import com.intellij.execution.testframework.sm.runner.SMTestProxy;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.plugins.gradle.util.XmlXpathHelper;
import org.jetbrains.plugins.gradle.execution.test.runner.GradleSMTestProxy;
import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsoleManager;
/**
* @author Vladislav.Soroka
* @since 2/28/14
*/
public class BeforeTestEvent extends AbstractTestEvent {
public BeforeTestEvent(GradleTestsExecutionConsoleManager consoleManager) {
super(consoleManager);
}
@Override
public void process(XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException {
final String testId = getTestId(eventXml);
final String parentTestId = getParentTestId(eventXml);
final String name = getTestName(eventXml);
final String fqClassName = getTestClassName(eventXml);
String locationUrl = findLocationUrl(name, fqClassName);
final GradleSMTestProxy testProxy = new GradleSMTestProxy(name, false, locationUrl, fqClassName);
testProxy.setStarted();
testProxy.setLocator(getConsoleManager().getUrlProvider());
getConsoleManager().getTestsMap().put(testId, testProxy);
if (StringUtil.isEmpty(parentTestId)) {
getResultsViewer().getTestsRootNode().addChild(testProxy);
}
else {
final SMTestProxy parentTestProxy = getConsoleManager().getTestsMap().get(parentTestId);
if (parentTestProxy != null) {
parentTestProxy.addChild(testProxy);
}
}
addToInvokeLater(new Runnable() {
@Override
public void run() {
getResultsViewer().onTestStarted(testProxy);
}
});
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner.events;
import com.intellij.ide.BrowserUtil;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationListener;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.externalSystem.ExternalSystemManager;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.GradleManager;
import org.jetbrains.plugins.gradle.util.XmlXpathHelper;
import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsoleManager;
import org.jetbrains.plugins.gradle.service.project.GradleNotification;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import javax.swing.event.HyperlinkEvent;
/**
* @author Vladislav.Soroka
* @since 2/28/14
*/
public class ConfigurationErrorEvent extends AbstractTestEvent {
public ConfigurationErrorEvent(GradleTestsExecutionConsoleManager consoleManager) {
super(consoleManager);
}
@Override
public void process(XmlXpathHelper xml) throws XmlXpathHelper.XmlParserException {
final String errorTitle = xml.queryXml("/ijLog/event/title");
assert errorTitle != null;
final String configurationErrorMsg = xml.queryXml("/ijLog/event/message");
assert configurationErrorMsg != null;
final boolean openSettings = Boolean.valueOf(xml.queryXml("/ijLog/event/@openSettings"));
final Project project = getProject();
assert project != null;
final String message =
openSettings ? String.format("<br>\n%s<br><br>\n\n<a href=\"Gradle settings\">Open gradle settings</a>", configurationErrorMsg)
: String.format("<br>\n%s", configurationErrorMsg);
GradleNotification.getInstance(project).showBalloon(
errorTitle,
message, NotificationType.WARNING, new NotificationListener() {
@Override
public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
notification.expire();
if ("Gradle settings".equals(event.getDescription())) {
ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(GradleConstants.SYSTEM_ID);
assert manager instanceof GradleManager;
GradleManager gradleManager = (GradleManager)manager;
Configurable configurable = gradleManager.getConfigurable(project);
ShowSettingsUtil.getInstance().editConfigurable(project, configurable);
}
else {
BrowserUtil.browse(event.getDescription());
}
}
}
);
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner.events;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.execution.testframework.sm.runner.SMTestProxy;
import org.jetbrains.plugins.gradle.util.XmlXpathHelper;
import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsoleManager;
/**
* @author Vladislav.Soroka
* @since 2/28/14
*/
public class OnOutputEvent extends AbstractTestEvent {
public OnOutputEvent(GradleTestsExecutionConsoleManager consoleManager) {
super(consoleManager);
}
@Override
public void process(XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException {
final String testId = getTestId(eventXml);
final String destination = eventXml.queryXml("/ijLog/event/test/event/@destination");
final String output = eventXml.queryXml("/ijLog/event/test/event");
SMTestProxy testProxy = getConsoleManager().getTestsMap().get(testId);
if (testProxy == null) return;
testProxy.addStdOutput(output, "StdOut".equals(destination) ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
}
}
@@ -0,0 +1,39 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner.events;
import org.jetbrains.plugins.gradle.util.XmlXpathHelper;
import org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsoleManager;
import java.io.File;
/**
* @author Vladislav.Soroka
* @since 2/28/14
*/
public class ReportLocationEvent extends AbstractTestEvent {
public ReportLocationEvent(GradleTestsExecutionConsoleManager consoleManager) {
super(consoleManager);
}
@Override
public void process(XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException {
final String testReport = eventXml.queryXml("/ijLog/event/@testReport");
assert testReport != null;
getProperties().setGradleTestReport(new File(testReport));
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner.events;
import org.jetbrains.plugins.gradle.util.XmlXpathHelper;
/**
* @author Vladislav.Soroka
* @since 2/28/14
*/
public interface TestEvent {
void process(XmlXpathHelper eventXml) throws XmlXpathHelper.XmlParserException;
}
@@ -0,0 +1,45 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner.events;
/**
* @author Vladislav.Soroka
* @since 2/28/14
*/
public enum TestEventResult {
SUCCESS("SUCCESS"),
FAILURE("FAILURE"),
SKIPPED("SKIPPED"),
UNKNOWN_RESULT("unknown");
private final String value;
TestEventResult(String v) {
value = v;
}
public String value() {
return value;
}
public static TestEventResult fromValue(String v) {
for (TestEventResult c : TestEventResult.values()) {
if (c.value.equals(v)) {
return c;
}
}
return UNKNOWN_RESULT;
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.execution.test.runner.events;
/**
* @author Vladislav.Soroka
* @since 2/28/14
*/
public enum TestEventType {
CONFIGURATION_ERROR("configurationError"),
REPORT_LOCATION("reportLocation"),
BEFORE_TEST("beforeTest"),
ON_OUTPUT("onOutput"),
AFTER_TEST("afterTest"),
BEFORE_SUITE("beforeSuite"),
AFTER_SUITE("afterSuite"),
UNKNOWN_EVENT("unknown");
private final String value;
TestEventType(String v) {
value = v;
}
public String value() {
return value;
}
public static TestEventType fromValue(String v) {
for (TestEventType c : TestEventType.values()) {
if (c.value.equals(v)) {
return c;
}
}
return UNKNOWN_EVENT;
}
}
@@ -16,6 +16,7 @@
package org.jetbrains.plugins.gradle.service.execution;
import com.intellij.openapi.externalSystem.service.execution.AbstractExternalSystemTaskConfigurationType;
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
import org.jetbrains.plugins.gradle.util.GradleConstants;
/**
@@ -23,8 +24,12 @@ import org.jetbrains.plugins.gradle.util.GradleConstants;
* @since 23.05.13 17:56
*/
public class GradleExternalTaskConfigurationType extends AbstractExternalSystemTaskConfigurationType {
public GradleExternalTaskConfigurationType() {
super(GradleConstants.SYSTEM_ID);
}
public static GradleExternalTaskConfigurationType getInstance() {
return (GradleExternalTaskConfigurationType)ExternalSystemUtil.findConfigurationType(GradleConstants.SYSTEM_ID);
}
}
@@ -48,6 +48,7 @@ import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* @author Denis Zhdanov
@@ -154,6 +155,9 @@ public class GradleExecutionHelper {
return StringUtil.isEmpty(s) ? null : s;
}
});
// TODO remove this replacement when --tests option will become available for tooling API
replaceTestCommandOptionWithInitScript(filteredArgs);
operation.withArguments(ArrayUtil.toStringArray(filteredArgs));
}
@@ -384,7 +388,7 @@ public class GradleExecutionHelper {
LOG.warn("Can't get init script template");
return null;
}
String s = FileUtil.loadTextAndClose(stream).replace("${EXTENSIONS_JARS_PATH}", getToolingExtensionsJarPaths());
String s = FileUtil.loadTextAndClose(stream).replaceFirst(Pattern.quote("${EXTENSIONS_JARS_PATH}"), getToolingExtensionsJarPaths());
if (isBuildSrcProject) {
String buildSrcDefaultInitScript = getBuildSrcDefaultInitScript();
if (buildSrcDefaultInitScript == null) return null;
@@ -432,6 +436,52 @@ public class GradleExecutionHelper {
}
}
private static void replaceTestCommandOptionWithInitScript(@NotNull List<String> args) {
Set<String> testIncludePatterns = ContainerUtil.newLinkedHashSet();
Iterator<String> it = args.iterator();
while (it.hasNext()) {
final String next = it.next();
if ("--tests".equals(next)) {
it.remove();
if (it.hasNext()) {
testIncludePatterns.add(it.next());
it.remove();
}
}
}
if (!testIncludePatterns.isEmpty()) {
StringBuilder buf = new StringBuilder();
buf.append('[');
for (Iterator<String> iterator = testIncludePatterns.iterator(); iterator.hasNext(); ) {
String pattern = iterator.next();
buf.append('\"').append(pattern).append('\"');
if (iterator.hasNext()) {
buf.append(',');
}
}
buf.append(']');
InputStream stream =
ProjectImportAction.class.getResourceAsStream("/org/jetbrains/plugins/gradle/tooling/internal/testFilterInit.gradle");
try {
if (stream == null) {
LOG.warn("Can't get test filter init script template");
return;
}
String s = FileUtil.loadTextAndClose(stream).replaceFirst(Pattern.quote("${TEST_NAME_INCLUDES}"), buf.toString());
final File tempFile = FileUtil.createTempFile("ijinit", '.' + GradleConstants.EXTENSION, true);
FileUtil.writeToFile(tempFile, s);
ContainerUtil.addAll(args, GradleConstants.INIT_SCRIPT_CMD_OPTION, tempFile.getAbsolutePath());
}
catch (Exception e) {
LOG.warn("Can't generate IJ gradle test filter init script", e);
}
finally {
StreamUtil.closeStream(stream);
}
}
}
@NotNull
private static String getToolingExtensionsJarPaths() throws ClassNotFoundException {
final ArrayList<Class<?>> list = ContainerUtil.newArrayList(
@@ -73,10 +73,6 @@ public class GradleTaskManager extends AbstractExternalSystemTaskManager<GradleE
@Nullable final String debuggerSetup,
@NotNull final ExternalSystemTaskNotificationListener listener) throws ExternalSystemException {
// if (settings != null) {
// myHelper.ensureInstalledWrapper(id, projectPath, settings, listener);
// }
// TODO add support for external process mode
if (ExternalSystemApiUtil.isInProcessMode(GradleConstants.SYSTEM_ID)) {
for (GradleTaskManagerExtension gradleTaskManagerExtension : GradleTaskManagerExtension.EP_NAME.getExtensions()) {
@@ -86,6 +82,9 @@ public class GradleTaskManager extends AbstractExternalSystemTaskManager<GradleE
}
}
}
if(!scriptParameters.contains("--tests") && taskNames.contains("test")) {
ContainerUtil.addAll(scriptParameters, "--tests", "*");
}
Function<ProjectConnection, Void> f = new Function<ProjectConnection, Void>() {
@Override
@@ -1,30 +0,0 @@
package org.jetbrains.plugins.gradle.ui;
import com.intellij.openapi.application.ex.ApplicationInfoEx;
import com.intellij.openapi.util.IconLoader;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
* @author Denis Zhdanov
* @since 1/18/12 1:16 PM
*/
public class GradleIcons {
public static final Icon PROJECT_ICON = getProjectIcon();
private GradleIcons() {
}
@NotNull
private static Icon getProjectIcon() {
try {
return IconLoader.getIcon(ApplicationInfoEx.getInstanceEx().getSmallIconUrl());
}
catch (Exception e) {
// Control flow may reach this place if we run tests and platform IoC has not been initialised.
return IconLoader.getIcon("/nodes/ideProject.png");
}
}
}
@@ -0,0 +1,106 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.util;
import com.sun.javafx.beans.annotations.NonNull;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.StringReader;
/**
* {@link XmlXpathHelper} utility class for query of XML using XPath expressions.
*
* @author Vladislav.Soroka
* @since 2/20/14
*/
public class XmlXpathHelper {
private final XPath xpath;
private Document xmlDocument;
/**
* Parses the given XML string as a DOM document, using the JDK parser. The parser does not
* validate, and is namespace aware.
*
* @param xml the XML content to be parsed (must be well formed)
* @param namespaceAware whether the parser is namespace aware
* @throws XmlParserException
*/
public XmlXpathHelper(@NonNull String xml, boolean namespaceAware) throws XmlParserException {
xpath = XPathFactory.newInstance().newXPath();
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
InputSource is = new InputSource(new StringReader(xml));
factory.setNamespaceAware(namespaceAware);
factory.setValidating(false);
xmlDocument = factory.newDocumentBuilder().parse(is);
}
catch (Exception ex) {
throw new XmlParserException(ex);
}
}
public XmlXpathHelper(@NonNull String xml) throws XmlParserException {
this(xml, false);
}
public String queryXml(final String xpathExpr) throws XmlParserException {
try {
return xmlDocument == null ? "" : xpath.evaluate(xpathExpr, xmlDocument);
}
catch (XPathExpressionException ex) {
throw new XmlParserException(ex);
}
}
/**
* Extracts the String value for the given expression.
*
* @param node
* @param expr
* @return
*/
public String getValue(Node node, String expr) throws XmlParserException {
try {
return xpath.compile(expr).evaluate(node);
}
catch (XPathExpressionException ex) {
throw new XmlParserException(ex);
}
}
/**
* {@link XmlParserException} indicates errors during xml processing.
*/
public static class XmlParserException extends Exception {
public XmlParserException(final Throwable cause) {
super(cause);
}
public XmlParserException(final String message, final Throwable cause) {
super(message, cause);
}
public XmlParserException(final String message) {
super(message);
}
}
}
@@ -0,0 +1,163 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
String[] ijTestIncludes = ${TEST_NAME_INCLUDES}
boolean ijTestFilteringSupported = GradleVersion.current().compareTo(GradleVersion.version("1.10"))>=0
boolean skipExecution
gradle.addBuildListener(new BuildListener() {
void buildFinished(BuildResult result) {
if(result.failure?.message?.startsWith("Task 'cleanTest' not found in") || result.failure?.message?.startsWith("Task 'test' not found in")) {
logConfigurationError(
"Unable to run unit tests",
"Unable to run unit tests, since there is no 'test' task configured in the project." +
"\n<br>You can add the following into the project gradle script: apply plugin: 'java'" +
"\n<br><br>See <a href='http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.testing.Test.html'>Gradle Test task</a> for details",
false
)
}
}
void buildStarted(Gradle gradle){}
void projectsEvaluated(Gradle gradle){}
void projectsLoaded(Gradle gradle){}
void settingsEvaluated(Settings settings){}
})
for (incl in ijTestIncludes) {
if(incl != '*' && !ijTestFilteringSupported) {
skipExecution = true
logConfigurationError(
"Unable to configure test filtering",
"Unable to configure test filtering for " + GradleVersion.current() +".<br>Please, use Gradle 1.10 or above or run all tests of a module.",
true)
break;
}
}
gradle.taskGraph.beforeTask { Task task ->
task.onlyIf { !skipExecution }
if (task instanceof Test) {
try {
logTestReportLocation(task.reports?.html?.entryPoint?.path)
task.beforeSuite { descriptor ->
logTestEvent("beforeSuite", descriptor, null, null)
}
task.afterSuite { descriptor, result ->
logTestEvent("afterSuite", descriptor, null, result)
}
task.beforeTest { descriptor ->
logTestEvent("beforeTest", descriptor, null, null)
}
task.onOutput { descriptor, event ->
logTestEvent("onOutput", descriptor, event, null)
}
task.afterTest { descriptor, result ->
logTestEvent("afterTest", descriptor, null, result)
}
if(ijTestFilteringSupported) {
task.filter {
ijTestIncludes.each(){ includeTestsMatching "${it}" }
}
}
}
catch (all) {
logger.error("", all)
}
}
}
Object logTestEvent(def testEventType, def testDescriptor, def testEvent, def testResult) {
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
xml.event(type: testEventType){
test(id: testDescriptor.id, parentId: testDescriptor.parent?.id ?: '') {
if(testDescriptor){
descriptor(name: testDescriptor.name ?: '', className: testDescriptor.className ?: '')
}
if(testEvent) {
def message = escapeCdata(testEvent.message)
event(destination: testEvent.destination){
xml.mkp.yieldUnescaped("$message")
}
}
if(testResult) {
def errorMsg = escapeCdata(testResult.exception?.message?:'')
def stackTrace = escapeCdata(getStackTrace(testResult.exception))
result(resultType: testResult.resultType ?: '', startTime: testResult.startTime, endTime: testResult.endTime){
if(testResult.exception?.message?.trim()) xml.mkp.yieldUnescaped("<errorMsg>$errorMsg</errorMsg>")
if(testResult.exception) xml.mkp.yieldUnescaped("<stackTrace>$stackTrace</stackTrace>")
if('junit.framework.ComparisonFailure'.equals(testResult.exception?.class?.name) ||
'org.junit.ComparisonFailure'.equals(testResult.exception?.class?.name)) {
def expected = escapeCdata(testResult.exception.fExpected)
def actual = escapeCdata(testResult.exception.fActual)
failureType('comparison')
xml.mkp.yieldUnescaped("<expected>$expected</expected>")
xml.mkp.yieldUnescaped("<actual>$actual</actual>")
} else if('junit.framework.AssertionFailedError'.equals(testResult.exception?.class?.name) ||
testResult.exception instanceof AssertionError) {
failureType('assertionFailed')
} else {
failureType('error')
}
}
}
}
}
writeLog(writer.toString())
}
def logTestReportLocation(def report) {
if(!report) return
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
xml.event(type: 'reportLocation', testReport: report)
writeLog(writer.toString());
}
def logConfigurationError(aTitle, aMessage, boolean openSettings) {
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
xml.event(type: 'configurationError', openSettings: openSettings) {
title(aTitle)
message(aMessage)
}
writeLog(writer.toString());
}
String escapeCdata(String s) {
return "<![CDATA[" + s.replaceAll("]]>", "]]]]><![CDATA[>")+ "]]>";
}
def getStackTrace(Throwable t) {
if(!t) return ''
StringWriter sw = new StringWriter()
t.printStackTrace(new PrintWriter(sw))
sw.toString()
}
def wrap(String s) {
if(!s) return s;
s.replaceAll("\r\n|\n\r|\n|\r","<ijLogEol/>\n")
}
def writeLog(s) {
println String.format("\n<ijLog>%s</ijLog>", wrap(s))
}