diff --git a/java/java-runtime/src/com/intellij/rt/execution/junit/ComparisonFailureData.java b/java/java-runtime/src/com/intellij/rt/execution/junit/ComparisonFailureData.java
new file mode 100644
index 000000000000..872fe864f76e
--- /dev/null
+++ b/java/java-runtime/src/com/intellij/rt/execution/junit/ComparisonFailureData.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2000-2013 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.rt.execution.junit;
+
+import junit.framework.ComparisonFailure;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ComparisonFailureData {
+ private final String myExpected;
+ private final String myActual;
+ private final String myFilePath;
+
+ private static Map EXPECTED = new HashMap();
+ private static Map ACTUAL = new HashMap();
+
+ static {
+ try {
+ init(ComparisonFailure.class);
+ init(org.junit.ComparisonFailure.class);
+ }
+ catch (Throwable e) {
+ }
+ }
+
+ private static void init(Class exceptionClass) throws NoSuchFieldException {
+ final Field expectedField = exceptionClass.getDeclaredField("fExpected");
+ expectedField.setAccessible(true);
+ EXPECTED.put(exceptionClass, expectedField);
+
+ final Field actualField = exceptionClass.getDeclaredField("fActual");
+ actualField.setAccessible(true);
+ ACTUAL.put(exceptionClass, actualField);
+ }
+
+ public ComparisonFailureData(String expected, String actual) {
+ this(expected, actual, null);
+ }
+
+ public ComparisonFailureData(String expected, String actual, String filePath) {
+ myExpected = expected;
+ myActual = actual;
+ myFilePath = filePath;
+ }
+
+ public String getFilePath() {
+ return myFilePath;
+ }
+
+ public String getExpected() {
+ return myExpected;
+ }
+
+ public String getActual() {
+ return myActual;
+ }
+
+ public static ComparisonFailureData create(Throwable assertion) {
+ if (assertion instanceof FileComparisonFailure) {
+ final FileComparisonFailure comparisonFailure = (FileComparisonFailure)assertion;
+ return new ComparisonFailureData(comparisonFailure.getExpected(), comparisonFailure.getActual(), comparisonFailure.getFilePath());
+ }
+ try {
+ return new ComparisonFailureData(getExpected(assertion), getActual(assertion));
+ }
+ catch (Throwable e) {
+ return null;
+ }
+ }
+
+ public static String getActual(Throwable assertion) throws IllegalAccessException, NoSuchFieldException {
+ return get(assertion, ACTUAL, "fActual");
+ }
+
+ public static String getExpected(Throwable assertion) throws IllegalAccessException, NoSuchFieldException {
+ return get(assertion, EXPECTED, "fExpected");
+ }
+
+ private static String get(final Throwable assertion, final Map staticMap, final String fieldName) throws IllegalAccessException, NoSuchFieldException {
+ String actual;
+ if (assertion instanceof ComparisonFailure) {
+ actual = (String)((Field)staticMap.get(ComparisonFailure.class)).get(assertion);
+ }
+ else if (assertion instanceof org.junit.ComparisonFailure) {
+ actual = (String)((Field)staticMap.get(org.junit.ComparisonFailure.class)).get(assertion);
+ }
+ else {
+ Field field = assertion.getClass().getDeclaredField(fieldName);
+ field.setAccessible(true);
+ actual = (String)field.get(assertion);
+ }
+ return actual;
+ }
+}
diff --git a/java/java-runtime/src/com/intellij/rt/execution/junit/FileComparisonFailure.java b/java/java-runtime/src/com/intellij/rt/execution/junit/FileComparisonFailure.java
index 0a66a53106cb..788e872067f2 100644
--- a/java/java-runtime/src/com/intellij/rt/execution/junit/FileComparisonFailure.java
+++ b/java/java-runtime/src/com/intellij/rt/execution/junit/FileComparisonFailure.java
@@ -31,6 +31,18 @@ public class FileComparisonFailure extends ComparisonFailure implements KnownExc
myFilePath = filePath;
}
+ public String getFilePath() {
+ return myFilePath;
+ }
+
+ public String getExpected() {
+ return myExpected;
+ }
+
+ public String getActual() {
+ return myActual;
+ }
+
public PacketFactory getPacketFactory() {
return new MyPacketFactory(this, myExpected, myActual, myFilePath);
}
diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties
index c2d4fcbae11d..3609f656e9cb 100644
--- a/platform/platform-resources-en/src/misc/registry.properties
+++ b/platform/platform-resources-en/src/misc/registry.properties
@@ -290,3 +290,4 @@ disable.graph.inference=false
comment.by.line.bulk.lines.trigger=100
scene.builder.start.executable=true
+junit_sm_runner=false
diff --git a/plugins/junit/junit.iml b/plugins/junit/junit.iml
index 8f4c38643198..04538142d4df 100644
--- a/plugins/junit/junit.iml
+++ b/plugins/junit/junit.iml
@@ -18,6 +18,7 @@
+
diff --git a/plugins/junit/src/com/intellij/execution/junit/TestObject.java b/plugins/junit/src/com/intellij/execution/junit/TestObject.java
index 04f2dfcc0579..7b54bda50b0a 100644
--- a/plugins/junit/src/com/intellij/execution/junit/TestObject.java
+++ b/plugins/junit/src/com/intellij/execution/junit/TestObject.java
@@ -35,6 +35,11 @@ import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.testframework.*;
+import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil;
+import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties;
+import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView;
+import com.intellij.execution.testframework.ui.BaseTestsOutputConsoleView;
+import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.util.JavaParametersUtil;
import com.intellij.execution.util.ProgramParametersUtil;
@@ -53,6 +58,7 @@ import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
@@ -62,6 +68,7 @@ import com.intellij.rt.execution.junit.IDEAJUnitListener;
import com.intellij.rt.execution.junit.JUnitStarter;
import com.intellij.util.Function;
import com.intellij.util.PathUtil;
+import jetbrains.buildServer.messages.serviceMessages.ServiceMessageTypes;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -76,6 +83,7 @@ public abstract class TestObject implements JavaCommandLine {
protected static final Logger LOG = Logger.getInstance("#com.intellij.execution.junit.TestObject");
private static final String MESSAGE = ExecutionBundle.message("configuration.not.speficied.message");
+ private static final String JUNIT_TEST_FRAMEWORK_NAME = "JUnit";
protected JavaParameters myJavaParameters;
private final Project myProject;
@@ -198,6 +206,7 @@ public abstract class TestObject implements JavaCommandLine {
myJavaParameters.getClassPath().add(JavaSdkUtil.getIdeaRtJarPath());
myJavaParameters.getClassPath().add(PathUtil.getJarPathForClass(JUnitStarter.class));
+ myJavaParameters.getClassPath().add(PathUtil.getJarPathForClass(ServiceMessageTypes.class));
myJavaParameters.getProgramParametersList().add(JUnitStarter.IDE_VERSION + JUnitStarter.VERSION);
for (RunConfigurationExtension ext : Extensions.getExtensions(RunConfigurationExtension.EP_NAME)) {
ext.updateJavaParameters(myConfiguration, myJavaParameters, getRunnerSettings());
@@ -248,9 +257,16 @@ public abstract class TestObject implements JavaCommandLine {
@Override
public ExecutionResult execute(final Executor executor, @NotNull final ProgramRunner runner) throws ExecutionException {
+ final boolean smRunner = Registry.is("junit_sm_runner", false);
+ if (smRunner) {
+ myJavaParameters.getVMParametersList().add("-Didea.junit.sm_runner");
+ }
final JUnitProcessHandler handler = createHandler(executor);
final RunnerSettings runnerSettings = getRunnerSettings();
JavaRunConfigurationExtensionManager.getInstance().attachExtensionsToProcess(myConfiguration, handler, runnerSettings);
+ if (smRunner) {
+ return useSmRunner(executor, handler);
+ }
final TestProxy unboundOutputRoot = new TestProxy(new RootTestInfo());
final JUnitConsoleProperties consoleProperties = new JUnitConsoleProperties(myConfiguration, executor);
final JUnitTreeConsoleView consoleView = new JUnitTreeConsoleView(consoleProperties, runnerSettings, getConfigurationSettings(), unboundOutputRoot);
@@ -349,6 +365,42 @@ public abstract class TestObject implements JavaCommandLine {
return result;
}
+ private ExecutionResult useSmRunner(Executor executor, JUnitProcessHandler handler) {
+ TestConsoleProperties testConsoleProperties = new SMTRunnerConsoleProperties(
+ new RuntimeConfigurationProducer.DelegatingRuntimeConfiguration(
+ (JUnitConfiguration)myEnvironment.getRunProfile()),
+ JUNIT_TEST_FRAMEWORK_NAME,
+ executor
+ );
+
+ testConsoleProperties.setIfUndefined(TestConsoleProperties.HIDE_PASSED_TESTS, false);
+
+ BaseTestsOutputConsoleView smtConsoleView = SMTestRunnerConnectionUtil.createConsoleWithCustomLocator(
+ JUNIT_TEST_FRAMEWORK_NAME,
+ testConsoleProperties,
+ myEnvironment.getRunnerSettings(),
+ myEnvironment.getConfigurationSettings(), null);
+
+
+ Disposer.register(myProject, smtConsoleView);
+
+ final ConsoleView consoleView = smtConsoleView;
+ consoleView.attachToProcess(handler);
+
+ final RerunFailedTestsAction rerunFailedTestsAction = new RerunFailedTestsAction(consoleView);
+ rerunFailedTestsAction.init(testConsoleProperties, myEnvironment);
+ rerunFailedTestsAction.setModelProvider(new Getter() {
+ @Override
+ public TestFrameworkRunningModel get() {
+ return ((SMTRunnerConsoleView)consoleView).getResultsViewer();
+ }
+ });
+
+ final DefaultExecutionResult result = new DefaultExecutionResult(consoleView, handler);
+ result.setRestartActions(rerunFailedTestsAction);
+ return result;
+ }
+
protected void notifyByBalloon(JUnitRunningModel model, boolean started, JUnitConsoleProperties consoleProperties) {
TestsUIUtil.notifyByBalloon(myProject, started, model != null ? model.getRoot() : null, consoleProperties);
}
diff --git a/plugins/junit_rt/junit_rt.iml b/plugins/junit_rt/junit_rt.iml
index e24b63784ebd..37653b53cb5a 100644
--- a/plugins/junit_rt/junit_rt.iml
+++ b/plugins/junit_rt/junit_rt.iml
@@ -9,6 +9,7 @@
+
diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java
index c4efb6e3c1f9..810ece92ec88 100644
--- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java
+++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java
@@ -15,10 +15,7 @@
*/
package com.intellij.junit4;
-import com.intellij.rt.execution.junit.IDEAJUnitListener;
-import com.intellij.rt.execution.junit.IdeaTestRunner;
-import com.intellij.rt.execution.junit.TimeSender;
-import com.intellij.rt.execution.junit.TreeSender;
+import com.intellij.rt.execution.junit.*;
import com.intellij.rt.execution.junit.segments.OutputObjectRegistry;
import com.intellij.rt.execution.junit.segments.SegmentedOutputStream;
import org.junit.internal.requests.ClassRequest;
@@ -140,8 +137,12 @@ public class JUnit4IdeaTestRunner implements IdeaTestRunner {
public void setStreams(SegmentedOutputStream segmentedOut, SegmentedOutputStream segmentedErr, int lastIdx) {
- myRegistry = new JUnit4OutputObjectRegistry(segmentedOut, lastIdx);
- myTestsListener = new JUnit4TestResultsSender(myRegistry);
+ if (JUnitStarter.SM_RUNNER) {
+ myTestsListener = new SMTestSender();
+ } else {
+ myRegistry = new JUnit4OutputObjectRegistry(segmentedOut, lastIdx);
+ myTestsListener = new JUnit4TestResultsSender(myRegistry);
+ }
}
public Object getTestToStart(String[] args) {
diff --git a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java
index 7e22e3757ede..81844f425ac0 100644
--- a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java
+++ b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java
@@ -20,16 +20,29 @@
*/
package com.intellij.junit4;
+import com.intellij.rt.execution.junit.*;
+import jetbrains.buildServer.messages.serviceMessages.ServiceMessage;
+import jetbrains.buildServer.messages.serviceMessages.ServiceMessageTypes;
+import junit.framework.ComparisonFailure;
+import org.junit.Ignore;
import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
class SMTestSender extends RunListener {
+ private static final String JUNIT_FRAMEWORK_COMPARISON_NAME = ComparisonFailure.class.getName();
+ private static final String ORG_JUNIT_COMPARISON_NAME = "org.junit.ComparisonFailure";
+
private String myCurrentClassName;
public void testRunStarted(Description description) throws Exception {
- //System.out.println("##teamcity[testSuiteStarted name =\'" + description.toString() + "\']");
+ System.out.println("##teamcity[testSuiteStarted name =\'" + description.toString() + "\']");
}
public void testRunFinished(Result result) throws Exception {
@@ -55,20 +68,108 @@ class SMTestSender extends RunListener {
}
public void testFailure(Failure failure) throws Exception {
- System.out.println("##teamcity[testFailed name=\'" +
- JUnit4ReflectionUtil.getMethodName(failure.getDescription()) +
- "\' message=\'" +
- failure.getMessage() +
- "\' details=\'" +
- failure.getTrace().replaceAll("\n", "n").replaceAll("\r", "r") +
- "\']");
+ final String failureMessage = failure.getMessage();
+ final String trace = failure.getTrace();
+ final Map attrs = new HashMap();
+ attrs.put("name", JUnit4ReflectionUtil.getMethodName(failure.getDescription()));
+ attrs.put("message", failureMessage);
+ final ComparisonFailureData notification = createExceptionNotification(failure.getException());
+ if (notification != null) {
+ attrs.put("expected", notification.getExpected());
+ attrs.put("actual", notification.getActual());
+
+ final int failureIdx = trace.indexOf(failureMessage);
+ attrs.put("details", failureIdx > -1 ? trace.substring(failureIdx + failureMessage.length()) : trace);
+ } else {
+ attrs.put("details", trace);
+ }
+
+ System.out.println(ServiceMessage.asString(ServiceMessageTypes.TEST_FAILED, attrs));
}
public void testAssumptionFailure(Failure failure) {
-
+ prepareIgnoreMessage(failure.getDescription(), false);
}
- public void testIgnored(Description description) throws Exception {
- System.out.println("##teamcity[testIgnored name=\'" + JUnit4ReflectionUtil.getMethodName(description) + "\']");
+ public synchronized void testIgnored(Description description) throws Exception {
+ prepareIgnoreMessage(description, true);
+ }
+
+ private static void prepareIgnoreMessage(Description description, boolean commentMessage) {
+ Map attrs = new HashMap();
+ if (commentMessage) {
+ try {
+ final Ignore ignoredAnnotation = (Ignore)description.getAnnotation(Ignore.class);
+ if (ignoredAnnotation != null) {
+ final String val = ignoredAnnotation.value();
+ if (val != null) {
+ attrs.put("message", val);
+ }
+ }
+ }
+ catch (NoSuchMethodError ignored) {
+ //junit < 4.4
+ }
+ }
+ attrs.put("name", JUnit4ReflectionUtil.getMethodName(description));
+ System.out.println(ServiceMessage.asString(ServiceMessageTypes.TEST_IGNORED, attrs));
+ }
+
+ private static boolean isComparisonFailure(Throwable throwable) {
+ if (throwable == null) return false;
+ return isComparisonFailure(throwable.getClass());
+ }
+
+ private static boolean isComparisonFailure(Class aClass) {
+ if (aClass == null) return false;
+ final String throwableClassName = aClass.getName();
+ if (throwableClassName.equals(JUNIT_FRAMEWORK_COMPARISON_NAME) || throwableClassName.equals(ORG_JUNIT_COMPARISON_NAME)) return true;
+ return isComparisonFailure(aClass.getSuperclass());
+ }
+
+ private static ComparisonFailureData createExceptionNotification(Throwable assertion) {
+ if (isComparisonFailure(assertion)) {
+ return ComparisonFailureData.create(assertion);
+ }
+ final Throwable cause = assertion.getCause();
+ if (isComparisonFailure(cause)) {
+ try {
+ return ComparisonFailureData.create(assertion);
+ }
+ catch (Throwable ignore) {
+ }
+ }
+
+ final String message = assertion.getMessage();
+ if (message != null) {
+ ComparisonFailureData notification = createExceptionNotification(message, "\nExpected: is \"(.*)\"\n\\s*got: \"(.*)\"\n");
+ if (notification == null) {
+ notification = createExceptionNotification(message, "\nExpected: is \"(.*)\"\n\\s*but: was \"(.*)\"");
+ }
+ if (notification == null) {
+ notification = createExceptionNotification(message, "\nExpected: (.*)\n\\s*got: (.*)");
+ }
+ if (notification == null) {
+ notification = createExceptionNotification(message, "\\s*expected same:<(.*)> was not:<(.*)>");
+ }
+ if (notification == null) {
+ notification = createExceptionNotification(message, "\\s*expected:<(.*)> but was:<(.*)>");
+ }
+ if (notification == null) {
+ notification = createExceptionNotification(message, "\nExpected: \"(.*)\"\n\\s*but: was \"(.*)\"");
+ }
+ if (notification != null) {
+ return notification;
+ }
+ }
+ return null;
+ }
+
+ private static ComparisonFailureData createExceptionNotification(String message, final String regex) {
+ final Matcher matcher = Pattern.compile(regex, Pattern.DOTALL | Pattern.CASE_INSENSITIVE).matcher(message);
+ if (matcher.matches()) {
+ return new ComparisonFailureData(matcher.group(1).replaceAll("\\\\n", "\n"), matcher.group(2).replaceAll("\\\\n", "\n"));
+ }
+ return null;
}
}
\ No newline at end of file
diff --git a/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java b/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java
index 9bdfd692aca4..2097d54a52bd 100644
--- a/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java
+++ b/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java
@@ -36,6 +36,7 @@ public class JUnitStarter {
private static final String SOCKET = "-socket";
private static String ourForkMode;
private static String ourCommandFileName;
+ public static boolean SM_RUNNER = System.getProperty("idea.junit.sm_runner") != null;
public static void main(String[] args) throws IOException {
SegmentedOutputStream out = new SegmentedOutputStream(System.out);
@@ -192,7 +193,7 @@ public class JUnitStarter {
}
IdeaTestRunner testRunner = (IdeaTestRunner)getAgentClass(isJUnit4).newInstance();
testRunner.setStreams(out, err, 0);
- return testRunner.startRunnerWithArgs(args, listeners, true);
+ return testRunner.startRunnerWithArgs(args, listeners, !SM_RUNNER);
}
catch (Exception e) {
e.printStackTrace(System.err);