diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/junit/tests/TestSorting.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/junit/tests/TestSorting.java new file mode 100644 index 0000000..42d0e9f --- /dev/null +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/junit/tests/TestSorting.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2014 Sandra Lions and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sandra Lions - [JUnit] allow to sort by name and by execution time - https://bugs.eclipse.org/bugs/show_bug.cgi?id=219466 + *******************************************************************************/ +package org.eclipse.jdt.junit.tests; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.junit.JUnitCore; +import org.eclipse.jdt.junit.TestRunListener; + +import org.eclipse.swt.widgets.Table; + +import org.eclipse.jface.viewers.TableViewer; + +import org.eclipse.ui.IWorkbenchPage; + +import org.eclipse.jdt.core.IType; + +import org.eclipse.jdt.internal.junit.ui.JUnitPlugin; +import org.eclipse.jdt.internal.junit.ui.TestRunnerViewPart; +import org.eclipse.jdt.internal.junit.ui.TestRunnerViewPart.SortingCriterion; + +public class TestSorting extends AbstractTestRunListenerTest { + + private String[] runSequenceTest(IType typeToLaunch) throws Exception { + TestRunLog log= new TestRunLog(); + final TestRunListener testRunListener= new TestRunListeners.SequenceTest(log); + JUnitCore.addTestRunListener(testRunListener); + try { + return launchJUnit(typeToLaunch, log); + } finally { + JUnitCore.removeTestRunListener(testRunListener); + } + } + + public void testSorting() throws Exception { + + IWorkbenchPage activePage= JUnitPlugin.getActivePage(); + TestRunnerViewPart testRunnerViewPart= (TestRunnerViewPart)activePage.showView(TestRunnerViewPart.NAME); + testRunnerViewPart.setLayoutMode(TestRunnerViewPart.LAYOUT_FLAT); // TableViewer + + String source= "package pack;\n" + + "import junit.framework.TestCase;\n" + + "public class ATestCase extends TestCase {\n" + + " private String fString;\n" + + " public void testB_FirstTest() throws Exception {\n" + + " fString= \"first\";\n" + + " Thread.sleep(30);\n" + + " }\n" + + " public void testa_SecondTest() throws Exception {\n" + + " fString= \"second\";\n" + + " Thread.sleep(50);\n" + + " }\n" + + " public void testC_ThirdTest() throws Exception {\n" + + " fString= \"second\";\n" + + " Thread.sleep(50);\n" + + " }\n" + + " public void testA_FourthTest() throws Exception {\n" + + " fString= \"third\";\n" + + " Thread.sleep(40);\n" + + " }\n" + + "}"; + + IType aTestCase= createType(source, "pack", "ATestCase.java"); + runSequenceTest(aTestCase); + + Table table= ((TableViewer)testRunnerViewPart.getTestViewer().getActiveViewer()).getTable(); + assertEquals(4, table.getItemCount()); + + List testResults; + + assertTrue(testRunnerViewPart.getTestRunSession().isRunning() == false); + testRunnerViewPart.setSortingCriterion(SortingCriterion.SORT_BY_NAME); + testResults= new ArrayList(); + for (int i= 0; i < table.getItems().length; i++) { + String text= table.getItems()[i].getText(); + testResults.add(i, text.substring(0, text.indexOf("_"))); + } + assertTrue(Arrays.deepEquals(new String[] { "testA", "testa", "testB", "testC" }, testResults.toArray())); + + testRunnerViewPart.setSortingCriterion(SortingCriterion.SORT_BY_EXECUTION_TIME); + testResults= new ArrayList(); + for (int i= 0; i < table.getItems().length; i++) { + String text= table.getItems()[i].getText(); + testResults.add(0, text.substring(text.indexOf("(") + 1, text.length())); + } + String previousResult= null; + for (int i= 0; i < testResults.size(); i++) { + String testResult= testResults.get(i); + if (previousResult != null) { + assertTrue(previousResult.compareTo(testResult) <= 0); + } + previousResult= testResult; + } + } +} diff --git a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitMessages.java b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitMessages.java index f3ec487..a39438e 100644 --- a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitMessages.java +++ b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitMessages.java @@ -9,6 +9,7 @@ * IBM Corporation - initial API and implementation * David Saff (saff@mit.edu) - bug 102632: [JUnit] Support for JUnit 4. * Robert Konigsberg - [JUnit] Improve discoverability of the ability to run a single method under JUnit Tests - https://bugs.eclipse.org/bugs/show_bug.cgi?id=285637 + * Sandra Lions - [JUnit] allow to sort by name and by execution time - https://bugs.eclipse.org/bugs/show_bug.cgi?id=219466 *******************************************************************************/ package org.eclipse.jdt.internal.junit.ui; @@ -297,6 +298,11 @@ public final class JUnitMessages extends NLS { public static String TestRunnerViewPart_show_failures_only; public static String TestRunnerViewPart_hierarchical_layout; + public static String TestRunnerViewPart_sort_by_menu; + public static String TestRunnerViewPart_toggle_name_label; + public static String TestRunnerViewPart_toggle_execution_order_label; + public static String TestRunnerViewPart_toggle_execution_time_label; + public static String TestSessionLabelProvider_testName_elapsedTimeInSeconds; public static String TestSessionLabelProvider_testName_JUnitVersion; diff --git a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitMessages.properties b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitMessages.properties index 3f98342..b1d2d9c 100644 --- a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitMessages.properties +++ b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitMessages.properties @@ -8,6 +8,7 @@ # Contributors: # IBM Corporation - initial API and implementation # Robert Konigsberg - [JUnit] Improve discoverability of the ability to run a single method under JUnit Tests - https://bugs.eclipse.org/bugs/show_bug.cgi?id=285637 +# Sandra Lions - [JUnit] allow to sort by name and by execution time - https://bugs.eclipse.org/bugs/show_bug.cgi?id=219466 ############################################################################### CopyTrace_action_label=Copy Trace CopyTraceAction_problem=Problem Copying to Clipboard @@ -75,6 +76,10 @@ TestRunnerViewPart_select_test_run=&Select a test run: TestRunnerViewPart_stopaction_tooltip=Stop JUnit Test Run TestRunnerViewPart_show_execution_time=Show Execution &Time TestRunnerViewPart_show_failures_only=Show &Failures Only +TestRunnerViewPart_sort_by_menu=&Sort By +TestRunnerViewPart_toggle_name_label=&Name +TestRunnerViewPart_toggle_execution_order_label=Execution &Order +TestRunnerViewPart_toggle_execution_time_label=Execution &Time TestRunnerViewPart_rerunaction_label=Rerun Test TestRunnerViewPart_rerunaction_tooltip=Rerun Test TestRunnerViewPart_hierarchical_layout=Show Tests in &Hierarchy diff --git a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestRunnerViewPart.java b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestRunnerViewPart.java index b2d691f..cac6456 100644 --- a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestRunnerViewPart.java +++ b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestRunnerViewPart.java @@ -16,6 +16,7 @@ * Andrew Eisenberg - [JUnit] Rerun failed first does not work with JUnit4 - https://bugs.eclipse.org/bugs/show_bug.cgi?id=140392 * Thirumala Reddy Mutchukota - [JUnit] Avoid rerun test launch on UI thread - https://bugs.eclipse.org/bugs/show_bug.cgi?id=411841 * Andrew Eisenberg - [JUnit] Add a monospace font option for the junit results view - https://bugs.eclipse.org/bugs/show_bug.cgi?id=411794 + * Sandra Lions - [JUnit] allow to sort by name and by execution time - https://bugs.eclipse.org/bugs/show_bug.cgi?id=219466 *******************************************************************************/ package org.eclipse.jdt.internal.junit.ui; @@ -163,8 +164,8 @@ public class TestRunnerViewPart extends ViewPart { static final int REFRESH_INTERVAL= 200; - static final int LAYOUT_FLAT= 0; - static final int LAYOUT_HIERARCHICAL= 1; + public static final int LAYOUT_FLAT= 0; + public static final int LAYOUT_HIERARCHICAL= 1; /** * Whether the output scrolls and reveals tests as they are executed. @@ -203,6 +204,14 @@ public class TestRunnerViewPart extends ViewPart { */ private boolean fIsDisposed= false; + public enum SortingCriterion { + SORT_BY_NAME, SORT_BY_EXECUTION_ORDER, SORT_BY_EXECUTION_TIME + } + /** + * The current sorting criterion. + */ + private SortingCriterion fSortingCriterion= SortingCriterion.SORT_BY_EXECUTION_ORDER; + /** * Actions */ @@ -225,6 +234,9 @@ public class TestRunnerViewPart extends ViewPart { private ShowTimeAction fShowTimeAction; private ActivateOnErrorAction fActivateOnErrorAction; private IMenuListener fViewMenuListener; + + private MenuManager fSortByMenu; + private ToggleSortingAction[] fToggleSortingActions; private TestRunSession fTestRunSession; private TestSessionListener fTestSessionListener; @@ -279,6 +291,8 @@ public class TestRunnerViewPart extends ViewPart { */ static final String TAG_SHOW_TIME= "time"; //$NON-NLS-1$ + static final String TAG_SORTING_CRITERION= "SortingCriterion"; //$NON-NLS-1$ + /** * @since 3.5 */ @@ -706,6 +720,12 @@ public class TestRunnerViewPart extends ViewPart { fStopAction.setEnabled(true); fRerunLastTestAction.setEnabled(true); + // While tests are running, always use the execution order + getDisplay().asyncExec(new Runnable() { + public void run() { + fTestViewer.setSortingCriterion(SortingCriterion.SORT_BY_EXECUTION_ORDER); + } + }); } public void sessionEnded(long elapsedTime){ @@ -735,6 +755,13 @@ public class TestRunnerViewPart extends ViewPart { } }); stopUpdateJobs(); + + // When test session ended, apply user sorting criterion + getDisplay().asyncExec(new Runnable() { + public void run() { + setSortingCriterion(fSortingCriterion); + } + }); } public void sessionStopped(final long elapsedTime) { @@ -961,6 +988,50 @@ public class TestRunnerViewPart extends ViewPart { } } + private class ToggleSortingAction extends Action { + private final SortingCriterion fActionSortingCriterion; + + public ToggleSortingAction(SortingCriterion sortingCriterion) { + super("", AS_RADIO_BUTTON); //$NON-NLS-1$ + switch (sortingCriterion) { + case SORT_BY_NAME: + setText(JUnitMessages.TestRunnerViewPart_toggle_name_label); + break; + case SORT_BY_EXECUTION_ORDER: + setText(JUnitMessages.TestRunnerViewPart_toggle_execution_order_label); + break; + case SORT_BY_EXECUTION_TIME: + setText(JUnitMessages.TestRunnerViewPart_toggle_execution_time_label); + break; + default: + break; + } + fActionSortingCriterion= sortingCriterion; + } + + @Override + public void run() { + if (isChecked()) { + setSortingCriterion(fActionSortingCriterion); + } + } + + public SortingCriterion getActionSortingCriterion() { + return fActionSortingCriterion; + } + } + + public SortingCriterion getSortingCriterion() { + return fSortingCriterion; + } + + public void setSortingCriterion(SortingCriterion sortingCriterion) { + fSortingCriterion= sortingCriterion; + if (fTestRunSession != null && fTestRunSession.isRunning() == false) { + fTestViewer.setSortingCriterion(sortingCriterion); + } + } + /** * Listen for for modifications to Java elements */ @@ -1145,6 +1216,7 @@ public class TestRunnerViewPart extends ViewPart { memento.putString(TAG_FAILURES_ONLY, fFailuresOnlyFilterAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ memento.putInteger(TAG_LAYOUT, fLayout); memento.putString(TAG_SHOW_TIME, fShowTimeAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ + memento.putString(TAG_SORTING_CRITERION, fSortingCriterion.name()); } private void restoreLayoutState(IMemento memento) { @@ -1183,6 +1255,16 @@ public class TestRunnerViewPart extends ViewPart { boolean showTime= true; if (time != null) showTime= time.equals("true"); //$NON-NLS-1$ + + SortingCriterion sortingCriterion= SortingCriterion.SORT_BY_EXECUTION_ORDER; + String tagSortingCriterion= memento.getString(TAG_SORTING_CRITERION); + if (tagSortingCriterion != null) { + sortingCriterion= Enum.valueOf(SortingCriterion.class, tagSortingCriterion); + } + setSortingCriterion(sortingCriterion); + for (int i= 0; i < fToggleSortingActions.length; i++) { + fToggleSortingActions[i].setChecked(sortingCriterion == fToggleSortingActions[i].getActionSortingCriterion()); + } setFilterAndLayout(showFailuresOnly, layoutValue); setShowExecutionTime(showTime); @@ -1507,12 +1589,14 @@ action enablement startUpdateJobs(); fStopAction.setEnabled(true); + fTestViewer.setSortingCriterion(SortingCriterion.SORT_BY_EXECUTION_ORDER); } else /* old or fresh session: don't want jobs at this stage */ { stopUpdateJobs(); fStopAction.setEnabled(fTestRunSession.isKeptAlive()); fTestViewer.expandFirstLevel(); + setSortingCriterion(fSortingCriterion); } } return deactivatedSession; @@ -1942,6 +2026,17 @@ action enablement viewMenu.add(fShowTestHierarchyAction); viewMenu.add(fShowTimeAction); viewMenu.add(new Separator()); + fToggleSortingActions= + new ToggleSortingAction[] { + new ToggleSortingAction(SortingCriterion.SORT_BY_EXECUTION_ORDER), + new ToggleSortingAction(SortingCriterion.SORT_BY_EXECUTION_TIME), + new ToggleSortingAction(SortingCriterion.SORT_BY_NAME)}; + fSortByMenu= new MenuManager(JUnitMessages.TestRunnerViewPart_sort_by_menu); + for (int i= 0; i < fToggleSortingActions.length; ++i) { + fSortByMenu.add(fToggleSortingActions[i]); + } + viewMenu.add(fSortByMenu); + viewMenu.add(new Separator()); MenuManager layoutSubMenu= new MenuManager(JUnitMessages.TestRunnerViewPart_layout_menu); for (int i = 0; i < fToggleOrientationActions.length; ++i) { @@ -2174,12 +2269,19 @@ action enablement return fFailureTrace; } + public TestViewer getTestViewer() { + return fTestViewer; + } + + public TestRunSession getTestRunSession() { + return fTestRunSession; + } void setShowFailuresOnly(boolean failuresOnly) { setFilterAndLayout(failuresOnly, fLayout); } - private void setLayoutMode(int mode) { + public void setLayoutMode(int mode) { setFilterAndLayout(fFailuresOnlyFilterAction.isChecked(), mode); } diff --git a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestViewer.java b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestViewer.java index 3ab858c..423d015 100644 --- a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestViewer.java +++ b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestViewer.java @@ -10,13 +10,14 @@ * Brock Janiczak (brockj@tpg.com.au) * - https://bugs.eclipse.org/bugs/show_bug.cgi?id=102236: [JUnit] display execution time next to each test * Xavier Coulon - https://bugs.eclipse.org/bugs/show_bug.cgi?id=102512 - [JUnit] test method name cut off before ( - + * Sandra Lions - [JUnit] allow to sort by name and by execution time - https://bugs.eclipse.org/bugs/show_bug.cgi?id=219466 *******************************************************************************/ package org.eclipse.jdt.internal.junit.ui; import java.util.AbstractList; import java.util.Arrays; +import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -48,6 +49,7 @@ import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.ui.IWorkbenchActionConstants; @@ -65,6 +67,7 @@ import org.eclipse.jdt.internal.junit.model.TestElement.Status; import org.eclipse.jdt.internal.junit.model.TestRoot; import org.eclipse.jdt.internal.junit.model.TestRunSession; import org.eclipse.jdt.internal.junit.model.TestSuiteElement; +import org.eclipse.jdt.internal.junit.ui.TestRunnerViewPart.SortingCriterion; import org.eclipse.jdt.internal.ui.viewsupport.ColoringLabelProvider; import org.eclipse.jdt.internal.ui.viewsupport.SelectionProviderMediator; @@ -324,6 +327,80 @@ public class TestViewer { } } + public synchronized void setSortingCriterion(SortingCriterion sortingCriterion) { + ViewerComparator viewComparator; + switch (sortingCriterion) { + case SORT_BY_EXECUTION_ORDER: + viewComparator= null; + break; + case SORT_BY_EXECUTION_TIME: + viewComparator= new TestExecutionTimeComparator(); + break; + case SORT_BY_NAME: + viewComparator= new TestNameComparator(); + break; + default: + viewComparator= null; + break; + } + fTableViewer.setComparator(viewComparator); + fTreeViewer.setComparator(viewComparator); + } + + private final class TestNameComparator extends ViewerComparator { + @Override + public int compare(Viewer viewer, Object o1, Object o2) { + return compareName(o1, o2); + } + } + + private final class TestExecutionTimeComparator extends ViewerComparator { + @Override + public int compare(Viewer viewer, Object o1, Object o2) { + return compareElapsedTime(o1, o2); + } + } + + private Comparator getComparator() { + SortingCriterion sortingCriterion= fTestRunnerPart.getSortingCriterion(); + Comparator comparator; + switch (sortingCriterion) { + case SORT_BY_EXECUTION_ORDER: + comparator= null; + break; + case SORT_BY_EXECUTION_TIME: + comparator= new Comparator() { + public int compare(ITestElement o1, ITestElement o2) { + return compareElapsedTime(o1, o2); + } + }; + break; + case SORT_BY_NAME: + comparator= new Comparator() { + public int compare(ITestElement o1, ITestElement o2) { + return compareName(o1, o2); + } + }; + break; + default: + comparator= null; + break; + } + return comparator; + } + + private int compareElapsedTime(Object o1, Object o2) { + double elapsedTime1= ((TestElement)o1).getElapsedTimeInSeconds(); + double elapsedTime2= ((TestElement)o2).getElapsedTimeInSeconds(); + return Double.compare(elapsedTime2, elapsedTime1); + } + + private int compareName(Object o1, Object o2) { + String testName1= ((TestElement)o1).getTestName(); + String testName2= ((TestElement)o2).getTestName(); + return testName1.toLowerCase().compareTo(testName2.toLowerCase()); + } + public synchronized void setShowFailuresOnly(boolean failuresOnly, int layoutMode) { /* * Management of fTreeViewer and fTableViewer @@ -399,7 +476,7 @@ public class TestViewer { fTableHasFilter= filter; } - private StructuredViewer getActiveViewer() { + public StructuredViewer getActiveViewer() { if (fLayoutMode == TestRunnerViewPart.LAYOUT_HIERARCHICAL) return fTreeViewer; else @@ -613,7 +690,12 @@ public class TestViewer { if (parent == null) return null; - List siblings= Arrays.asList(parent.getChildren()); + ITestElement[] elements= parent.getChildren(); + Comparator comparator= getComparator(); + if (comparator != null) { + Arrays.sort(elements, comparator); + } + List siblings= Arrays.asList(elements); if (! showNext) siblings= new ReverseList(siblings); @@ -632,7 +714,12 @@ public class TestViewer { } private TestCaseElement getNextChildFailure(TestSuiteElement root, boolean showNext) { - List children= Arrays.asList(root.getChildren()); + ITestElement[] elements= root.getChildren(); + Comparator comparator= getComparator(); + if (comparator != null) { + Arrays.sort(elements, comparator); + } + List children= Arrays.asList(elements); if (! showNext) children= new ReverseList(children); for (int i= 0; i < children.size(); i++) { diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/junit/tests/JUnitJUnitTests.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/junit/tests/JUnitJUnitTests.java index ca3ef8b..e2c5619 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/junit/tests/JUnitJUnitTests.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/junit/tests/JUnitJUnitTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2010 IBM Corporation and others. + * Copyright (c) 2005, 2014 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Sandra Lions - [JUnit] allow to sort by name and by execution time - https://bugs.eclipse.org/bugs/show_bug.cgi?id=219466 *******************************************************************************/ package org.eclipse.jdt.junit.tests; @@ -37,6 +38,9 @@ public class JUnitJUnitTests { suite.addTestSuite(JUnit3TestFinderTest.class); suite.addTestSuite(JUnit4TestFinderTest.class); + + suite.addTestSuite(TestSorting.class); + //$JUnit-END$ return suite; }