comparison diff: create temp files when content would be truncated by OOM threshold on IDE side (IDEA-163388)

This commit is contained in:
Anna.Kozlova
2016-12-29 15:57:25 +01:00
parent 57f292dd7c
commit 695ce5b995
11 changed files with 155 additions and 33 deletions
@@ -19,6 +19,7 @@ import com.intellij.ExtensionPoints;
import com.intellij.debugger.impl.GenericDebuggerRunnerSettings;
import com.intellij.diagnostic.logging.OutputFileUtil;
import com.intellij.execution.configurations.*;
import com.intellij.execution.impl.ConsoleBuffer;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
@@ -125,14 +126,6 @@ public abstract class JavaTestFrameworkRunnableState<T extends
@NotNull
@Override
public ExecutionResult execute(@NotNull Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
return startSMRunner(executor);
}
/**
* to be removed in 2017.1, compatibility with jtreg plugin
*/
@Deprecated
protected ExecutionResult startSMRunner(Executor executor) throws ExecutionException {
final RunnerSettings runnerSettings = getRunnerSettings();
final SMTRunnerConsoleProperties testConsoleProperties = getConfiguration().createTestConsoleProperties(executor);
@@ -228,6 +221,10 @@ public abstract class JavaTestFrameworkRunnableState<T extends
javaParameters.getProgramParametersList().addAll(getNamedParams(parameters));
}
if (ConsoleBuffer.useCycleBuffer()) {
javaParameters.getVMParametersList().addProperty("idea.test.cyclic.buffer.size", String.valueOf(ConsoleBuffer.getCycleBufferSize()));
}
return javaParameters;
}
@@ -17,6 +17,9 @@ package com.intellij.rt.execution.junit;
import junit.framework.ComparisonFailure;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@@ -84,7 +87,8 @@ public class ComparisonFailureData {
final int failureIdx = failureMessage != null ? trace.indexOf(failureMessage) : -1;
final int failureMessageLength = failureMessage != null ? failureMessage.length() : 0;
attrs.put("details", failureIdx > -1 ? trace.substring(failureIdx + failureMessageLength) : trace);
String details = failureIdx > -1 ? trace.substring(failureIdx + failureMessageLength) : trace;
attrs.put("details", details);
if (notification != null) {
final int expectedIdx = trace.indexOf(expectedPrefix);
@@ -101,18 +105,26 @@ public class ComparisonFailureData {
attrs.put("message", comparisonFailureMessage);
final String filePath = notification.getFilePath();
final String actualFilePath = notification.getActualFilePath();
final String expected = notification.getExpected();
final String actual = notification.getActual();
int fullLength = (filePath == null && expected != null ? expected.length() : 0) +
(actualFilePath == null && actual != null ? actual.length() : 0) +
details.length() +
comparisonFailureMessage.length() + 100;
if (filePath != null) {
attrs.put("expectedFile", filePath);
}
else {
attrs.put("expected", notification.getExpected());
writeDiffSide(attrs, "expected", expected, fullLength);
}
final String actualFilePath = notification.getActualFilePath();
if (actualFilePath != null) {
attrs.put("actualFile", actualFilePath);
}
else {
attrs.put("actual", notification.getActual());
writeDiffSide(attrs, "actual", actual, fullLength);
}
}
else {
@@ -130,6 +142,37 @@ public class ComparisonFailureData {
}
}
private static void writeDiffSide(Map attrs, final String expectedOrActualPrefix, final String text, int fullLength) {
String property = System.getProperty("idea.test.cyclic.buffer.size");
int threshold;
try {
threshold = Integer.parseInt(property);
}
catch (NumberFormatException ignored) {
threshold = -1;
}
if (threshold > 0 && fullLength > threshold) {
try {
//noinspection SSBasedInspection
File tempFile = File.createTempFile(expectedOrActualPrefix, "");
OutputStream stream = new FileOutputStream(tempFile, false);
try {
stream.write(text.getBytes("UTF-8"), 0, text.length());
}
finally {
stream.close();
}
attrs.put(expectedOrActualPrefix + "File", tempFile.getAbsolutePath());
attrs.put(expectedOrActualPrefix + "IsTempFile", "true");
return;
}
catch (Throwable ignored) {}
}
attrs.put(expectedOrActualPrefix, text);
}
public static boolean isAssertionError(Class throwableClass) {
if (throwableClass == null) return false;
final String throwableClassName = throwableClass.getName();
@@ -254,10 +254,7 @@ public class GeneralIdBasedToSMTRunnerEventsConvertor extends GeneralTestEventsP
String failureMessage = testFailedEvent.getLocalizedFailureMessage();
String stackTrace = testFailedEvent.getStacktrace();
if (comparisonFailureActualText != null && comparisonFailureExpectedText != null) {
testProxy.setTestComparisonFailed(failureMessage, stackTrace,
comparisonFailureActualText, comparisonFailureExpectedText,
testFailedEvent.getExpectedFilePath(),
testFailedEvent.getActualFilePath());
testProxy.setTestComparisonFailed(failureMessage, stackTrace, comparisonFailureActualText, comparisonFailureExpectedText, testFailedEvent);
} else if (comparisonFailureActualText == null && comparisonFailureExpectedText == null) {
testProxy.setTestFailed(failureMessage, stackTrace, testFailedEvent.isTestError());
} else {
@@ -386,9 +386,7 @@ public class GeneralToSMTRunnerEventsConvertor extends GeneralTestEventsProcesso
}
if (comparisionFailureActualText != null && comparisionFailureExpectedText != null) {
testProxy.setTestComparisonFailed(localizedMessage, stackTrace,
comparisionFailureActualText, comparisionFailureExpectedText,
testFailedEvent.getExpectedFilePath(), testFailedEvent.getActualFilePath());
testProxy.setTestComparisonFailed(localizedMessage, stackTrace, comparisionFailureActualText, comparisionFailureExpectedText, testFailedEvent);
}
else if (comparisionFailureActualText == null && comparisionFailureExpectedText == null) {
testProxy.setTestFailed(localizedMessage, stackTrace, isTestError);
@@ -20,6 +20,7 @@ import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.testframework.*;
import com.intellij.execution.testframework.sm.SMStacktraceParser;
import com.intellij.execution.testframework.sm.SMStacktraceParserEx;
import com.intellij.execution.testframework.sm.runner.events.TestFailedEvent;
import com.intellij.execution.testframework.sm.runner.states.*;
import com.intellij.execution.testframework.sm.runner.ui.TestsPresentationUtil;
import com.intellij.execution.testframework.stacktrace.DiffHyperlink;
@@ -29,6 +30,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
@@ -446,23 +448,26 @@ public class SMTestProxy extends AbstractTestProxy {
@Nullable final String stackTrace,
@NotNull final String actualText,
@NotNull final String expectedText) {
setTestComparisonFailed(localizedMessage, stackTrace, actualText, expectedText, null);
setTestComparisonFailed(localizedMessage, stackTrace, actualText, expectedText, null, null);
}
public void setTestComparisonFailed(@NotNull final String localizedMessage,
@Nullable final String stackTrace,
@NotNull final String actualText,
@NotNull final String expectedText,
@Nullable final String filePath) {
setTestComparisonFailed(localizedMessage, stackTrace, actualText, expectedText, filePath, null);
@NotNull final TestFailedEvent event) {
TestComparisionFailedState comparisionFailedState =
setTestComparisonFailed(localizedMessage, stackTrace, actualText, expectedText, event.getExpectedFilePath(), event.getActualFilePath());
comparisionFailedState.setToDeleteExpectedFile(event.isExpectedFIleTemp());
comparisionFailedState.setToDeleteActualFile(event.isActualFileTemp());
}
public void setTestComparisonFailed(@NotNull final String localizedMessage,
@Nullable final String stackTrace,
@NotNull final String actualText,
@NotNull final String expectedText,
@Nullable final String expectedFilePath,
@Nullable final String actualFilePath) {
public TestComparisionFailedState setTestComparisonFailed(@NotNull final String localizedMessage,
@Nullable final String stackTrace,
@NotNull final String actualText,
@NotNull final String expectedText,
@Nullable final String expectedFilePath,
@Nullable final String actualFilePath) {
setStacktraceIfNotSet(stackTrace);
final TestComparisionFailedState comparisionFailedState = new TestComparisionFailedState(localizedMessage, stackTrace, actualText, expectedText, expectedFilePath, actualFilePath);
if (myState instanceof CompoundTestFailedState) {
@@ -478,6 +483,16 @@ public class SMTestProxy extends AbstractTestProxy {
myState = comparisionFailedState;
}
fireOnNewPrintable(comparisionFailedState);
return comparisionFailedState;
}
@Override
public void dispose() {
if (myState instanceof TestFailedState) {
Disposer.dispose((TestFailedState)myState);
}
super.dispose();
}
public void setTestIgnored(@Nullable String ignoreComment, @Nullable String stackTrace) {
@@ -23,6 +23,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.Map;
public class TestFailedEvent extends TreeNodeEvent {
@@ -34,6 +35,8 @@ public class TestFailedEvent extends TreeNodeEvent {
private final String myExpectedFilePath;
private final String myActualFilePath;
private final long myDurationMillis;
private boolean myExpectedFileTemp;
private boolean myActualFileTemp;
public TestFailedEvent(@NotNull TestFailed testFailed, boolean testError) {
this(testFailed, testError, null);
@@ -71,7 +74,18 @@ public class TestFailedEvent extends TreeNodeEvent {
}
myComparisonFailureActualText = actual;
myDurationMillis = parseDuration(testFailed.getAttributes().get("duration"));
Map<String, String> attributes = testFailed.getAttributes();
myDurationMillis = parseDuration(attributes.get("duration"));
myActualFileTemp = Boolean.parseBoolean(attributes.get("actualIsTempFile"));
myExpectedFileTemp = Boolean.parseBoolean(attributes.get("expectedIsTempFile"));
}
public boolean isExpectedFIleTemp() {
return myExpectedFileTemp;
}
public boolean isActualFileTemp() {
return myActualFileTemp;
}
private static long parseDuration(@Nullable String durationStr) {
@@ -99,6 +113,9 @@ public class TestFailedEvent extends TreeNodeEvent {
comparisonFailureActualText,
comparisonFailureExpectedText,
null,
null,
false,
false,
-1);
}
@@ -110,6 +127,9 @@ public class TestFailedEvent extends TreeNodeEvent {
@Nullable String comparisonFailureActualText,
@Nullable String comparisonFailureExpectedText,
@Nullable String expectedFilePath,
@Nullable String actualFilePath,
boolean expectedFileTemp,
boolean actualFileTemp,
long durationMillis) {
super(testName, id);
myLocalizedFailureMessage = localizedFailureMessage;
@@ -124,9 +144,11 @@ public class TestFailedEvent extends TreeNodeEvent {
}
myComparisonFailureActualText = comparisonFailureActualText;
myActualFilePath = null;
myActualFilePath = actualFilePath;
myComparisonFailureExpectedText = comparisonFailureExpectedText;
myDurationMillis = durationMillis;
myExpectedFileTemp = expectedFileTemp;
myActualFileTemp = actualFileTemp;
}
@NotNull
@@ -18,7 +18,7 @@ package com.intellij.execution.testframework.sm.runner.states;
import com.intellij.execution.testframework.Printer;
import com.intellij.execution.testframework.stacktrace.DiffHyperlink;
import com.intellij.util.Function;
import com.intellij.openapi.util.Disposer;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,6 +36,7 @@ public class CompoundTestFailedState extends TestFailedState {
public void addFailure(TestFailedState state) {
myStates.add(state);
Disposer.register(this, state);
}
@Override
@@ -20,10 +20,13 @@ import com.intellij.execution.testframework.CompositePrintable;
import com.intellij.execution.testframework.Printer;
import com.intellij.execution.testframework.stacktrace.DiffHyperlink;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
/**
* @author Roman.Chernyatchik
*/
@@ -31,6 +34,8 @@ public class TestComparisionFailedState extends TestFailedState {
private final String myErrorMsgPresentation;
private final String myStacktracePresentation;
private DiffHyperlink myHyperlink;
private boolean myToDeleteExpectedFile;
private boolean myToDeleteActualFile;
public TestComparisionFailedState(@Nullable final String localizedMessage,
@@ -82,4 +87,21 @@ public class TestComparisionFailedState extends TestFailedState {
public DiffHyperlink getHyperlink() {
return myHyperlink;
}
public void setToDeleteExpectedFile(boolean expectedTemp) {
myToDeleteExpectedFile = expectedTemp;
}
public void setToDeleteActualFile(boolean actualTemp) {
myToDeleteActualFile = actualTemp;
}
public void dispose() {
if (myToDeleteActualFile) {
FileUtil.delete(new File(myHyperlink.getActualFilePath()));
}
if (myToDeleteExpectedFile) {
FileUtil.delete(new File(myHyperlink.getFilePath()));
}
}
}
@@ -19,6 +19,7 @@ import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.execution.testframework.CompositePrintable;
import com.intellij.execution.testframework.Printer;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -31,7 +32,7 @@ import java.util.List;
/**
* @author Roman Chernyatchik
*/
public class TestFailedState extends AbstractState {
public class TestFailedState extends AbstractState implements Disposable{
private final List<String> myPresentationText;
public TestFailedState(@Nullable final String localizedMessage,
@@ -51,6 +52,9 @@ public class TestFailedState extends AbstractState {
}
}
@Override
public void dispose() {}
@Nullable
public static String buildErrorPresentationText(@Nullable final String localizedMessage,
@Nullable final String stackTrace)
@@ -124,7 +124,7 @@ public class GeneralIdBasedToSMTRunnerEventsConvertorTest extends BaseSMTRunnerT
private void onTestFailed(@NotNull String id, @NotNull String errorMessage, int durationMillis) {
myEventsProcessor.onTestFailure(new TestFailedEvent(null, id, errorMessage, null, false, null,
null, null, durationMillis));
null, null, null, false, false, durationMillis));
}
}
@@ -29,11 +29,14 @@ import com.intellij.execution.testframework.sm.runner.ui.SMTestRunnerResultsForm
import com.intellij.execution.testframework.ui.TestsOutputConsolePrinter;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeModel;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
@@ -46,6 +49,7 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
private TreeModel myTreeModel;
private SMTestRunnerResultsForm myResultsViewer;
private MockPrinter myMockResettablePrinter;
private File myTempFile;
private class MyConsoleView extends SMTRunnerConsoleView {
private final TestsOutputConsolePrinter myTestsOutputConsolePrinter;
@@ -96,6 +100,10 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
Disposer.dispose(myConsole);
super.tearDown();
if (myTempFile != null) {
assertFalse(myTempFile.exists());
}
}
public void testOnStartedTesting() {
@@ -177,6 +185,21 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
assertFalse(proxy.isInProgress());
}
public void testOnTestComparisonFailureLongExpectedText() throws IOException {
onTestStarted("some_test");
myTempFile = FileUtil.createTempFile("expected", "");
FileUtil.writeToFile(myTempFile, "expected text");
myEventsProcessor.onTestFailure(new TestFailedEvent("some_test", "", "", null, false, "actual",
"expected", myTempFile.getCanonicalPath(), null, true, false, -1));
final String fullName = myEventsProcessor.getFullTestName("some_test");
final SMTestProxy proxy = myEventsProcessor.getProxyByFullTestName(fullName);
assertNotNull(proxy);
assertTrue(proxy.isDefect());
assertFalse(proxy.isInProgress());
}
public void testOnTestTruncatedComparisonFailure() {
onTestStarted("some_test");
myEventsProcessor.onTestFailure(new TestFailedEvent("some_test", "", "", false, "actual", null));