From be895ed80c39f40040c70e8b7e7e24cb29434200 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 9 Sep 2014 13:39:35 +0400 Subject: [PATCH] navigate through test failures without closing diff view (IDEA-119967) --- .../states/TestComparisionFailedState.java | 12 ++- .../testframework/AbstractTestProxy.java | 12 +++ .../execution/testframework/TestTreeView.java | 8 +- .../actions/ViewAssertEqualsDiffAction.java | 68 ++++++++++++++- .../stacktrace/DiffHyperlink.java | 83 +++++++++++++++++-- .../junit2/states/ComparisonFailureState.java | 17 +++- .../testng/model/TestProxy.java | 12 ++- 7 files changed, 199 insertions(+), 13 deletions(-) diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/states/TestComparisionFailedState.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/states/TestComparisionFailedState.java index 2d500115c038..461001d3b111 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/states/TestComparisionFailedState.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/states/TestComparisionFailedState.java @@ -30,7 +30,7 @@ import org.jetbrains.annotations.Nullable; /** * @author Roman.Chernyatchik */ -public class TestComparisionFailedState extends TestFailedState implements AbstractTestProxy.AssertEqualsDiffViewerProvider { +public class TestComparisionFailedState extends TestFailedState implements AbstractTestProxy.AssertEqualsMultiDiffViewProvider { private final String myErrorMsgPresentation; private final String myStacktracePresentation; private DiffHyperlink myHyperlink; @@ -77,4 +77,14 @@ public class TestComparisionFailedState extends TestFailedState implements Abstr public String getActual() { return myHyperlink.getRight(); } + + @Override + public void openMultiDiff(Project project, AbstractTestProxy.AssertEqualsDiffChain chain) { + myHyperlink.openMultiDiff(project, chain); + } + + @Override + public String getFilePath() { + return myHyperlink.getFilePath(); + } } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/AbstractTestProxy.java b/platform/testRunner/src/com/intellij/execution/testframework/AbstractTestProxy.java index 73ecdb2ddd8c..7b84c66ae2ac 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/AbstractTestProxy.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/AbstractTestProxy.java @@ -143,4 +143,16 @@ public abstract class AbstractTestProxy extends CompositePrintable { String getExpected(); String getActual(); } + + public interface AssertEqualsDiffChain { + AssertEqualsMultiDiffViewProvider getPrevious(); + AssertEqualsMultiDiffViewProvider getCurrent(); + AssertEqualsMultiDiffViewProvider getNext(); + void setCurrent(AssertEqualsMultiDiffViewProvider provider); + } + + public interface AssertEqualsMultiDiffViewProvider extends AssertEqualsDiffViewerProvider { + void openMultiDiff(Project project, AssertEqualsDiffChain chain); + String getFilePath(); + } } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java b/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java index bdbee84e49bd..5d1d2b346a85 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java @@ -46,6 +46,8 @@ import java.util.ArrayList; import java.util.List; public abstract class TestTreeView extends Tree implements DataProvider, CopyProvider { + public static final DataKey MODEL_DATA_KEY = DataKey.create("testFrameworkModel.dataId"); + private TestFrameworkRunningModel myModel; protected abstract TreeCellRenderer getRenderer(TestConsoleProperties properties); @@ -127,7 +129,11 @@ public abstract class TestTreeView extends Tree implements DataProvider, CopyPro return locations.isEmpty() ? null : locations.toArray(new Location[locations.size()]); } } - + + if (MODEL_DATA_KEY.is(dataId)) { + return myModel; + } + final TreePath selectionPath = getSelectionPath(); if (selectionPath == null) return null; final AbstractTestProxy testProxy = getSelectedTest(selectionPath); diff --git a/platform/testRunner/src/com/intellij/execution/testframework/actions/ViewAssertEqualsDiffAction.java b/platform/testRunner/src/com/intellij/execution/testframework/actions/ViewAssertEqualsDiffAction.java index ab093eb1bb5a..5d19c5ee0877 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/actions/ViewAssertEqualsDiffAction.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/actions/ViewAssertEqualsDiffAction.java @@ -16,12 +16,13 @@ package com.intellij.execution.testframework.actions; -import com.intellij.execution.testframework.AbstractTestProxy; -import com.intellij.execution.testframework.TestTreeViewAction; +import com.intellij.execution.testframework.*; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NonNls; -import javax.swing.*; +import java.util.ArrayList; +import java.util.List; public class ViewAssertEqualsDiffAction extends AnAction implements TestTreeViewAction { @NonNls public static final String ACTION_ID = "openAssertEqualsDiff"; @@ -31,11 +32,35 @@ public class ViewAssertEqualsDiffAction extends AnAction implements TestTreeView if (testProxy != null) { final AbstractTestProxy.AssertEqualsDiffViewerProvider diffViewerProvider = testProxy.getDiffViewerProvider(); if (diffViewerProvider != null) { - diffViewerProvider.openDiff(CommonDataKeys.PROJECT.getData(e.getDataContext())); + final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); + if (diffViewerProvider instanceof AbstractTestProxy.AssertEqualsMultiDiffViewProvider) { + final TestFrameworkRunningModel runningModel = TestTreeView.MODEL_DATA_KEY.getData(e.getDataContext()); + final List providers = collectAvailableProviders(runningModel); + final MyAssertEqualsDiffChain diffChain = + providers.size() > 1 ? new MyAssertEqualsDiffChain(providers, (AbstractTestProxy.AssertEqualsMultiDiffViewProvider)diffViewerProvider) : null; + ((AbstractTestProxy.AssertEqualsMultiDiffViewProvider)diffViewerProvider).openMultiDiff(project, diffChain); + } else { + diffViewerProvider.openDiff(project); + } } } } + private static List collectAvailableProviders(TestFrameworkRunningModel model) { + final List providers = new ArrayList(); + if (model != null) { + final AbstractTestProxy root = model.getRoot(); + final List allTests = root.getAllTests(); + for (AbstractTestProxy test : allTests) { + final AbstractTestProxy.AssertEqualsDiffViewerProvider provider = test.getDiffViewerProvider(); + if (provider instanceof AbstractTestProxy.AssertEqualsMultiDiffViewProvider) { + providers.add((AbstractTestProxy.AssertEqualsMultiDiffViewProvider)provider); + } + } + } + return providers; + } + public void update(final AnActionEvent e) { final Presentation presentation = e.getPresentation(); final boolean enabled; @@ -55,4 +80,39 @@ public class ViewAssertEqualsDiffAction extends AnAction implements TestTreeView presentation.setEnabled(enabled); presentation.setVisible(enabled); } + + private static class MyAssertEqualsDiffChain implements AbstractTestProxy.AssertEqualsDiffChain { + + + private final List myProviders; + private AbstractTestProxy.AssertEqualsMultiDiffViewProvider myProvider; + + public MyAssertEqualsDiffChain(List providers, + AbstractTestProxy.AssertEqualsMultiDiffViewProvider provider) { + myProviders = providers; + myProvider = provider; + } + + @Override + public AbstractTestProxy.AssertEqualsMultiDiffViewProvider getPrevious() { + final int prevIdx = (myProviders.size() + myProviders.indexOf(myProvider) - 1) % myProviders.size(); + return myProviders.get(prevIdx); + } + + @Override + public AbstractTestProxy.AssertEqualsMultiDiffViewProvider getCurrent() { + return myProvider; + } + + @Override + public AbstractTestProxy.AssertEqualsMultiDiffViewProvider getNext() { + final int nextIdx = (myProviders.indexOf(myProvider) + 1) % myProviders.size(); + return myProviders.get(nextIdx); + } + + @Override + public void setCurrent(AbstractTestProxy.AssertEqualsMultiDiffViewProvider provider) { + myProvider = provider; + } + } } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/stacktrace/DiffHyperlink.java b/platform/testRunner/src/com/intellij/execution/testframework/stacktrace/DiffHyperlink.java index b60770692d9b..34ab07903c38 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/stacktrace/DiffHyperlink.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/stacktrace/DiffHyperlink.java @@ -22,14 +22,23 @@ package com.intellij.execution.testframework.stacktrace; import com.intellij.execution.ExecutionBundle; import com.intellij.execution.filters.HyperlinkInfo; +import com.intellij.execution.testframework.AbstractTestProxy; import com.intellij.execution.testframework.Printable; import com.intellij.execution.testframework.Printer; import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.diff.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.io.File; public class DiffHyperlink implements Printable { @@ -56,21 +65,85 @@ public class DiffHyperlink implements Printable { myPrintOneLine = printOneLine; } - public void openDiff(final Project project) { + public void openDiff(Project project) { + openMultiDiff(project, null); + } + + public void openMultiDiff(final Project project, + final AbstractTestProxy.AssertEqualsDiffChain chain) { + final SimpleDiffRequest diffData = createRequest(project, chain, myFilePath, myExpected, myActual); + DiffManager.getInstance().getIdeaDiffTool().show(diffData); + } + + private SimpleDiffRequest createRequest(final Project project, + final AbstractTestProxy.AssertEqualsDiffChain chain, + String filePath, String expected, String actual) { String expectedTitle = ExecutionBundle.message("diff.content.expected.title"); final DiffContent expectedContent; final VirtualFile vFile; - if (myFilePath != null && (vFile = LocalFileSystem.getInstance().findFileByPath(myFilePath)) != null) { + if (filePath != null && (vFile = LocalFileSystem.getInstance().findFileByPath(filePath)) != null) { expectedContent = DiffContent.fromFile(project, vFile); expectedTitle += " (" + vFile.getPresentableUrl() + ")"; - } else expectedContent = new SimpleContent(myExpected); + } else { + expectedContent = new SimpleContent(expected); + } final SimpleDiffRequest diffData = new SimpleDiffRequest(project, getTitle()); - diffData.setContents(expectedContent, new SimpleContent(myActual)); + if (chain != null) { + diffData.setToolbarAddons(new DiffRequest.ToolbarAddons() { + @Override + public void customize(DiffToolbar toolbar) { + toolbar.addAction(new NextPrevAction("Compare Previous Failure", AllIcons.Actions.Prevfile, chain) { + @Override + protected AbstractTestProxy.AssertEqualsMultiDiffViewProvider getNextId() { + return chain.getPrevious(); + } + }); + toolbar.addAction(new NextPrevAction("Compare Next Failure", AllIcons.Actions.Nextfile, chain) { + @Override + protected AbstractTestProxy.AssertEqualsMultiDiffViewProvider getNextId() { + return chain.getNext(); + } + }); + } + }); + } + diffData.setContents(expectedContent, new SimpleContent(actual)); diffData.setContentTitles(expectedTitle, ExecutionBundle.message("diff.content.actual.title")); diffData.addHint(DiffTool.HINT_SHOW_FRAME); diffData.addHint(DiffTool.HINT_DO_NOT_IGNORE_WHITESPACES); diffData.setGroupKey("#com.intellij.execution.junit2.states.ComparisonFailureState$DiffDialog"); - DiffManager.getInstance().getIdeaDiffTool().show(diffData); + return diffData; + } + + abstract class NextPrevAction extends AnAction { + + private final AbstractTestProxy.AssertEqualsDiffChain myChain; + + public NextPrevAction(@Nullable String text, @Nullable Icon icon, + final AbstractTestProxy.AssertEqualsDiffChain chain) { + super(text, text, icon); + myChain = chain; + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + final DiffViewer viewer = e.getData(PlatformDataKeys.DIFF_VIEWER); + final Project project = e.getData(CommonDataKeys.PROJECT); + final AbstractTestProxy.AssertEqualsMultiDiffViewProvider nextProvider = getNextId(); + myChain.setCurrent(nextProvider); + final SimpleDiffRequest nextRequest = + createRequest(project, myChain, nextProvider.getFilePath(), nextProvider.getExpected(), nextProvider.getActual()); + viewer.setDiffRequest(nextRequest); + } + + @Override + public void update(@NotNull AnActionEvent e) { + final DiffViewer viewer = e.getData(PlatformDataKeys.DIFF_VIEWER); + final Project project = e.getData(CommonDataKeys.PROJECT); + e.getPresentation().setEnabled(project != null && viewer != null); + } + + protected abstract AbstractTestProxy.AssertEqualsMultiDiffViewProvider getNextId(); } protected String getTitle() { diff --git a/plugins/junit/src/com/intellij/execution/junit2/states/ComparisonFailureState.java b/plugins/junit/src/com/intellij/execution/junit2/states/ComparisonFailureState.java index d4ef1319d866..c772936f5d20 100644 --- a/plugins/junit/src/com/intellij/execution/junit2/states/ComparisonFailureState.java +++ b/plugins/junit/src/com/intellij/execution/junit2/states/ComparisonFailureState.java @@ -24,7 +24,7 @@ import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NonNls; -public class ComparisonFailureState extends FaultyState implements AbstractTestProxy.AssertEqualsDiffViewerProvider { +public class ComparisonFailureState extends FaultyState implements AbstractTestProxy.AssertEqualsMultiDiffViewProvider { private DiffHyperlink myHyperlink; @NonNls protected static final String EXPECTED_VALUE_MESSAGE_TEXT = "expected:<"; @@ -59,4 +59,19 @@ public class ComparisonFailureState extends FaultyState implements AbstractTestP public void openDiff(final Project project) { if (myHyperlink != null) myHyperlink.openDiff(project); } + + @Override + public void openMultiDiff(Project project, AbstractTestProxy.AssertEqualsDiffChain chain) { + if (myHyperlink != null) { + myHyperlink.openMultiDiff(project, chain); + } + } + + @Override + public String getFilePath() { + if (myHyperlink != null) { + return myHyperlink.getFilePath(); + } + return null; + } } diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestProxy.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestProxy.java index a7d29ce4de98..b5e24707bba7 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/model/TestProxy.java +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestProxy.java @@ -292,7 +292,7 @@ public class TestProxy extends AbstractTestProxy { if (myHyperlink == null) { return null; } - return new AssertEqualsDiffViewerProvider() { + return new AssertEqualsMultiDiffViewProvider() { @Override public void openDiff(Project project) { myHyperlink.openDiff(project); @@ -307,6 +307,16 @@ public class TestProxy extends AbstractTestProxy { public String getActual() { return myHyperlink.getRight(); } + + @Override + public void openMultiDiff(Project project, AssertEqualsDiffChain chain) { + myHyperlink.openMultiDiff(project, chain); + } + + @Override + public String getFilePath() { + return myHyperlink.getFilePath(); + } }; }