Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Rubezhny2020-10-22 11:44:36 +0000
committerMickael Istria2020-11-09 16:04:59 +0000
commit9a809a15352d02bb22dbbdcf5a088ee445b5819a (patch)
treea57109489c611488e802fa77ad98f8148e57249f
parent5bb7e72122e0bb02d75efe535220e8ee71e01372 (diff)
downloadeclipse.platform.debug-9a809a15352d02bb22dbbdcf5a088ee445b5819a.tar.gz
eclipse.platform.debug-9a809a15352d02bb22dbbdcf5a088ee445b5819a.tar.xz
eclipse.platform.debug-9a809a15352d02bb22dbbdcf5a088ee445b5819a.zip
Bug 507626: Debug framework should provide a generic "test report" viewY20201109-1200
A Unit Test View implementation based on refactored JUnit Test View Also-By: Mickael Istria <mistria@redhat.com> Also-By: Alexander Kurtakov <akurtako@redhat.com> Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com> Change-Id: Id971613f3f33030343950c539d09a02192cc7ecf
-rw-r--r--org.eclipse.unittest.ui/.classpath11
-rw-r--r--org.eclipse.unittest.ui/.project34
-rw-r--r--org.eclipse.unittest.ui/.settings/org.eclipse.core.resources.prefs2
-rw-r--r--org.eclipse.unittest.ui/.settings/org.eclipse.core.runtime.prefs2
-rw-r--r--org.eclipse.unittest.ui/.settings/org.eclipse.jdt.core.prefs538
-rw-r--r--org.eclipse.unittest.ui/.settings/org.eclipse.jdt.ui.prefs84
-rw-r--r--org.eclipse.unittest.ui/.settings/org.eclipse.pde.api.tools.prefs102
-rw-r--r--org.eclipse.unittest.ui/.settings/org.eclipse.pde.prefs36
-rw-r--r--org.eclipse.unittest.ui/META-INF/MANIFEST.MF21
-rw-r--r--org.eclipse.unittest.ui/about.html36
-rw-r--r--org.eclipse.unittest.ui/build.properties25
-rw-r--r--org.eclipse.unittest.ui/icons-work/unitViewIcon.xcfbin0 -> 4483 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/cfilter.pngbin0 -> 212 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/cfilter@2x.pngbin0 -> 348 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/compare.pngbin0 -> 245 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/compare@2x.pngbin0 -> 394 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout.pngbin0 -> 131 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout@2x.pngbin0 -> 180 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout.pngbin0 -> 154 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout@2x.pngbin0 -> 207 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/history_list.pngbin0 -> 378 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/history_list@2x.pngbin0 -> 792 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/lock.pngbin0 -> 422 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/lock@2x.pngbin0 -> 912 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/open_console.pngbin0 -> 430 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/open_console@2x.pngbin0 -> 889 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/relaunch.pngbin0 -> 536 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/relaunch@2x.pngbin0 -> 1216 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf.pngbin0 -> 562 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf@2x.pngbin0 -> 1232 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/select_next.pngbin0 -> 315 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/select_next@2x.pngbin0 -> 567 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/select_prev.pngbin0 -> 315 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/select_prev@2x.pngbin0 -> 538 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/stop.pngbin0 -> 257 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/stop@2x.pngbin0 -> 460 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic.pngbin0 -> 305 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic@2x.pngbin0 -> 599 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal.pngbin0 -> 312 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal@2x.pngbin0 -> 607 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical.pngbin0 -> 258 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical@2x.pngbin0 -> 426 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dtool16/new_testcase.pngbin0 -> 371 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dtool16/new_testcase@2x.pngbin0 -> 625 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite.pngbin0 -> 389 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite@2x.pngbin0 -> 678 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/cfilter.pngbin0 -> 365 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/cfilter@2x.pngbin0 -> 648 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/compare.pngbin0 -> 299 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/compare@2x.pngbin0 -> 591 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/flatLayout.pngbin0 -> 123 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/flatLayout@2x.pngbin0 -> 199 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout.pngbin0 -> 148 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout@2x.pngbin0 -> 230 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/history_list.pngbin0 -> 441 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/history_list@2x.pngbin0 -> 1117 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/lock.pngbin0 -> 541 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/lock@2x.pngbin0 -> 1316 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/open_console.pngbin0 -> 590 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/open_console@2x.pngbin0 -> 1251 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/relaunch.pngbin0 -> 671 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/relaunch@2x.pngbin0 -> 1664 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/relaunchf.pngbin0 -> 687 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/relaunchf@2x.pngbin0 -> 1643 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/select_next.pngbin0 -> 366 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/select_next@2x.pngbin0 -> 767 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/select_prev.pngbin0 -> 364 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/select_prev@2x.pngbin0 -> 730 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/stop.pngbin0 -> 301 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/stop@2x.pngbin0 -> 515 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/th_automatic.pngbin0 -> 383 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/th_automatic@2x.pngbin0 -> 822 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal.pngbin0 -> 383 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal@2x.pngbin0 -> 846 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/th_vertical.pngbin0 -> 315 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/elcl16/th_vertical@2x.pngbin0 -> 586 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/etool16/new_testcase.pngbin0 -> 493 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/etool16/new_testcase@2x.pngbin0 -> 989 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/etool16/new_testsuite.pngbin0 -> 484 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/etool16/new_testsuite@2x.pngbin0 -> 965 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/stackframe.pngbin0 -> 266 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/stackframe@2x.pngbin0 -> 557 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/unit.pngbin0 -> 980 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/unit@2x.pngbin0 -> 1300 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/uniterr.pngbin0 -> 994 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/uniterr@2x.pngbin0 -> 1366 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/uniterrq.pngbin0 -> 1098 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/uniterrq@2x.pngbin0 -> 1610 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/unitsucc.pngbin0 -> 1057 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/unitsucc@2x.pngbin0 -> 1489 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/unitsuccq.pngbin0 -> 1131 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/eview16/unitsuccq@2x.pngbin0 -> 1724 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/exc_catch.pngbin0 -> 377 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/exc_catch@2x.pngbin0 -> 824 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/faillist.pngbin0 -> 167 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/faillist@2x.pngbin0 -> 289 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/failures.pngbin0 -> 262 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/failures@2x.pngbin0 -> 487 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/julaunch.pngbin0 -> 240 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/julaunch@2x.pngbin0 -> 429 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj.pngbin0 -> 266 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj@2x.pngbin0 -> 557 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/test.pngbin0 -> 384 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/test@2x.pngbin0 -> 776 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed.pngbin0 -> 489 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed@2x.pngbin0 -> 1056 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testerr.pngbin0 -> 434 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testerr@2x.pngbin0 -> 925 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testfail.pngbin0 -> 422 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testfail@2x.pngbin0 -> 868 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testfile_obj.pngbin0 -> 492 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testfile_obj@2x.pngbin0 -> 990 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testhier.pngbin0 -> 390 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testhier@2x.pngbin0 -> 796 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testignored.pngbin0 -> 441 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testignored@2x.pngbin0 -> 922 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testok.pngbin0 -> 473 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testok@2x.pngbin0 -> 1052 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testrun.pngbin0 -> 482 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/testrun@2x.pngbin0 -> 1024 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/tsuite.pngbin0 -> 358 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/tsuite@2x.pngbin0 -> 701 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror.pngbin0 -> 447 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror@2x.pngbin0 -> 977 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/tsuitefail.pngbin0 -> 439 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/tsuitefail@2x.pngbin0 -> 942 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/tsuiteok.pngbin0 -> 473 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/tsuiteok@2x.pngbin0 -> 1067 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/tsuiterun.pngbin0 -> 464 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/obj16/tsuiterun@2x.pngbin0 -> 927 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/ovr16/error_ovr.pngbin0 -> 150 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/ovr16/error_ovr@2x.pngbin0 -> 247 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr.pngbin0 -> 155 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr@2x.pngbin0 -> 260 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/ovr16/success_ovr.pngbin0 -> 198 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/ovr16/success_ovr@2x.pngbin0 -> 399 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz.pngbin0 -> 3805 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz@2x.pngbin0 -> 8362 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz.pngbin0 -> 3448 bytes
-rw-r--r--org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz@2x.pngbin0 -> 7413 bytes
-rw-r--r--org.eclipse.unittest.ui/plugin.properties31
-rw-r--r--org.eclipse.unittest.ui/plugin.xml67
-rw-r--r--org.eclipse.unittest.ui/pom.xml27
-rw-r--r--org.eclipse.unittest.ui/schema/unittestViewSupport.exsd98
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPlugin.java98
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPreferencesConstants.java101
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/HistoryEntryHandler.java79
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/IXMLTags.java112
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunHandler.java365
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunSessionSerializer.java316
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestListenerRegistry.java66
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestRunListener.java106
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportExtension.java87
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportRegistry.java145
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/UnitTestLaunchConfigurationConstants.java37
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionListener.java37
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionReport.java50
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestSessionListener.java86
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.java47
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.properties31
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ProgressState.java39
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/Status.java182
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestCaseElement.java99
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestElement.java313
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunListenerAdapter.java110
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunSession.java749
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestSuiteElement.java287
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestLaunchListener.java88
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestModel.java287
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/BasicElementLabels.java135
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultDialog.java320
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultsAction.java74
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CopyFailureListAction.java96
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CounterPanel.java184
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/EnableStackFilterAction.java53
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTableDisplay.java103
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTraceUIBlock.java285
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ITraceDisplay.java28
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/IUnitTestHelpContextIds.java42
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Images.java117
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.java130
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.properties109
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ProgressIcons.java103
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/RerunAction.java58
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ScrollLockAction.java47
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/SelectionProviderMediator.java198
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowNextFailureAction.java43
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowPreviousFailureAction.java43
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowStackTraceInConsoleViewAction.java62
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestRunnerViewPart.java1823
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionLabelProvider.java186
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTableContentProvider.java61
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTreeContentProvider.java70
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestViewer.java871
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TextualTrace.java145
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UITestRunListener.java72
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestCopyAction.java110
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestProgressBar.java216
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestUIPreferencesConstants.java70
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/History.java143
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryDialog.java270
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryHandler.java80
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryItem.java324
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/Messages.java39
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/messages.properties22
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/launcher/ITestRunnerClient.java52
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestCaseElement.java40
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestElement.java184
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestRunSession.java140
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestSuiteElement.java35
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/model/package.html15
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ConfigureViewerSupport.java41
-rw-r--r--org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ITestViewSupport.java110
-rw-r--r--pom.xml1
214 files changed, 12381 insertions, 0 deletions
diff --git a/org.eclipse.unittest.ui/.classpath b/org.eclipse.unittest.ui/.classpath
new file mode 100644
index 000000000..4a00becd8
--- /dev/null
+++ b/org.eclipse.unittest.ui/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+ <attributes>
+ <attribute name="module" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.unittest.ui/.project b/org.eclipse.unittest.ui/.project
new file mode 100644
index 000000000..3b2e76cfb
--- /dev/null
+++ b/org.eclipse.unittest.ui/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.unittest.ui</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.core.resources.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 000000000..99f26c020
--- /dev/null
+++ b/org.eclipse.unittest.ui/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 000000000..5a0ad22d2
--- /dev/null
+++ b/org.eclipse.unittest.ui/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..5ab320324
--- /dev/null
+++ b/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,538 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.classpath.outputOverlappingAnotherSource=error
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=f
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=fg
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnull.secondary=
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary=
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=11
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.APILeak=warning
+org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=info
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=error
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=error
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=info
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=enabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=error
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=info
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=info
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=info
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=info
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning
+org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
+org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=info
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=11
+org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
+org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false
+org.eclipse.jdt.core.formatter.align_with_spaces=false
+org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assertion_message=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
+org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_record_components=16
+org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0
+org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
+org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_record_constructor=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_record_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=true
+org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
+org.eclipse.jdt.core.formatter.comment.indent_tag_description=false
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert
+org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.lineSplit=120
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.text_block_indentation=0
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
+org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
+org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 000000000..e1de5eec4
--- /dev/null
+++ b/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,84 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile
+formatter_settings_version=20
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;sun;com;org;org.apache;org.w3c;org.eclipse;org.eclipse.swt;org.eclipse.core;org.eclipse.core.runtime;org.eclipse.core.resources;org.eclipse.core.filebuffers;org.eclipse.text;org.eclipse.jface;org.eclipse.jface.text;org.eclipse.ui;org.eclipse.ui.workbench.texteditor;org.eclipse.ui.texteditor;org.eclipse.ui.editors;org.eclipse.compare;org.eclipse.debug;org.eclipse.debug.ui;org.eclipse.search;org.eclipse.search2;org.eclipse.ltk;org.eclipse.jdt.core;org.eclipse.jdt.internal;org.eclipse.jdt.launching;org.eclipse.jdt.ui;org.eclipse.jdt.internal.ui;
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=99
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=true
+sp_cleanup.convert_to_enhanced_for_loop=true
+sp_cleanup.convert_to_enhanced_for_loop_if_loop_var_used=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.lazy_logical_operator=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.merge_conditional_blocks=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.number_suffix=false
+sp_cleanup.objects_equals=false
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.precompile_regex=true
+sp_cleanup.push_down_negation=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_modifiers=true
+sp_cleanup.remove_redundant_semicolons=true
+sp_cleanup.remove_redundant_type_arguments=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_array_creation=true
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=true
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.simplify_lambda_expression_and_method_ref=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_autoboxing=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_directly_map_method=true
+sp_cleanup.use_lambda=true
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+sp_cleanup.use_unboxing=false
+sp_cleanup.use_var=false
diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 000000000..9d6e2dc79
--- /dev/null
+++ b/org.eclipse.unittest.ui/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,102 @@
+ANNOTATION_ELEMENT_TYPE_ADDED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_USE_SCAN_FIELD_SEVERITY=Error
+API_USE_SCAN_METHOD_SEVERITY=Error
+API_USE_SCAN_TYPE_SEVERITY=Error
+CLASS_ELEMENT_TYPE_ADDED_FIELD=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_DEFAULT_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_ANNOTATION=Ignore
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Ignore
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+MISSING_EE_DESCRIPTIONS=Warning
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+changed_execution_env=Error
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_report_major_without_breaking_change=Error
+incompatible_api_component_version_report_minor_without_api_change=Error
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.pde.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.pde.prefs
new file mode 100644
index 000000000..9dbfcc5af
--- /dev/null
+++ b/org.eclipse.unittest.ui/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,36 @@
+compilers.f.unresolved-features=1
+compilers.f.unresolved-plugins=1
+compilers.incompatible-environment=0
+compilers.p.build=1
+compilers.p.build.bin.includes=1
+compilers.p.build.encodings=2
+compilers.p.build.java.compiler=2
+compilers.p.build.java.compliance=1
+compilers.p.build.missing.output=2
+compilers.p.build.output.library=1
+compilers.p.build.source.library=1
+compilers.p.build.src.includes=1
+compilers.p.deprecated=1
+compilers.p.discouraged-class=1
+compilers.p.exec-env-too-low=1
+compilers.p.internal=1
+compilers.p.missing-packages=2
+compilers.p.missing-version-export-package=2
+compilers.p.missing-version-import-package=2
+compilers.p.missing-version-require-bundle=1
+compilers.p.no-required-att=0
+compilers.p.no.automatic.module=1
+compilers.p.not-externalized-att=1
+compilers.p.service.component.without.lazyactivation=1
+compilers.p.unknown-attribute=0
+compilers.p.unknown-class=0
+compilers.p.unknown-element=0
+compilers.p.unknown-identifier=0
+compilers.p.unknown-resource=0
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.s.create-docs=false
+compilers.s.doc-folder=doc
+compilers.s.open-tags=1
+compilers.use-project=true
+eclipse.preferences.version=1
diff --git a/org.eclipse.unittest.ui/META-INF/MANIFEST.MF b/org.eclipse.unittest.ui/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..9f68832bb
--- /dev/null
+++ b/org.eclipse.unittest.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Automatic-Module-Name: org.eclipse.unittest.ui
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.unittest.ui;singleton:=true
+Bundle-Version: 0.1.0.qualifier
+Bundle-Activator: org.eclipse.unittest.internal.UnitTestPlugin
+Bundle-ActivationPolicy: lazy
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.unittest.launcher,
+ org.eclipse.unittest.model,
+ org.eclipse.unittest.ui
+Require-Bundle:
+ org.eclipse.ui.ide;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.jface.text;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.debug.ui;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.runtime;bundle-version="[3.11.0,4.0.0)",
+ org.eclipse.compare;bundle-version="[3.5.0,4.0.0)"
+Bundle-RequiredExecutionEnvironment: JavaSE-11
diff --git a/org.eclipse.unittest.ui/about.html b/org.eclipse.unittest.ui/about.html
new file mode 100644
index 000000000..164f781a8
--- /dev/null
+++ b/org.eclipse.unittest.ui/about.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>About</title>
+</head>
+<body lang="EN-US">
+ <h2>About This Content</h2>
+
+ <p>November 30, 2017</p>
+ <h3>License</h3>
+
+ <p>
+ The Eclipse Foundation makes available all content in this plug-in
+ (&quot;Content&quot;). Unless otherwise indicated below, the Content
+ is provided to you under the terms and conditions of the Eclipse
+ Public License Version 2.0 (&quot;EPL&quot;). A copy of the EPL is
+ available at <a href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>.
+ For purposes of the EPL, &quot;Program&quot; will mean the Content.
+ </p>
+
+ <p>
+ If you did not receive this Content directly from the Eclipse
+ Foundation, the Content is being redistributed by another party
+ (&quot;Redistributor&quot;) and different terms and conditions may
+ apply to your use of any object code in the Content. Check the
+ Redistributor's license that was provided with the Content. If no such
+ license exists, contact the Redistributor. Unless otherwise indicated
+ below, the terms and conditions of the EPL still apply to any source
+ code in the Content and such source code may be obtained at <a
+ href="http://www.eclipse.org/">http://www.eclipse.org</a>.
+ </p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/org.eclipse.unittest.ui/build.properties b/org.eclipse.unittest.ui/build.properties
new file mode 100644
index 000000000..136d1bca6
--- /dev/null
+++ b/org.eclipse.unittest.ui/build.properties
@@ -0,0 +1,25 @@
+###############################################################################
+# Copyright (c) 2000, 2011 IBM Corporation and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+bin.includes = plugin.xml,\
+ about.html,\
+ icons/,\
+ plugin.properties,\
+ .,\
+ META-INF/
+
+source.. = src/
+src.includes = about.html,\
+ schema/
+
+javacWarnings..=-unavoidableGenericProblems
diff --git a/org.eclipse.unittest.ui/icons-work/unitViewIcon.xcf b/org.eclipse.unittest.ui/icons-work/unitViewIcon.xcf
new file mode 100644
index 000000000..9391c1c7d
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons-work/unitViewIcon.xcf
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter.png b/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter.png
new file mode 100644
index 000000000..fd883fb43
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter@2x.png
new file mode 100644
index 000000000..1473192df
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/compare.png b/org.eclipse.unittest.ui/icons/full/dlcl16/compare.png
new file mode 100644
index 000000000..cbeeeabe5
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/compare.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/compare@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/compare@2x.png
new file mode 100644
index 000000000..bd6e98107
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/compare@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout.png b/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout.png
new file mode 100644
index 000000000..061b593ca
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout@2x.png
new file mode 100644
index 000000000..23540f72b
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout.png b/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout.png
new file mode 100644
index 000000000..64822eaa0
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout@2x.png
new file mode 100644
index 000000000..b84fb01bf
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/history_list.png b/org.eclipse.unittest.ui/icons/full/dlcl16/history_list.png
new file mode 100644
index 000000000..033bef13d
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/history_list.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/history_list@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/history_list@2x.png
new file mode 100644
index 000000000..2a9eeb412
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/history_list@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/lock.png b/org.eclipse.unittest.ui/icons/full/dlcl16/lock.png
new file mode 100644
index 000000000..615dbbb64
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/lock.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/lock@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/lock@2x.png
new file mode 100644
index 000000000..cde2a0f41
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/lock@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/open_console.png b/org.eclipse.unittest.ui/icons/full/dlcl16/open_console.png
new file mode 100644
index 000000000..735f57b08
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/open_console.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/open_console@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/open_console@2x.png
new file mode 100644
index 000000000..e0232a871
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/open_console@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch.png b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch.png
new file mode 100644
index 000000000..f84f509a0
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch@2x.png
new file mode 100644
index 000000000..42a5cbddf
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf.png b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf.png
new file mode 100644
index 000000000..b30f5738a
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf@2x.png
new file mode 100644
index 000000000..a4a23a5e6
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/select_next.png b/org.eclipse.unittest.ui/icons/full/dlcl16/select_next.png
new file mode 100644
index 000000000..1a71a7923
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/select_next.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/select_next@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/select_next@2x.png
new file mode 100644
index 000000000..064c3002c
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/select_next@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev.png b/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev.png
new file mode 100644
index 000000000..7eaaff979
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev@2x.png
new file mode 100644
index 000000000..0b95e38b5
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/stop.png b/org.eclipse.unittest.ui/icons/full/dlcl16/stop.png
new file mode 100644
index 000000000..ed5e84804
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/stop.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/stop@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/stop@2x.png
new file mode 100644
index 000000000..561447b40
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/stop@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic.png
new file mode 100644
index 000000000..0d8f6ddd1
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic@2x.png
new file mode 100644
index 000000000..7e5f9d7a8
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal.png
new file mode 100644
index 000000000..a7b1f6bbd
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal@2x.png
new file mode 100644
index 000000000..943c571bb
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical.png
new file mode 100644
index 000000000..a524112f9
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical@2x.png
new file mode 100644
index 000000000..a12d09145
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase.png b/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase.png
new file mode 100644
index 000000000..d9cb08eac
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase@2x.png b/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase@2x.png
new file mode 100644
index 000000000..5c596075d
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite.png b/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite.png
new file mode 100644
index 000000000..eda2dad33
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite@2x.png b/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite@2x.png
new file mode 100644
index 000000000..2441d86cf
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/cfilter.png b/org.eclipse.unittest.ui/icons/full/elcl16/cfilter.png
new file mode 100644
index 000000000..e779b3e29
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/cfilter.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/cfilter@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/cfilter@2x.png
new file mode 100644
index 000000000..3bea811c8
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/cfilter@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/compare.png b/org.eclipse.unittest.ui/icons/full/elcl16/compare.png
new file mode 100644
index 000000000..07fb14ee9
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/compare.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/compare@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/compare@2x.png
new file mode 100644
index 000000000..89c631a38
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/compare@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout.png b/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout.png
new file mode 100644
index 000000000..13f83a237
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout@2x.png
new file mode 100644
index 000000000..280561aeb
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout.png b/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout.png
new file mode 100644
index 000000000..665aa5cee
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout@2x.png
new file mode 100644
index 000000000..84039be3a
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/history_list.png b/org.eclipse.unittest.ui/icons/full/elcl16/history_list.png
new file mode 100644
index 000000000..7808b5070
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/history_list.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/history_list@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/history_list@2x.png
new file mode 100644
index 000000000..585fc17b9
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/history_list@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/lock.png b/org.eclipse.unittest.ui/icons/full/elcl16/lock.png
new file mode 100644
index 000000000..91fef558a
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/lock.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/lock@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/lock@2x.png
new file mode 100644
index 000000000..8742944db
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/lock@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/open_console.png b/org.eclipse.unittest.ui/icons/full/elcl16/open_console.png
new file mode 100644
index 000000000..fdb3b0cb3
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/open_console.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/open_console@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/open_console@2x.png
new file mode 100644
index 000000000..55bcdc0e2
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/open_console@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/relaunch.png b/org.eclipse.unittest.ui/icons/full/elcl16/relaunch.png
new file mode 100644
index 000000000..89228fa08
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/relaunch.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/relaunch@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/relaunch@2x.png
new file mode 100644
index 000000000..c0a3d3ab8
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/relaunch@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf.png b/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf.png
new file mode 100644
index 000000000..d25cafa34
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf@2x.png
new file mode 100644
index 000000000..74550215f
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/select_next.png b/org.eclipse.unittest.ui/icons/full/elcl16/select_next.png
new file mode 100644
index 000000000..9fcc646d9
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/select_next.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/select_next@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/select_next@2x.png
new file mode 100644
index 000000000..d46b3a062
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/select_next@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/select_prev.png b/org.eclipse.unittest.ui/icons/full/elcl16/select_prev.png
new file mode 100644
index 000000000..f2e6a039c
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/select_prev.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/select_prev@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/select_prev@2x.png
new file mode 100644
index 000000000..8e26ea4b4
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/select_prev@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/stop.png b/org.eclipse.unittest.ui/icons/full/elcl16/stop.png
new file mode 100644
index 000000000..032a26c1c
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/stop.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/stop@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/stop@2x.png
new file mode 100644
index 000000000..a7dc14cc9
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/stop@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic.png
new file mode 100644
index 000000000..a1103e8a5
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic@2x.png
new file mode 100644
index 000000000..bf6057510
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal.png
new file mode 100644
index 000000000..ca4602849
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal@2x.png
new file mode 100644
index 000000000..217f8560e
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical.png
new file mode 100644
index 000000000..07baf5138
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical@2x.png
new file mode 100644
index 000000000..62e0a379a
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/etool16/new_testcase.png b/org.eclipse.unittest.ui/icons/full/etool16/new_testcase.png
new file mode 100644
index 000000000..7c4692f69
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/etool16/new_testcase.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/etool16/new_testcase@2x.png b/org.eclipse.unittest.ui/icons/full/etool16/new_testcase@2x.png
new file mode 100644
index 000000000..eb4401afe
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/etool16/new_testcase@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite.png b/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite.png
new file mode 100644
index 000000000..8ceabbcb7
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite@2x.png b/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite@2x.png
new file mode 100644
index 000000000..87bc8eb52
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/stackframe.png b/org.eclipse.unittest.ui/icons/full/eview16/stackframe.png
new file mode 100644
index 000000000..cffa1aaea
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/stackframe.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/stackframe@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/stackframe@2x.png
new file mode 100644
index 000000000..3f880c529
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/stackframe@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unit.png b/org.eclipse.unittest.ui/icons/full/eview16/unit.png
new file mode 100644
index 000000000..358eba121
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/unit.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unit@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/unit@2x.png
new file mode 100644
index 000000000..70cf88e8b
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/unit@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/uniterr.png b/org.eclipse.unittest.ui/icons/full/eview16/uniterr.png
new file mode 100644
index 000000000..834deffeb
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/uniterr.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/uniterr@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/uniterr@2x.png
new file mode 100644
index 000000000..7b71aa58b
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/uniterr@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/uniterrq.png b/org.eclipse.unittest.ui/icons/full/eview16/uniterrq.png
new file mode 100644
index 000000000..9e17bb9f5
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/uniterrq.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/uniterrq@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/uniterrq@2x.png
new file mode 100644
index 000000000..aa2e0b5ab
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/uniterrq@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unitsucc.png b/org.eclipse.unittest.ui/icons/full/eview16/unitsucc.png
new file mode 100644
index 000000000..ef6dd9412
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/unitsucc.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unitsucc@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/unitsucc@2x.png
new file mode 100644
index 000000000..53b19687b
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/unitsucc@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq.png b/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq.png
new file mode 100644
index 000000000..97884575b
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq@2x.png
new file mode 100644
index 000000000..1a7a42572
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/exc_catch.png b/org.eclipse.unittest.ui/icons/full/obj16/exc_catch.png
new file mode 100644
index 000000000..2962f6e63
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/exc_catch.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/exc_catch@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/exc_catch@2x.png
new file mode 100644
index 000000000..b943d360d
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/exc_catch@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/faillist.png b/org.eclipse.unittest.ui/icons/full/obj16/faillist.png
new file mode 100644
index 000000000..c33980f65
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/faillist.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/faillist@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/faillist@2x.png
new file mode 100644
index 000000000..d3686b843
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/faillist@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/failures.png b/org.eclipse.unittest.ui/icons/full/obj16/failures.png
new file mode 100644
index 000000000..c2cd90d96
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/failures.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/failures@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/failures@2x.png
new file mode 100644
index 000000000..1d6815daf
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/failures@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/julaunch.png b/org.eclipse.unittest.ui/icons/full/obj16/julaunch.png
new file mode 100644
index 000000000..ede46fbac
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/julaunch.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/julaunch@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/julaunch@2x.png
new file mode 100644
index 000000000..e9e9b1d69
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/julaunch@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj.png b/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj.png
new file mode 100644
index 000000000..cffa1aaea
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj@2x.png
new file mode 100644
index 000000000..3f880c529
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/test.png b/org.eclipse.unittest.ui/icons/full/obj16/test.png
new file mode 100644
index 000000000..c95f6b58b
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/test.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/test@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/test@2x.png
new file mode 100644
index 000000000..c404f6507
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/test@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed.png b/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed.png
new file mode 100644
index 000000000..e12b607ed
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed@2x.png
new file mode 100644
index 000000000..4702a33f3
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testerr.png b/org.eclipse.unittest.ui/icons/full/obj16/testerr.png
new file mode 100644
index 000000000..1814c22a0
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testerr.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testerr@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testerr@2x.png
new file mode 100644
index 000000000..221811a32
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testerr@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testfail.png b/org.eclipse.unittest.ui/icons/full/obj16/testfail.png
new file mode 100644
index 000000000..f7ebe5efd
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testfail.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testfail@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testfail@2x.png
new file mode 100644
index 000000000..944e6381c
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testfail@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj.png b/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj.png
new file mode 100644
index 000000000..0905b505f
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj@2x.png
new file mode 100644
index 000000000..9cb969611
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testhier.png b/org.eclipse.unittest.ui/icons/full/obj16/testhier.png
new file mode 100644
index 000000000..acfde8818
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testhier.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testhier@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testhier@2x.png
new file mode 100644
index 000000000..9ab1d917f
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testhier@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testignored.png b/org.eclipse.unittest.ui/icons/full/obj16/testignored.png
new file mode 100644
index 000000000..7f241980b
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testignored.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testignored@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testignored@2x.png
new file mode 100644
index 000000000..459d022cf
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testignored@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testok.png b/org.eclipse.unittest.ui/icons/full/obj16/testok.png
new file mode 100644
index 000000000..c27c47ea5
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testok.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testok@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testok@2x.png
new file mode 100644
index 000000000..272d3577b
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testok@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testrun.png b/org.eclipse.unittest.ui/icons/full/obj16/testrun.png
new file mode 100644
index 000000000..8a08a1d33
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testrun.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testrun@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testrun@2x.png
new file mode 100644
index 000000000..54c54d615
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/testrun@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuite.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuite.png
new file mode 100644
index 000000000..d45394e5a
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuite.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuite@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuite@2x.png
new file mode 100644
index 000000000..b3bdb79f7
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuite@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror.png
new file mode 100644
index 000000000..53ab5187e
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror@2x.png
new file mode 100644
index 000000000..c1dd70b1e
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail.png
new file mode 100644
index 000000000..21402750d
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail@2x.png
new file mode 100644
index 000000000..830da7e99
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok.png
new file mode 100644
index 000000000..a6bfbf83e
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok@2x.png
new file mode 100644
index 000000000..bb0604352
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun.png
new file mode 100644
index 000000000..d4d5170be
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun@2x.png
new file mode 100644
index 000000000..d829ce8f1
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr.png b/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr.png
new file mode 100644
index 000000000..412588ac5
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr@2x.png b/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr@2x.png
new file mode 100644
index 000000000..683b3f743
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr.png b/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr.png
new file mode 100644
index 000000000..a2f7f2812
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr@2x.png b/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr@2x.png
new file mode 100644
index 000000000..453e52792
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr.png b/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr.png
new file mode 100644
index 000000000..40c5322bd
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr@2x.png b/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr@2x.png
new file mode 100644
index 000000000..d912a1772
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz.png b/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz.png
new file mode 100644
index 000000000..55f6dbb33
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz@2x.png b/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz@2x.png
new file mode 100644
index 000000000..425b38e38
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz.png b/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz.png
new file mode 100644
index 000000000..d601a97f2
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz@2x.png b/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz@2x.png
new file mode 100644
index 000000000..9011edd06
--- /dev/null
+++ b/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz@2x.png
Binary files differ
diff --git a/org.eclipse.unittest.ui/plugin.properties b/org.eclipse.unittest.ui/plugin.properties
new file mode 100644
index 000000000..473441c53
--- /dev/null
+++ b/org.eclipse.unittest.ui/plugin.properties
@@ -0,0 +1,31 @@
+###############################################################################
+# Copyright (c) 2000, 2019 IBM Corporation and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+pluginName=Debug Unit Test support
+providerName=Eclipse.org
+
+testRunListeners.name= Test Run Listeners
+
+testViewSupport.name= Unit Test View Support
+
+View.label= Test Results
+
+UnitTestShortcut.label= Unit Test
+UnitTestShortcut.description.run= Run Unit Test
+UnitTestShortcut.description.debug= Debug Unit Test
+UnitTestShortcut.description.rerunLast= Rerun Unit Test
+UnitTestShortcut.description.rerunFailedCases= Rerun Unit Test - Failures only
+UnitTestShortcut.description.history=Test Sessions History
+
+GotoTestCommand.name= Referring Tests
+GotoTestCommand.description= Referring Tests
diff --git a/org.eclipse.unittest.ui/plugin.xml b/org.eclipse.unittest.ui/plugin.xml
new file mode 100644
index 000000000..1e351367e
--- /dev/null
+++ b/org.eclipse.unittest.ui/plugin.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+
+ <extension-point id="unittestViewSupport" name="%testViewSupport.name" schema="schema/unittestViewSupport.exsd"/>
+
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ name="%View.label"
+ icon="$nl$/icons/full/eview16/unit.png"
+ category="org.eclipse.debug.ui"
+ fastViewWidthRatio="0.40"
+ class="org.eclipse.unittest.internal.ui.TestRunnerViewPart"
+ id="org.eclipse.unittest.ui.ResultView">
+ </view>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ name="%GotoTestCommand.name"
+ description="%GotoTestCommand.description"
+ categoryId="org.eclipse.search.ui.category.search"
+ id="org.eclipse.unittest.ui.gotoTest">
+ </command>
+ <command
+ name="%UnitTestShortcut.description.run"
+ description="%UnitTestShortcut.description.run"
+ categoryId="org.eclipse.debug.ui.category.run"
+ id="org.eclipse.unittest.ui.UnitTestShortcut.run">
+ </command>
+ <command
+ name="%UnitTestShortcut.description.debug"
+ description="%UnitTestShortcut.description.debug"
+ categoryId="org.eclipse.debug.ui.category.run"
+ id="org.eclipse.unittest.ui.UnitTestShortcut.debug">
+ </command>
+ <command
+ name="%UnitTestShortcut.description.rerunLast"
+ description="%UnitTestShortcut.description.rerunLast"
+ categoryId="org.eclipse.debug.ui.category.run"
+ id="org.eclipse.unittest.ui.UnitTestShortcut.rerunLast">
+ </command>
+ <command
+ name="%UnitTestShortcut.description.rerunFailedCases"
+ description="%UnitTestShortcut.description.rerunFailedCases"
+ categoryId="org.eclipse.debug.ui.category.run"
+ id="org.eclipse.unittest.ui.UnitTestShortcut.rerunFailedCases">
+ </command>
+ <command
+ defaultHandler="org.eclipse.unittest.internal.ui.history.HistoryHandler"
+ description="%UnitTestShortcut.description.history"
+ id="org.eclipse.unittest.ui.history"
+ name="%UnitTestShortcut.description.history">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commandImages">
+ <image
+ commandId="org.eclipse.unittest.ui.history"
+ disabledIcon="icons/full/dlcl16/history_list.png"
+ icon="icons/full/elcl16/history_list.png">
+ </image>
+ </extension>
+
+</plugin>
diff --git a/org.eclipse.unittest.ui/pom.xml b/org.eclipse.unittest.ui/pom.xml
new file mode 100644
index 000000000..b2f8e31cf
--- /dev/null
+++ b/org.eclipse.unittest.ui/pom.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2012, 2019 Eclipse Foundation and others.
+ All rights reserved. This program and the accompanying materials
+ are made available under the terms of the Eclipse Distribution License v1.0
+ which accompanies this distribution, and is available at
+ http://www.eclipse.org/org/documents/edl-v10.php
+
+ Contributors:
+ Igor Fedorenko - initial implementation
+ Jens Reimann (jreimann@redhat.com) - add copy & paste support
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>eclipse.platform.debug</artifactId>
+ <groupId>eclipse.platform.debug</groupId>
+ <version>4.18.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.eclipse.debug</groupId>
+ <artifactId>org.eclipse.unittest.ui</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
+ <packaging>eclipse-plugin</packaging>
+ <properties>
+ <skipAPIAnalysis>true</skipAPIAnalysis>
+ </properties>
+</project>
diff --git a/org.eclipse.unittest.ui/schema/unittestViewSupport.exsd b/org.eclipse.unittest.ui/schema/unittestViewSupport.exsd
new file mode 100644
index 000000000..2217935a8
--- /dev/null
+++ b/org.eclipse.unittest.ui/schema/unittestViewSupport.exsd
@@ -0,0 +1,98 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.unittest.ui" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.unittest.ui" id="unittestViewSupport" name="UnitTest View Support"/>
+ </appInfo>
+ <documentation>
+ Internal extension point to register Unit Test View support.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="viewSupport" minOccurs="1" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+ a fully qualified extention point identifier
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+ a fully qualified identifier of the target extension point
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+ a name of this extention point
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="viewSupport">
+ <annotation>
+ <documentation>
+ A Test View Support definition that implements org.eclipse.unittest.launcher.ITestViewSupport.
+This is used by the &lt;i&gt;Unit Test&lt;/i&gt; view to provide support for test engines.
+ </documentation>
+ </annotation>
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ a fully qualified extention point identifier.
+This identifier is used to load support for the &lt;i&gt;Unit Test&lt;/i&gt; view when some ILaunch is running, and the underlying launch configuration sets attributes &lt;code&gt;UnitTestLaunchConfigurationConstants.ATTR_UNIT_TEST_VIEW_SUPPORT&lt;/code&gt; to the given id.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ a fully qualified class name that implements org.eclipse.unittest.launcher.ITestViewSupport.
+This is used by the &lt;i&gt;Unit Test&lt;/i&gt; view to provide support for test engines.
+The class defines the method that creates a Unit Test Runner Client dedicated to communicate with a test engine and to gather the results of testing as well as a number of methods that help to the &lt;i&gt;Unit Test&lt;/i&gt; view in creation of unit test related actions, like openning a test source or a file reported on a stack trace in editor or returning a ILaunchConfiguration for Rerun action.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.unittest.launcher.ITestViewSupport"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+
+
+
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2006, 2020 IBM Corporation and others.
+
+This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at &lt;a href=&quot;https://www.eclipse.org/legal/epl-2.0&quot;&gt;https://www.eclipse.org/legal/epl-v20.html&lt;/a&gt;/
+
+SPDX-License-Identifier: EPL-2.0
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPlugin.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPlugin.java
new file mode 100644
index 000000000..b1f3a2585
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPlugin.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal;
+
+import org.osgi.framework.BundleContext;
+
+import org.eclipse.unittest.internal.model.UnitTestLaunchListener;
+import org.eclipse.unittest.internal.model.UnitTestModel;
+import org.eclipse.unittest.internal.ui.history.History;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchListener;
+
+/**
+ * The plug-in runtime class for the Unit Test plug-in.
+ */
+public class UnitTestPlugin extends AbstractUIPlugin {
+
+ /**
+ * The single instance of this plug-in runtime class.
+ */
+ private static UnitTestPlugin fgPlugin = null;
+
+ private final ILaunchListener fLaunchListener = new UnitTestLaunchListener();
+
+ public static final String PLUGIN_ID = "org.eclipse.unittest.ui"; //$NON-NLS-1$
+
+ /**
+ * Constructs a {@link UnitTestPlugin} object
+ */
+ public UnitTestPlugin() {
+ fgPlugin = this;
+ }
+
+ /**
+ * Returns the {@link UnitTestPlugin} instance
+ *
+ * @return a {@link UnitTestPlugin} instance
+ */
+ public static UnitTestPlugin getDefault() {
+ return fgPlugin;
+ }
+
+ /**
+ * Logs the given exception.
+ *
+ * @param e the {@link Throwable} to log
+ */
+ public static void log(Throwable e) {
+ log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.ERROR, "Error", e)); //$NON-NLS-1$
+ }
+
+ /**
+ * Logs the given status.
+ *
+ * @param status the status to log
+ */
+ public static void log(IStatus status) {
+ getDefault().getLog().log(status);
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ UnitTestModel.getInstance().start();
+ DebugPlugin.getDefault().getLaunchManager().addLaunchListener(fLaunchListener);
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ try {
+ InstanceScope.INSTANCE.getNode(UnitTestPlugin.PLUGIN_ID).flush();
+ UnitTestModel.getInstance().stop();
+ DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(fLaunchListener);
+ History.INSTANCE.clear();
+ } finally {
+ super.stop(context);
+ }
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPreferencesConstants.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPreferencesConstants.java
new file mode 100644
index 000000000..1afcd46b4
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPreferencesConstants.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+
+/**
+ * Defines constants which are used to refer to values in the plugin's
+ * preference store.
+ */
+public class UnitTestPreferencesConstants {
+ /**
+ * Boolean preference controlling whether the failure stack should be filtered.
+ */
+ public static final String DO_FILTER_STACK = UnitTestPlugin.PLUGIN_ID + ".do_filter_stack"; //$NON-NLS-1$
+
+ /**
+ * Boolean preference controlling whether the Unit Test view should be shown on
+ * errors only.
+ */
+ public static final String SHOW_ON_ERROR_ONLY = UnitTestPlugin.PLUGIN_ID + ".show_on_error"; //$NON-NLS-1$
+
+ /**
+ * Maximum number of remembered test runs.
+ */
+ public static final String MAX_TEST_RUNS = UnitTestPlugin.PLUGIN_ID + ".max_test_runs"; //$NON-NLS-1$
+
+ private UnitTestPreferencesConstants() {
+ // no instance
+ }
+
+ /**
+ * Serializes the array of strings into one comma-separated string.
+ *
+ * @param list array of strings
+ * @return a single string composed of the given list
+ */
+ public static String serializeList(String[] list) {
+ if (list == null)
+ return ""; //$NON-NLS-1$
+
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < list.length; i++) {
+ if (i > 0)
+ buffer.append(',');
+
+ buffer.append(list[i]);
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Parses the comma-separated string into an array of strings.
+ *
+ * @param listString a comma-separated string
+ * @return an array of strings
+ */
+ public static String[] parseList(String listString) {
+ List<String> list = new ArrayList<>(10);
+ StringTokenizer tokenizer = new StringTokenizer(listString, ","); //$NON-NLS-1$
+ while (tokenizer.hasMoreTokens())
+ list.add(tokenizer.nextToken());
+ return list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * Indicates if a filter patterns are to be applied on a stacktrace/error
+ * messages
+ *
+ * @return <code>true</code> in case the stacktrace is to be filtered, otherwise
+ * - <code>false</code>
+ */
+ public static boolean getFilterStack() {
+ return Platform.getPreferencesService().getBoolean(UnitTestPlugin.PLUGIN_ID, DO_FILTER_STACK, false, null);
+ }
+
+ /**
+ * Sets up a value for the DO_FILTER_STACK preference
+ *
+ * @param filter boolean indicating if a stacktrace is to be filtered
+ */
+ public static void setFilterStack(boolean filter) {
+ InstanceScope.INSTANCE.getNode(UnitTestPlugin.PLUGIN_ID).putBoolean(DO_FILTER_STACK, filter);
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/HistoryEntryHandler.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/HistoryEntryHandler.java
new file mode 100644
index 000000000..c55df2bd3
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/HistoryEntryHandler.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Red Ha, Inc. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.junitXmlReport;
+
+import java.time.Instant;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.eclipse.core.runtime.OperationCanceledException;
+
+/**
+ * Minimal reader to populate history entry data.
+ */
+public class HistoryEntryHandler extends DefaultHandler {
+
+ private int failuresAndErrors;
+ private Instant startTime;
+ private String name;
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ if (Thread.interrupted())
+ throw new OperationCanceledException();
+
+ if (IXMLTags.NODE_TESTRUN.equals(qName)) {
+ String attribute = attributes.getValue(IXMLTags.ATTR_ERRORS);
+ if (attribute != null) {
+ failuresAndErrors = getFailuresAndErrors() + Integer.parseInt(attribute);
+ }
+ attribute = attributes.getValue(IXMLTags.ATTR_FAILURES);
+ if (attribute != null) {
+ failuresAndErrors = getFailuresAndErrors() + Integer.parseInt(attribute);
+ }
+ name = attributes.getValue(IXMLTags.ATTR_NAME);
+ attribute = attributes.getValue(IXMLTags.ATTR_START_TIME);
+ if (attribute != null) {
+ startTime = Instant.parse(attribute);
+ }
+ }
+ }
+
+ /**
+ * Returns the number of failures and errors for the history entry
+ *
+ * @return a number of failures and errors
+ */
+ public int getFailuresAndErrors() {
+ return failuresAndErrors;
+ }
+
+ /**
+ * Returns an {@link Instant} object instance indicating a unit test start time.
+ *
+ * @return an {@link Instant} object instance
+ */
+ public Instant getStartTime() {
+ return startTime;
+ }
+
+ /**
+ * Returns a name of history entry
+ *
+ * @return a name of history entry
+ */
+ public String getName() {
+ return name;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/IXMLTags.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/IXMLTags.java
new file mode 100644
index 000000000..efe388a47
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/IXMLTags.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.junitXmlReport;
+
+interface IXMLTags {
+
+ String NODE_TESTRUN = "testrun"; //$NON-NLS-1$
+ String NODE_TESTSUITES = "testsuites"; //$NON-NLS-1$
+ String NODE_TESTSUITE = "testsuite"; //$NON-NLS-1$
+ String NODE_PROPERTIES = "properties"; //$NON-NLS-1$
+ String NODE_PROPERTY = "property"; //$NON-NLS-1$
+ String NODE_TESTCASE = "testcase"; //$NON-NLS-1$
+ String NODE_ERROR = "error"; //$NON-NLS-1$
+ String NODE_FAILURE = "failure"; //$NON-NLS-1$
+ String NODE_EXPECTED = "expected"; //$NON-NLS-1$
+ String NODE_ACTUAL = "actual"; //$NON-NLS-1$
+ String NODE_SYSTEM_OUT = "system-out"; //$NON-NLS-1$
+ String NODE_SYSTEM_ERR = "system-err"; //$NON-NLS-1$
+ String NODE_SKIPPED = "skipped"; //$NON-NLS-1$
+
+ /**
+ * value: String
+ */
+ String ATTR_NAME = "name"; //$NON-NLS-1$
+ /**
+ * value: String
+ */
+ String ATTR_LAUNCH_CONFIG_NAME = "launch_config_name"; //$NON-NLS-1$
+ /**
+ * value: Integer
+ */
+ String ATTR_TESTS = "tests"; //$NON-NLS-1$
+ /**
+ * value: Integer
+ */
+ String ATTR_STARTED = "started"; //$NON-NLS-1$
+ /**
+ * value: Integer
+ */
+ String ATTR_FAILURES = "failures"; //$NON-NLS-1$
+ /**
+ * value: Integer
+ */
+ String ATTR_ERRORS = "errors"; //$NON-NLS-1$
+ /**
+ * value: Boolean
+ */
+ String ATTR_IGNORED = "ignored"; //$NON-NLS-1$
+ /**
+ * value: String
+ */
+ String ATTR_PACKAGE = "package"; //$NON-NLS-1$
+ /**
+ * value: String
+ */
+ String ATTR_ID = "id"; //$NON-NLS-1$
+ /**
+ * value: String
+ */
+ String ATTR_CLASSNAME = "classname"; //$NON-NLS-1$
+ /**
+ * value: Boolean
+ */
+ String ATTR_INCOMPLETE = "incomplete"; //$NON-NLS-1$
+ /**
+ * value: Duration.toString()
+ */
+ String ATTR_START_TIME = "startTime"; //$NON-NLS-1$
+ /**
+ * value: Double (duration in seconds)
+ */
+ String ATTR_DURATION = "time"; //$NON-NLS-1$
+ /**
+ * value: String
+ */
+ String ATTR_MESSAGE = "message"; //$NON-NLS-1$
+ /**
+ * value: String
+ */
+ String ATTR_DISPLAY_NAME = "displayname"; //$NON-NLS-1$
+ /**
+ * value: Boolean
+ */
+ String ATTR_DYNAMIC_TEST = "dynamicTest"; //$NON-NLS-1$
+ /**
+ * value: String
+ */
+ String ATTR_DATA = "data"; //$NON-NLS-1$
+
+ /**
+ * value: String
+ */
+ String ATTR_INCLUDE_TAGS = "include_tags"; //$NON-NLS-1$
+ /**
+ * value: String
+ */
+ String ATTR_EXCLUDE_TAGS = "exclude_tags"; //$NON-NLS-1$
+
+// public static final String ATTR_TYPE= "type"; //$NON-NLS-1$
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunHandler.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunHandler.java
new file mode 100644
index 000000000..29a49c38f
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunHandler.java
@@ -0,0 +1,365 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.junitXmlReport;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Stack;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.model.ModelMessages;
+import org.eclipse.unittest.internal.model.TestCaseElement;
+import org.eclipse.unittest.internal.model.TestElement;
+import org.eclipse.unittest.internal.model.TestRunSession;
+import org.eclipse.unittest.internal.model.TestSuiteElement;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestElement.FailureTrace;
+import org.eclipse.unittest.model.ITestElement.Result;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+
+public class TestRunHandler extends DefaultHandler {
+
+ /*
+ * TODO: validate (currently assumes correct XML)
+ */
+
+ private int fId;
+
+ private TestRunSession fTestRunSession;
+ private TestSuiteElement fTestSuite;
+ private TestCaseElement fTestCase;
+ private Stack<Boolean> fNotRun = new Stack<>();
+
+ private StringBuilder fFailureBuffer;
+ private boolean fInExpected;
+ private boolean fInActual;
+ private StringBuilder fExpectedBuffer;
+ private StringBuilder fActualBuffer;
+
+ private Locator fLocator;
+
+ private Result fStatus;
+
+ private final IProgressMonitor fMonitor;
+ private int fLastReportedLine;
+
+ /**
+ * Constructs a default {@link TestRunHandler} object instance
+ */
+ public TestRunHandler() {
+ fMonitor = new NullProgressMonitor();
+ }
+
+ /**
+ * Constructs a {@link TestRunHandler} object instance
+ *
+ * @param monitor a progress monitor
+ */
+ public TestRunHandler(IProgressMonitor monitor) {
+ fMonitor = monitor != null ? monitor : new NullProgressMonitor();
+ }
+
+ @Override
+ public void setDocumentLocator(Locator locator) {
+ fLocator = locator;
+ }
+
+ @Override
+ public void startDocument() throws SAXException {
+ // Nothing to do
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ if (fMonitor.isCanceled())
+ throw new OperationCanceledException();
+
+ if (fLocator != null) {
+ int line = fLocator.getLineNumber();
+ if (line - 20 >= fLastReportedLine) {
+ line -= line % 20;
+ fLastReportedLine = line;
+ fMonitor.subTask(NLS.bind(ModelMessages.TestRunHandler_lines_read, Integer.valueOf(line)));
+ }
+ }
+
+ if (Thread.interrupted())
+ throw new OperationCanceledException();
+
+ switch (qName) {
+ case IXMLTags.NODE_TESTRUN:
+ if (fTestRunSession == null) {
+ String name = attributes.getValue(IXMLTags.ATTR_NAME);
+ String launchConfigName = attributes.getValue(IXMLTags.ATTR_LAUNCH_CONFIG_NAME);
+ ILaunchConfiguration launchConfiguration = null;
+ if (launchConfigName != null) {
+ try {
+ for (ILaunchConfiguration config : DebugPlugin.getDefault().getLaunchManager()
+ .getLaunchConfigurations()) {
+ if (config.getName().equals(launchConfigName)) {
+ launchConfiguration = config;
+ }
+ }
+ } catch (CoreException e) {
+ UnitTestPlugin.log(e);
+ }
+ }
+ fTestRunSession = new TestRunSession(name, Instant.parse(attributes.getValue(IXMLTags.ATTR_START_TIME)),
+ launchConfiguration);
+ readDuration(fTestRunSession, attributes);
+ // TODO: read counts?
+ } else {
+ fTestRunSession.reset();
+ }
+ fTestSuite = fTestRunSession;
+ break;
+ case IXMLTags.NODE_TESTSUITES:
+ break;
+ case IXMLTags.NODE_TESTSUITE: {
+ String name = attributes.getValue(IXMLTags.ATTR_NAME);
+ String pack = attributes.getValue(IXMLTags.ATTR_PACKAGE);
+ String suiteName = pack == null ? name : pack + "." + name; //$NON-NLS-1$
+ String displayName = attributes.getValue(IXMLTags.ATTR_DISPLAY_NAME);
+ String data = attributes.getValue(IXMLTags.ATTR_DATA);
+ if (data != null && data.isBlank()) {
+ data = null;
+ }
+ fTestSuite = (TestSuiteElement) fTestRunSession.createTestElement(fTestSuite, getNextId(), suiteName, true,
+ null, false, displayName, data);
+ readDuration(fTestSuite, attributes);
+ fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE)));
+ break;
+ }
+ // not interested
+ case IXMLTags.NODE_PROPERTIES:
+ case IXMLTags.NODE_PROPERTY:
+ break;
+ case IXMLTags.NODE_TESTCASE: {
+ String name = attributes.getValue(IXMLTags.ATTR_NAME);
+ String classname = attributes.getValue(IXMLTags.ATTR_CLASSNAME);
+ String testName = name + '(' + classname + ')';
+ boolean isDynamicTest = Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_DYNAMIC_TEST)).booleanValue();
+ String displayName = attributes.getValue(IXMLTags.ATTR_DISPLAY_NAME);
+ String data = attributes.getValue(IXMLTags.ATTR_DATA);
+ if (data != null && data.isBlank()) {
+ data = null;
+ }
+ fTestCase = (TestCaseElement) fTestRunSession.createTestElement(fTestSuite, getNextId(), testName, false, 1,
+ isDynamicTest, displayName, data);
+ fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE)));
+ fTestCase.setIgnored(Boolean.parseBoolean(attributes.getValue(IXMLTags.ATTR_IGNORED)));
+ readDuration(fTestCase, attributes);
+ break;
+ }
+ case IXMLTags.NODE_ERROR:
+ // TODO: multiple failures: https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296
+ fStatus = Result.ERROR;
+ fFailureBuffer = new StringBuilder();
+ break;
+ case IXMLTags.NODE_FAILURE:
+ // TODO: multiple failures: https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296
+ fStatus = Result.FAILURE;
+ fFailureBuffer = new StringBuilder();
+ break;
+ case IXMLTags.NODE_EXPECTED:
+ fInExpected = true;
+ fExpectedBuffer = new StringBuilder();
+ break;
+ case IXMLTags.NODE_ACTUAL:
+ fInActual = true;
+ fActualBuffer = new StringBuilder();
+ break;
+ // not interested
+ case IXMLTags.NODE_SYSTEM_OUT:
+ case IXMLTags.NODE_SYSTEM_ERR:
+ break;
+ case IXMLTags.NODE_SKIPPED:
+ // before Ant 1.9.0: not an Ant JUnit tag, see
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=276068
+ // later: child of <suite> or <test>, see
+ // https://issues.apache.org/bugzilla/show_bug.cgi?id=43969
+ fStatus = Result.OK;
+ fFailureBuffer = new StringBuilder();
+ String message = attributes.getValue(IXMLTags.ATTR_MESSAGE);
+ if (message != null) {
+ fFailureBuffer.append(message).append('\n');
+ }
+ break;
+ default:
+ throw new SAXParseException("unknown node '" + qName + "'", fLocator); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ }
+
+ private void readDuration(ITestElement testElement, Attributes attributes) {
+ if (testElement instanceof TestElement) {
+ TestElement element = (TestElement) testElement;
+ String timeString = attributes.getValue(IXMLTags.ATTR_DURATION);
+ if (timeString != null) {
+ try {
+ double seconds = Double.parseDouble(timeString);
+ long millis = (long) (seconds * 1000);
+ element.setDuration(Duration.ofMillis(millis));
+ } catch (NumberFormatException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ if (fInExpected) {
+ fExpectedBuffer.append(ch, start, length);
+
+ } else if (fInActual) {
+ fActualBuffer.append(ch, start, length);
+
+ } else if (fFailureBuffer != null) {
+ fFailureBuffer.append(ch, start, length);
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ switch (qName) {
+ // OK
+ case IXMLTags.NODE_TESTRUN:
+ break;
+ // OK
+ case IXMLTags.NODE_TESTSUITES:
+ break;
+ case IXMLTags.NODE_TESTSUITE:
+ handleTestElementEnd(fTestSuite);
+ fTestSuite = fTestSuite.getParent();
+ // TODO: end suite: compare counters?
+ break;
+ // OK
+ case IXMLTags.NODE_PROPERTIES:
+ case IXMLTags.NODE_PROPERTY:
+ break;
+ case IXMLTags.NODE_TESTCASE:
+ handleTestElementEnd(fTestCase);
+ fTestCase = null;
+ break;
+ case IXMLTags.NODE_FAILURE:
+ case IXMLTags.NODE_ERROR: {
+ ITestElement testElement = fTestCase;
+ if (testElement == null)
+ testElement = fTestSuite;
+ handleFailure(testElement);
+ break;
+ }
+ case IXMLTags.NODE_EXPECTED:
+ fInExpected = false;
+ if (fFailureBuffer != null) {
+ // skip whitespace from before <expected> and <actual> nodes
+ fFailureBuffer.setLength(0);
+ }
+ break;
+ case IXMLTags.NODE_ACTUAL:
+ fInActual = false;
+ if (fFailureBuffer != null) {
+ // skip whitespace from before <expected> and <actual> nodes
+ fFailureBuffer.setLength(0);
+ }
+ break;
+ // OK
+ case IXMLTags.NODE_SYSTEM_OUT:
+ case IXMLTags.NODE_SYSTEM_ERR:
+ break;
+ case IXMLTags.NODE_SKIPPED: {
+ TestElement testElement = fTestCase;
+ if (testElement == null)
+ testElement = fTestSuite;
+ if (fFailureBuffer != null && fFailureBuffer.length() > 0) {
+ handleFailure(testElement);
+ testElement.setAssumptionFailed(true);
+ } else if (fTestCase != null) {
+ fTestCase.setIgnored(true);
+ } else { // not expected
+ testElement.setAssumptionFailed(true);
+ }
+ break;
+ }
+ default:
+ handleUnknownNode(qName);
+ break;
+ }
+ }
+
+ private void handleTestElementEnd(ITestElement testElement) {
+ boolean completed = !fNotRun.pop().booleanValue();
+ fTestRunSession.registerTestEnded((TestElement) testElement, completed);
+ }
+
+ private void handleFailure(ITestElement testElement) {
+ if (fFailureBuffer != null) {
+ fTestRunSession.registerTestFailureStatus((TestElement) testElement, fStatus,
+ new FailureTrace(fFailureBuffer.toString(), toString(fExpectedBuffer), toString(fActualBuffer)));
+ fFailureBuffer = null;
+ fExpectedBuffer = null;
+ fActualBuffer = null;
+ fStatus = null;
+ }
+ }
+
+ private String toString(StringBuilder buffer) {
+ return buffer != null ? buffer.toString() : null;
+ }
+
+ private void handleUnknownNode(String qName) throws SAXException {
+ // TODO: just log if debug option is enabled?
+ String msg = "unknown node '" + qName + "'"; //$NON-NLS-1$//$NON-NLS-2$
+ if (fLocator != null) {
+ msg += " at line " + fLocator.getLineNumber() + ", column " + fLocator.getColumnNumber(); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ throw new SAXException(msg);
+ }
+
+ @Override
+ public void error(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ @Override
+ public void warning(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ private String getNextId() {
+ return Integer.toString(fId++);
+ }
+
+ /**
+ * @return the parsed test run session, or <code>null</code>
+ */
+ public TestRunSession getTestRunSession() {
+ return fTestRunSession;
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunSessionSerializer.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunSessionSerializer.java
new file mode 100644
index 000000000..4147bfb80
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunSessionSerializer.java
@@ -0,0 +1,316 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.junitXmlReport;
+
+import java.io.IOException;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.eclipse.unittest.internal.model.ProgressState;
+import org.eclipse.unittest.internal.model.TestCaseElement;
+import org.eclipse.unittest.internal.model.TestElement;
+import org.eclipse.unittest.internal.model.TestRunSession;
+import org.eclipse.unittest.internal.model.TestSuiteElement;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestElement.FailureTrace;
+import org.eclipse.unittest.model.ITestElement.Result;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+
+/**
+ * A {@link TestRunSession} object serializer
+ */
+public class TestRunSessionSerializer implements XMLReader {
+
+ private static final String EMPTY = ""; //$NON-NLS-1$
+ private static final String CDATA = "CDATA"; //$NON-NLS-1$
+ private static final Attributes NO_ATTS = new AttributesImpl();
+
+ private final TestRunSession fTestRunSession;
+ private ContentHandler fHandler;
+ private ErrorHandler fErrorHandler;
+
+ /**
+ * @param testRunSession the test run session to serialize
+ */
+ public TestRunSessionSerializer(TestRunSession testRunSession) {
+ Assert.isNotNull(testRunSession);
+ fTestRunSession = testRunSession;
+ }
+
+ @Override
+ public void parse(InputSource input) throws IOException, SAXException {
+ if (fHandler == null)
+ throw new SAXException("ContentHandler missing"); //$NON-NLS-1$
+
+ fHandler.startDocument();
+ handleTestRun();
+ fHandler.endDocument();
+ }
+
+ private void handleTestRun() throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ addCDATA(atts, IXMLTags.ATTR_NAME, fTestRunSession.getTestRunName());
+
+ ILaunchConfiguration launchConfig = fTestRunSession.getLaunch() != null
+ ? fTestRunSession.getLaunch().getLaunchConfiguration()
+ : null;
+ if (launchConfig != null) {
+ addCDATA(atts, IXMLTags.ATTR_LAUNCH_CONFIG_NAME, launchConfig.getName());
+ }
+
+ Integer total = fTestRunSession.getFinalTestCaseCount();
+ if (total != null) {
+ addCDATA(atts, IXMLTags.ATTR_TESTS, total.intValue());
+ }
+ addCDATA(atts, IXMLTags.ATTR_STARTED, fTestRunSession.countStartedTestCases());
+ addCDATA(atts, IXMLTags.ATTR_FAILURES, fTestRunSession.getCurrentFailureCount());
+ addCDATA(atts, IXMLTags.ATTR_ERRORS, fTestRunSession.getCurrentErrorCount());
+ addCDATA(atts, IXMLTags.ATTR_IGNORED, fTestRunSession.getCurrentIgnoredCount());
+ addCDATA(atts, IXMLTags.ATTR_START_TIME, fTestRunSession.getStartTime().toString());
+ if (fTestRunSession.getDuration() != null) {
+ addCDATA(atts, IXMLTags.ATTR_DURATION, fTestRunSession.getDuration().toString());
+ }
+ startElement(IXMLTags.NODE_TESTRUN, atts);
+
+ for (ITestElement topSuite : fTestRunSession.getChildren()) {
+ handleTestElement(topSuite);
+ }
+
+ endElement(IXMLTags.NODE_TESTRUN);
+ }
+
+ private void handleTestElement(ITestElement testElement) throws SAXException {
+ if (testElement instanceof TestSuiteElement) {
+ TestSuiteElement testSuiteElement = (TestSuiteElement) testElement;
+
+ AttributesImpl atts = new AttributesImpl();
+ // Need to store the full #getTestName instead of only the #getSuiteTypeName for
+ // test factory methods
+ addCDATA(atts, IXMLTags.ATTR_NAME, testSuiteElement.getTestName());
+ if (testSuiteElement.getDuration() != null) {
+ addCDATA(atts, IXMLTags.ATTR_DURATION,
+ Double.toString(testSuiteElement.getDuration().toMillis() / 1000.));
+ }
+ if (testSuiteElement.getProgressState() != ProgressState.COMPLETED
+ || testSuiteElement.getTestResult(false) != Result.UNDEFINED)
+ addCDATA(atts, IXMLTags.ATTR_INCOMPLETE, Boolean.TRUE.toString());
+ if (testSuiteElement.getDisplayName() != null) {
+ addCDATA(atts, IXMLTags.ATTR_DISPLAY_NAME, testSuiteElement.getDisplayName());
+ }
+ if (testSuiteElement.getData() != null) {
+ addCDATA(atts, IXMLTags.ATTR_DATA, testSuiteElement.getData());
+ }
+ startElement(IXMLTags.NODE_TESTSUITE, atts);
+ addFailure(testSuiteElement);
+
+ for (ITestElement child : testSuiteElement.getChildren()) {
+ handleTestElement(child);
+ }
+ endElement(IXMLTags.NODE_TESTSUITE);
+
+ } else if (testElement instanceof TestCaseElement) {
+ TestCaseElement testCaseElement = (TestCaseElement) testElement;
+
+ AttributesImpl atts = new AttributesImpl();
+ if (testCaseElement.getDuration() != null) {
+ addCDATA(atts, IXMLTags.ATTR_DURATION, testCaseElement.getDuration().toString());
+ }
+ if (testCaseElement.getProgressState() != ProgressState.COMPLETED)
+ addCDATA(atts, IXMLTags.ATTR_INCOMPLETE, Boolean.TRUE.toString());
+ if (testCaseElement.isIgnored())
+ addCDATA(atts, IXMLTags.ATTR_IGNORED, Boolean.TRUE.toString());
+ if (testCaseElement.isDynamicTest()) {
+ addCDATA(atts, IXMLTags.ATTR_DYNAMIC_TEST, Boolean.TRUE.toString());
+ }
+ if (testCaseElement.getDisplayName() != null) {
+ addCDATA(atts, IXMLTags.ATTR_DISPLAY_NAME, testCaseElement.getDisplayName());
+ }
+ if (testCaseElement.getData() != null) {
+ addCDATA(atts, IXMLTags.ATTR_DATA, testCaseElement.getData());
+ }
+ startElement(IXMLTags.NODE_TESTCASE, atts);
+ addFailure(testCaseElement);
+
+ endElement(IXMLTags.NODE_TESTCASE);
+
+ } else {
+ throw new IllegalStateException(String.valueOf(testElement));
+ }
+
+ }
+
+ private void addFailure(TestElement testElement) throws SAXException {
+ FailureTrace failureTrace = testElement.getFailureTrace();
+
+ if (testElement.isAssumptionFailure()) {
+ startElement(IXMLTags.NODE_SKIPPED, NO_ATTS);
+ if (failureTrace != null) {
+ addCharacters(failureTrace.getTrace());
+ }
+ endElement(IXMLTags.NODE_SKIPPED);
+
+ } else if (failureTrace != null) {
+ AttributesImpl failureAtts = new AttributesImpl();
+// addCDATA(failureAtts, IXMLTags.ATTR_MESSAGE, xx);
+// addCDATA(failureAtts, IXMLTags.ATTR_TYPE, xx);
+ String failureKind = testElement.getTestResult(false) == Result.ERROR ? IXMLTags.NODE_ERROR
+ : IXMLTags.NODE_FAILURE;
+ startElement(failureKind, failureAtts);
+ String expected = failureTrace.getExpected();
+ String actual = failureTrace.getActual();
+ if (expected != null) {
+ startElement(IXMLTags.NODE_EXPECTED, NO_ATTS);
+ addCharacters(expected);
+ endElement(IXMLTags.NODE_EXPECTED);
+ }
+ if (actual != null) {
+ startElement(IXMLTags.NODE_ACTUAL, NO_ATTS);
+ addCharacters(actual);
+ endElement(IXMLTags.NODE_ACTUAL);
+ }
+ String trace = failureTrace.getTrace();
+ addCharacters(trace);
+ endElement(failureKind);
+ }
+ }
+
+ private void startElement(String name, Attributes atts) throws SAXException {
+ fHandler.startElement(EMPTY, name, name, atts);
+ }
+
+ private void endElement(String name) throws SAXException {
+ fHandler.endElement(EMPTY, name, name);
+ }
+
+ private static void addCDATA(AttributesImpl atts, String name, int value) {
+ addCDATA(atts, name, Integer.toString(value));
+ }
+
+ private static void addCDATA(AttributesImpl atts, String name, String value) {
+ atts.addAttribute(EMPTY, EMPTY, name, CDATA, value);
+ }
+
+ private void addCharacters(String string) throws SAXException {
+ string = escapeNonUnicodeChars(string);
+ fHandler.characters(string.toCharArray(), 0, string.length());
+ }
+
+ /**
+ * Replaces all non-Unicode characters in the given string.
+ *
+ * @param string a string
+ * @return string with Java-escapes
+ */
+ private static String escapeNonUnicodeChars(String string) {
+ StringBuilder buf = null;
+ for (int i = 0; i < string.length(); i++) {
+ char ch = string.charAt(i);
+ if (!(ch == 9 || ch == 10 || ch == 13 || ch >= 32)) {
+ if (buf == null) {
+ buf = new StringBuilder(string.substring(0, i));
+ }
+ buf.append("\\u"); //$NON-NLS-1$
+ String hex = Integer.toHexString(ch);
+ for (int j = hex.length(); j < 4; j++)
+ buf.append('0');
+ buf.append(hex);
+ } else if (buf != null) {
+ buf.append(ch);
+ }
+ }
+ if (buf != null) {
+ return buf.toString();
+ }
+ return string;
+ }
+
+ @Override
+ public void setContentHandler(ContentHandler handler) {
+ this.fHandler = handler;
+ }
+
+ @Override
+ public ContentHandler getContentHandler() {
+ return fHandler;
+ }
+
+ @Override
+ public void setErrorHandler(ErrorHandler handler) {
+ fErrorHandler = handler;
+ }
+
+ @Override
+ public ErrorHandler getErrorHandler() {
+ return fErrorHandler;
+ }
+
+ // ignored:
+
+ @Override
+ public void parse(String systemId) throws IOException, SAXException {
+ // Nothing to do
+ }
+
+ @Override
+ public void setDTDHandler(DTDHandler handler) {
+ // Nothing to do
+ }
+
+ @Override
+ public DTDHandler getDTDHandler() {
+ return null;
+ }
+
+ @Override
+ public void setEntityResolver(EntityResolver resolver) {
+ // Nothing to do
+ }
+
+ @Override
+ public EntityResolver getEntityResolver() {
+ return null;
+ }
+
+ @Override
+ public void setProperty(java.lang.String name, java.lang.Object value) {
+ // Nothing to do
+ }
+
+ @Override
+ public Object getProperty(java.lang.String name) {
+ return null;
+ }
+
+ @Override
+ public void setFeature(java.lang.String name, boolean value) {
+ // Nothing to do
+ }
+
+ @Override
+ public boolean getFeature(java.lang.String name) {
+ return false;
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestListenerRegistry.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestListenerRegistry.java
new file mode 100644
index 000000000..a6ed3eaf9
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestListenerRegistry.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2018 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.launcher;
+
+import org.eclipse.unittest.internal.ui.UITestRunListener;
+
+import org.eclipse.core.runtime.ListenerList;
+
+/**
+ * Test View Support registry
+ */
+public class TestListenerRegistry {
+
+ /**
+ * Returns a {@link TestListenerRegistry} object instance
+ *
+ * @return a {@link TestListenerRegistry} object
+ */
+ public static TestListenerRegistry getDefault() {
+ if (fgRegistry != null)
+ return fgRegistry;
+
+ fgRegistry = new TestListenerRegistry();
+ return fgRegistry;
+ }
+
+ private static TestListenerRegistry fgRegistry;
+
+ /**
+ * List storing the registered test run listeners
+ */
+ private ListenerList<TestRunListener> fUnitTestRunListeners = new ListenerList<>();
+
+ private TestListenerRegistry() {
+ }
+
+ /**
+ * @return a <code>ListenerList</code> of all <code>TestRunListener</code>s
+ */
+ public ListenerList<TestRunListener> getUnitTestRunListeners() {
+ loadUnitTestRunListeners();
+ return fUnitTestRunListeners;
+ }
+
+ /**
+ * Initializes TestRun Listener extensions
+ */
+ private synchronized void loadUnitTestRunListeners() {
+ if (!fUnitTestRunListeners.isEmpty()) {
+ return;
+ }
+ fUnitTestRunListeners.add(new UITestRunListener());
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestRunListener.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestRunListener.java
new file mode 100644
index 000000000..0150734cd
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestRunListener.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.launcher;
+
+import org.eclipse.unittest.model.ITestCaseElement;
+import org.eclipse.unittest.model.ITestRunSession;
+
+/**
+ * A test run listener default implementation.
+ *
+ * <p>
+ * A test run starts with the call to {@link #sessionLaunched(ITestRunSession)}
+ * and {@link #sessionStarted(ITestRunSession)}, followed by calls to
+ * {@link #testCaseStarted(ITestCaseElement)} and
+ * {@link #testCaseFinished(ITestCaseElement)} for all test cases contained in
+ * the tree.
+ * </p>
+ * <p>
+ * A test run session is ended with the call to
+ * {@link #sessionFinished(ITestRunSession)}. After that call, no references
+ * must be kept to the session or any of the test cases or suites.
+ * </p>
+ */
+public class TestRunListener {
+
+ /**
+ * A test run session has been launched. The test tree is not available yet.
+ * <p>
+ * Important: The implementor of this method must not keep a reference to the
+ * session element after {@link #sessionFinished(ITestRunSession)} has finished.
+ * </p>
+ *
+ * @param session the session that has just been launched
+ */
+ public void sessionLaunched(ITestRunSession session) {
+ // does nothing
+ }
+
+ /**
+ * A test run session has started. The test tree can be accessed through the
+ * session element.
+ * <p>
+ * Important: The implementor of this method must not keep a reference to the
+ * session element after {@link #sessionFinished(ITestRunSession)} has finished.
+ * </p>
+ *
+ * @param session the session that has just started.
+ */
+ public void sessionStarted(ITestRunSession session) {
+ // does nothing
+ }
+
+ /**
+ * A test run session has finished. The test tree can be accessed through the
+ * session element.
+ *
+ * <p>
+ * Important: The implementor of this method must not keep the session element
+ * when the method is finished.
+ * </p>
+ *
+ * @param session the test
+ */
+ public void sessionFinished(ITestRunSession session) {
+ // does nothing
+ }
+
+ /**
+ * A test case has started. The result can be accessed from the element.
+ * <p>
+ * Important: The implementor of this method must not keep a reference to the
+ * test case element after {@link #sessionFinished(ITestRunSession)} has
+ * finished.
+ * </p>
+ *
+ * @param testCaseElement the test that has started to run
+ */
+ public void testCaseStarted(ITestCaseElement testCaseElement) {
+ // does nothing
+ }
+
+ /**
+ * A test case has ended. The result can be accessed from the element.
+ * <p>
+ * Important: The implementor of this method must not keep a reference to the
+ * test case element after {@link #sessionFinished(ITestRunSession)} has
+ * finished.
+ * </p>
+ *
+ * @param testCaseElement the test that has finished running
+ */
+ public void testCaseFinished(ITestCaseElement testCaseElement) {
+ // does nothing
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportExtension.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportExtension.java
new file mode 100644
index 000000000..7e61cfba3
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportExtension.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Victor Rubezhny, Mickael Istria (Red Hat Inc.) - Adaptation from JUnit
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.launcher;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.model.ModelMessages;
+import org.eclipse.unittest.ui.ITestViewSupport;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * A {@link TestViewSupportExtension} implementation
+ */
+public class TestViewSupportExtension {
+
+ private static final String ID = "id"; //$NON-NLS-1$
+ private static final String CLASS = "class"; //$NON-NLS-1$
+
+ private final IConfigurationElement fElement;
+
+ /**
+ * Constructs a {@link TestViewSupportExtension} object
+ *
+ * @param element an {@link IConfigurationElement} object
+ */
+ public TestViewSupportExtension(IConfigurationElement element) {
+ fElement = element;
+ }
+
+ /**
+ * Returns an identifier of the test view support extension
+ *
+ * @return A test view support identifier
+ */
+ public String getId() {
+ return getAttribute(ID);
+ }
+
+ /**
+ * Returns an attribute by a given name
+ *
+ * @param attributeName an attribute name
+ *
+ * @return a value of an attribute
+ */
+ protected String getAttribute(String attributeName) {
+ return fElement.getAttribute(attributeName);
+ }
+
+ /**
+ * Instantiates an {@link ITestViewSupport} object
+ *
+ * @return an instance of {@link ITestViewSupport} for the given extension.
+ * <code>null</code> if class couldn't be loaded.
+ * @throws CoreException In case of error during the Test View Support
+ * instantiation
+ */
+ ITestViewSupport instantiateTestViewSupport() throws CoreException {
+ try {
+ return (ITestViewSupport) fElement.createExecutableExtension(CLASS);
+ } catch (Exception e) {
+ throw new CoreException(new Status(IStatus.ERROR, UnitTestPlugin.PLUGIN_ID,
+ ModelMessages.UnitTestModel_could_not_instantiate_support, e));
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getId();
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportRegistry.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportRegistry.java
new file mode 100644
index 000000000..c8d2f8bd0
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportRegistry.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2018 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.launcher;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.ui.ITestViewSupport;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.Platform;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+
+/**
+ * Test View Support registry
+ */
+public class TestViewSupportRegistry {
+ /**
+ * An identifier of Test View Support extension point
+ */
+ public static final String ID_EXTENSION_POINT_TEST_VIEW_SUPPORTS = UnitTestPlugin.PLUGIN_ID + "." //$NON-NLS-1$
+ + "unittestViewSupport"; //$NON-NLS-1$
+
+ /**
+ * Returns an instance of {@link TestViewSupportRegistry} object
+ *
+ * @return a {@link TestViewSupportRegistry} object
+ */
+ public static TestViewSupportRegistry getDefault() {
+ if (fgRegistry != null)
+ return fgRegistry;
+
+ fgRegistry = new TestViewSupportRegistry(
+ Platform.getExtensionRegistry().getExtensionPoint(ID_EXTENSION_POINT_TEST_VIEW_SUPPORTS));
+ return fgRegistry;
+ }
+
+ private static TestViewSupportRegistry fgRegistry;
+
+ private final IExtensionPoint fPoint;
+ private List<TestViewSupportExtension> fTestViewSupportExtensions;
+
+ private TestViewSupportRegistry(IExtensionPoint point) {
+ fPoint = point;
+ }
+
+ /**
+ * Returns all the registered View Support extensions
+ *
+ * @return a {@link List} containing all the registered View Support extensions
+ */
+ private List<TestViewSupportExtension> getAllTestViewSupportExtensions() {
+ loadTestViewSupportExtensions();
+ return fTestViewSupportExtensions;
+ }
+
+ /**
+ * Returns all the registered View Support extensions that suit the specified
+ * filter
+ *
+ * @param filter a registry identifier filter
+ * @return an {@link ArrayList} containing the registry kings filtered by
+ * identifier
+ */
+ public List<TestViewSupportExtension> getTestViewSupportExtensions(final String filter) {
+ List<TestViewSupportExtension> all = getAllTestViewSupportExtensions();
+ if (all == null)
+ return Collections.emptyList();
+
+ return all.stream().filter(p -> p.getId().startsWith(filter)).collect(Collectors.toList());
+ }
+
+ private void loadTestViewSupportExtensions() {
+ if (fTestViewSupportExtensions != null)
+ return;
+
+ List<TestViewSupportExtension> items = getConfigurationElements().stream().map(TestViewSupportExtension::new)
+ .collect(Collectors.toList());
+
+ fTestViewSupportExtensions = items;
+ }
+
+ /*
+ * Returns an {@link Optional <ITestViewSupport>} object instance by its
+ * identifier
+ *
+ * @param id an identifier, can be <code>null</code>
+ *
+ * @return an {@link Optional <ITestViewSupport>} object instance
+ */
+ private Optional<ITestViewSupport> findTestViewSupport(String id) {
+ return getAllTestViewSupportExtensions().stream().filter(ext -> ext.getId().equals(id)).findFirst().map(t -> {
+ try {
+ return t.instantiateTestViewSupport();
+ } catch (CoreException e) {
+ UnitTestPlugin.log(e);
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Returns {@link ITestViewSupport} instance from the given launch configuration
+ *
+ * @param launchConfiguration a launch configuration
+ * @return an {@link Optional <ITestViewSupport>} object instance
+ */
+ public static Optional<ITestViewSupport> newTestRunnerViewSupport(ILaunchConfiguration launchConfiguration) {
+ try {
+ return getDefault().findTestViewSupport(launchConfiguration
+ .getAttribute(UnitTestLaunchConfigurationConstants.ATTR_UNIT_TEST_VIEW_SUPPORT, (String) null));
+ } catch (CoreException e) {
+ UnitTestPlugin.log(e);
+ return Optional.empty();
+ }
+ }
+
+ private List<IConfigurationElement> getConfigurationElements() {
+ List<IConfigurationElement> items = new ArrayList<>();
+ for (IExtension extension : fPoint.getExtensions()) {
+ Collections.addAll(items, extension.getConfigurationElements());
+ }
+ return items;
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/UnitTestLaunchConfigurationConstants.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/UnitTestLaunchConfigurationConstants.java
new file mode 100644
index 000000000..6431887d5
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/UnitTestLaunchConfigurationConstants.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.launcher;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.ui.ITestViewSupport;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+
+/**
+ * Attribute keys used by the UnitTest LaunchConfiguration. Note that these
+ * constants are not API and might change in the future.
+ */
+public class UnitTestLaunchConfigurationConstants {
+
+ /**
+ * An identifier of a property to be set on a {@link ILaunchConfiguration} to
+ * identify an {@link ITestViewSupport} implementation.
+ */
+ public static final String ATTR_UNIT_TEST_VIEW_SUPPORT = UnitTestPlugin.PLUGIN_ID + ".TEST_VIEW_SUPPPORT"; //$NON-NLS-1$
+
+ private UnitTestLaunchConfigurationConstants() {
+ // No instance allowed
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionListener.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionListener.java
new file mode 100644
index 000000000..d647ec149
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionListener.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.model;
+
+import org.eclipse.unittest.model.ITestRunSession;
+
+/**
+ * An interface to listen to the events from the on added/removed
+ * {@link ITestRunSession} instances.
+ */
+public interface ITestRunSessionListener {
+ /**
+ * Notifies on an added {@link ITestRunSession} instance.
+ *
+ * @param testRunSession the new session
+ */
+ void sessionAdded(ITestRunSession testRunSession);
+
+ /**
+ * Notifies on a removed {@link ITestRunSession} instance.
+ *
+ * @param testRunSession the new session
+ */
+ void sessionRemoved(ITestRunSession testRunSession);
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionReport.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionReport.java
new file mode 100644
index 000000000..acb0ac094
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionReport.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Red Hat Inc. and others
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.unittest.internal.model;
+
+/**
+ * An {@link ITestRunSessionReport} object
+ */
+public interface ITestRunSessionReport {
+
+ /**
+ * Returns the name of the test run. The name is the name of the launch
+ * configuration use to run this test.
+ *
+ * @return returns the test run name
+ */
+ String getTestRunName();
+
+ /**
+ * Indicates if the test run session is starting
+ *
+ * @return <code>true</code> in case of the test session is starting, otherwise
+ * returns <code>false</code>
+ */
+ boolean isStarting();
+
+ /**
+ * Indicates if the test run session is running
+ *
+ * @return <code>true</code> in case of the test session is running, otherwise
+ * returns <code>false</code>
+ */
+ boolean isRunning();
+
+ /**
+ * Indicates if the test run session has been stopped or terminated
+ *
+ * @return <code>true</code> if the session has been stopped or terminated,
+ * otherwise returns <code>false</code>
+ */
+ boolean isStopped();
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestSessionListener.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestSessionListener.java
new file mode 100644
index 000000000..ed35d4ff4
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestSessionListener.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.model;
+
+import java.time.Duration;
+
+import org.eclipse.unittest.model.ITestCaseElement;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestElement.FailureTrace;
+import org.eclipse.unittest.model.ITestElement.Result;
+
+/**
+ * A listener interface for observing the execution of a test session (initial
+ * run and reruns).
+ */
+public interface ITestSessionListener {
+ /**
+ * A test run has started.
+ */
+ void sessionStarted();
+
+ /**
+ * A test run has ended.
+ *
+ * @param duration the total elapsed time of the test run
+ */
+ void sessionCompleted(Duration duration);
+
+ /**
+ * A test run has been stopped prematurely.
+ *
+ * @param duration the time elapsed before the test run was stopped
+ */
+ void sessionAborted(Duration duration);
+
+ /**
+ * A test has been added to the plan.
+ *
+ * @param testElement the test
+ */
+ void testAdded(ITestElement testElement);
+
+ /**
+ * All test have been added and running begins
+ */
+ void runningBegins();
+
+ /**
+ * An individual test has started.
+ *
+ * @param testCaseElement the test
+ */
+ void testStarted(ITestCaseElement testCaseElement);
+
+ /**
+ * An individual test has ended.
+ *
+ * @param testCaseElement the test
+ */
+ void testEnded(ITestCaseElement testCaseElement);
+
+ /**
+ * An individual test has failed with a stack trace.
+ *
+ * @param testElement the test
+ * @param status the outcome of the test; one of
+ * {@link org.eclipse.unittest.model.ITestElement.Result#ERROR}
+ * or
+ * {@link org.eclipse.unittest.model.ITestElement.Result#FAILURE}
+ * @param trace the stack trace
+ */
+ void testFailed(ITestElement testElement, Result status, FailureTrace trace);
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.java
new file mode 100644
index 000000000..f2ddf946d
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.model;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Unit Test Model messages
+ */
+public class ModelMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.unittest.internal.model.ModelMessages"; //$NON-NLS-1$
+
+ public static String UnitTestModel_could_not_instantiate_support;
+ public static String UnitTestModel_could_not_import;
+ public static String UnitTestModel_could_not_export;
+ public static String UnitTestModel_could_not_read;
+ public static String UnitTestModel_could_not_write;
+ public static String UnitTestModel_importing_from_url;
+ public static String TestRunHandler_lines_read;
+
+ public static String TestingSession_finished_status;
+ public static String TestingSession_name_format;
+ public static String TestingSession_starting_status;
+ public static String TestingSession_stopped_status;
+
+ public static String TestRunSession_unrootedTests;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, ModelMessages.class);
+ }
+
+ private ModelMessages() {
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.properties b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.properties
new file mode 100644
index 000000000..521f0eff3
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.properties
@@ -0,0 +1,31 @@
+###############################################################################
+# Copyright (c) 2009, 2010 IBM Corporation and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+
+# From JUnit Model
+UnitTestModel_could_not_instantiate_support=A Test View Support instance couldn't be instantiated.
+UnitTestModel_could_not_export=Test run could not be exported.
+UnitTestModel_could_not_import=Test run could not be imported.
+UnitTestModel_could_not_write=The test run could not be written to file ''{0}''.
+UnitTestModel_could_not_read=The test run could not be imported from file ''{0}''.
+UnitTestModel_importing_from_url=Importing from URL...
+TestRunHandler_lines_read={0} lines read
+
+# From CDT Model
+TestingSession_name_format={0} ({1})
+TestingSession_finished_status=Finished after {0} seconds
+TestingSession_starting_status=Starting...
+TestingSession_stopped_status=Testing was stopped by user
+
+TestRunSession_unrootedTests=Unrooted Tests
+
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ProgressState.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ProgressState.java
new file mode 100644
index 000000000..051d18526
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ProgressState.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.unittest.internal.model;
+
+/**
+ * Running states of a test.
+ */
+public enum ProgressState {
+ /** state that describes that the test element has not started */
+ NOT_STARTED("Not Started"), //$NON-NLS-1$
+ /** state that describes that the test element is running */
+ RUNNING("Running"), //$NON-NLS-1$
+ /**
+ * state that describes that the test element has been stopped before being
+ * completed
+ */
+ ABORTED("Aborted"), //$NON-NLS-1$
+ /** state that describes that the test element has completed */
+ COMPLETED("Completed"); //$NON-NLS-1$
+
+ private String fName;
+
+ private ProgressState(String name) {
+ fName = name;
+ }
+
+ @Override
+ public String toString() {
+ return fName;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/Status.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/Status.java
new file mode 100644
index 000000000..b9bd597f8
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/Status.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.model;
+
+import org.eclipse.unittest.model.ITestElement.Result;
+
+/**
+ * An object describing a test current status
+ */
+public final class Status {
+ public static final Status RUNNING_ERROR = new Status("RUNNING_ERROR"); //$NON-NLS-1$
+ public static final Status RUNNING_FAILURE = new Status("RUNNING_FAILURE"); //$NON-NLS-1$
+ public static final Status RUNNING = new Status("RUNNING"); //$NON-NLS-1$
+
+ public static final Status ERROR = new Status("ERROR"); //$NON-NLS-1$
+ public static final Status FAILURE = new Status("FAILURE"); //$NON-NLS-1$
+ public static final Status OK = new Status("OK"); //$NON-NLS-1$
+ public static final Status NOT_RUN = new Status("NOT_RUN"); //$NON-NLS-1$
+
+ private static final Status[] OLD_CODE = { OK, ERROR, FAILURE };
+
+ private final String fName;
+
+ private Status(String name) {
+ fName = name;
+ }
+
+ @Override
+ public String toString() {
+ return fName;
+ }
+
+ /* error state predicates */
+
+ /**
+ * Indicates if current test status is OK
+ *
+ * @return <code>true</code> if current status is OK, otherwise returns
+ * <code>false</code>
+ */
+ public boolean isOK() {
+ return this == OK || this == RUNNING || this == NOT_RUN;
+ }
+
+ /**
+ * Indicates if current test has failures
+ *
+ * @return <code>true</code> if current test has failures, otherwise return
+ * <code>false</code>
+ */
+ public boolean isFailure() {
+ return this == FAILURE || this == RUNNING_FAILURE;
+ }
+
+ /**
+ * Indicates if current test has errors
+ *
+ * @return <code>true</code> if current test has errors, otherwise return
+ * <code>false</code>
+ */
+ public boolean isError() {
+ return this == ERROR || this == RUNNING_ERROR;
+ }
+
+ /**
+ * Indicates if current test has errors and/or failures
+ *
+ * @return <code>true</code> if current test has errors and/or failures,
+ * otherwise return <code>false</code>
+ */
+ public boolean isErrorOrFailure() {
+ return isError() || isFailure();
+ }
+
+ /* progress state predicates */
+
+ /**
+ * Indicates if current test isn't run yet
+ *
+ * @return <code>true</code> if current test isn't run yet, otherwise return
+ * <code>false</code>
+ */
+ public boolean isNotRun() {
+ return this == NOT_RUN;
+ }
+
+ /**
+ * Indicates if current test is running
+ *
+ * @return <code>true</code> if current test is running, otherwise return
+ * <code>false</code>
+ */
+ public boolean isRunning() {
+ return this == RUNNING || this == RUNNING_FAILURE || this == RUNNING_ERROR;
+ }
+
+ public boolean isDone() {
+ return this == OK || this == FAILURE || this == ERROR;
+ }
+
+ /**
+ * Converts from an old status code to a {@link Status} constant
+ *
+ * @param oldStatus one of {@link Status}'s constants
+ * @return the {@link Status} constant
+ */
+ public static Status convert(int oldStatus) {
+ return OLD_CODE[oldStatus];
+ }
+
+ /**
+ * Converts the current {@link Status} object into a
+ * {@link org.eclipse.unittest.model.ITestElement.Result} object
+ *
+ * @return a {@link org.eclipse.unittest.model.ITestElement.Result} object
+ * instance
+ */
+ public Result convertToResult() {
+ if (isNotRun())
+ return Result.UNDEFINED;
+ if (isError())
+ return Result.ERROR;
+ if (isFailure())
+ return Result.FAILURE;
+ if (isRunning()) {
+ return Result.UNDEFINED;
+ }
+ return Result.OK;
+ }
+
+ /**
+ * Converts the current {@link Status} object into a {@link ProgressState}
+ * object
+ *
+ * @return a {@link ProgressState} object instance
+ */
+ public ProgressState convertToProgressState() {
+ if (isRunning()) {
+ return ProgressState.RUNNING;
+ }
+ if (isDone()) {
+ return ProgressState.COMPLETED;
+ }
+ return ProgressState.NOT_STARTED;
+ }
+
+ /**
+ * Creates a {@link Status} object from a given
+ * {@link org.eclipse.unittest.model.ITestElement.Result} object
+ *
+ * @param status a {@link Status} object
+ * @return an {@link org.eclipse.unittest.model.ITestElement.Result} object
+ * instance
+ */
+ public static Status fromResult(Result status) {
+ switch (status) {
+ case ERROR:
+ return Status.ERROR;
+ case FAILURE:
+ return Status.FAILURE;
+ case OK:
+ return Status.OK;
+ case IGNORED:
+ return Status.OK;
+ case UNDEFINED:
+ return Status.NOT_RUN;
+ default:
+ return Status.NOT_RUN;
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestCaseElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestCaseElement.java
new file mode 100644
index 000000000..9e0c4f075
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestCaseElement.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.model;
+
+import org.eclipse.unittest.model.ITestCaseElement;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * A test case element. Holds all information about a test case
+ */
+public class TestCaseElement extends TestElement implements ITestCaseElement {
+
+ private boolean fIgnored;
+ private boolean fIsDynamicTest;
+
+ /**
+ * Constructs a {@link TestCaseElement} object
+ *
+ * @param parent a parent {@link TestSuiteElement} object
+ * @param id an identifier of the object
+ * @param testName a name of the test case
+ * @param displayName a display name for a test case
+ * @param isDynamicTest an indicator of a "dynamic" test case
+ * @param uniqueId an unique test case identifier or <code>null</code>
+ */
+ public TestCaseElement(TestSuiteElement parent, String id, String testName, String displayName,
+ boolean isDynamicTest, String uniqueId) {
+ super(parent, id, testName, displayName, uniqueId);
+ Assert.isNotNull(parent);
+ fIsDynamicTest = isDynamicTest;
+ }
+
+ @Override
+ public Result getTestResult(boolean includeChildren) {
+ return isIgnored() ? Result.IGNORED : super.getTestResult(includeChildren);
+ }
+
+ public void setIgnored(boolean ignored) {
+ fIgnored = ignored;
+ }
+
+ @Override
+ public boolean isIgnored() {
+ return fIgnored;
+ }
+
+ @Override
+ public String toString() {
+ return "TestCase: " + super.toString(); //$NON-NLS-1$
+ }
+
+ @Override
+ public boolean isDynamicTest() {
+ return fIsDynamicTest;
+ }
+
+ @Override
+ Integer getFinalTestCaseCount() {
+ return Integer.valueOf(1);
+ }
+
+ @Override
+ int countStartedTestCases() {
+ return getProgressState() != ProgressState.NOT_STARTED || testStartedInstant != null ? 1 : 0;
+ }
+
+ @Override
+ int getCurrentFailureCount() {
+ return getStatus() == Status.FAILURE ? 1 : 0;
+ }
+
+ @Override
+ int getCurrentAssumptionFailureCount() {
+ return isAssumptionFailure() ? 1 : 0;
+ }
+
+ @Override
+ int getCurrentIgnoredCount() {
+ return isIgnored() ? 1 : 0;
+ }
+
+ @Override
+ int getCurrentErrorCount() {
+ return getStatus() == Status.ERROR ? 1 : 0;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestElement.java
new file mode 100644
index 000000000..7171e12f4
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestElement.java
@@ -0,0 +1,313 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.model;
+
+import java.time.Duration;
+import java.time.Instant;
+
+import org.eclipse.unittest.model.ITestElement;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * A test element n holds basic information about a test case or a test suite
+ * element
+ */
+public abstract class TestElement implements ITestElement {
+ private final TestSuiteElement fParent;
+ private final String fId;
+ private final String fTestName;
+ /**
+ * Extra (runner-specific) data, can be <code>null</code>
+ */
+ private final String fData;
+
+ /**
+ * The display name of the test element, can be <code>null</code>. In that case,
+ * use {@link TestElement#fTestName fTestName}.
+ */
+ private final String fDisplayName;
+
+ private Status fStatus;
+ protected FailureTrace fTrace;
+
+ private boolean fAssumptionFailed;
+
+ protected Instant testStartedInstant = null;
+ protected Duration fDuration = null;
+
+ /**
+ * Constructs the test element object
+ *
+ * @param parent the parent, can be <code>null</code>
+ * @param id the test id
+ * @param testName the test name
+ * @param displayName the test display name, can be <code>null</code>
+ * @param data some runner-specific data, can be <code>null</code>
+ */
+ public TestElement(TestSuiteElement parent, String id, String testName, String displayName, String data) {
+ Assert.isNotNull(id);
+ Assert.isNotNull(testName);
+ fParent = parent;
+ fId = id;
+ fTestName = testName;
+ fDisplayName = displayName;
+ fData = data;
+ fStatus = Status.NOT_RUN;
+ if (parent != null) {
+ parent.addChild(this);
+ } else if (!(this instanceof TestRunSession)) {
+ throw new IllegalArgumentException("Test elements must have a parent"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Returns the progress state of this test element.
+ * <ul>
+ * <li>{@link ProgressState#NOT_STARTED}: the test has not yet started</li>
+ * <li>{@link ProgressState#RUNNING}: the test is currently running</li>
+ * <li>{@link ProgressState#ABORTED}: the test has stopped before being
+ * completed</li>
+ * <li>{@link ProgressState#COMPLETED}: the test (and all its children) has
+ * completed</li>
+ * </ul>
+ *
+ * @return returns one of {@link ProgressState#NOT_STARTED},
+ * {@link ProgressState#RUNNING}, {@link ProgressState#ABORTED} or
+ * {@link ProgressState#COMPLETED}.
+ */
+ public ProgressState getProgressState() {
+ return getStatus().convertToProgressState();
+ }
+
+ /**
+ * Returns the result of the test element.
+ * <ul>
+ * <li>{@link org.eclipse.unittest.model.ITestElement.Result#UNDEFINED}: the
+ * result is not yet evaluated</li>
+ * <li>{@link org.eclipse.unittest.model.ITestElement.Result#OK}: the test has
+ * succeeded</li>
+ * <li>{@link org.eclipse.unittest.model.ITestElement.Result#ERROR}: the test
+ * has returned an error</li>
+ * <li>{@link org.eclipse.unittest.model.ITestElement.Result#FAILURE}: the test
+ * has returned an failure</li>
+ * <li>{@link org.eclipse.unittest.model.ITestElement.Result#IGNORED}: the test
+ * has been ignored (skipped)</li>
+ * </ul>
+ *
+ * @param includeChildren if <code>true</code>, the returned result is the
+ * combined result of the test and its children (if it
+ * has any). If <code>false</code>, only the test's
+ * result is returned.
+ *
+ * @return returns one of
+ * {@link org.eclipse.unittest.model.ITestElement.Result#UNDEFINED},
+ * {@link org.eclipse.unittest.model.ITestElement.Result#OK},
+ * {@link org.eclipse.unittest.model.ITestElement.Result#ERROR},
+ * {@link org.eclipse.unittest.model.ITestElement.Result#FAILURE} or
+ * {@link org.eclipse.unittest.model.ITestElement.Result#IGNORED}.
+ * Clients should also prepare for other, new values.
+ */
+ public Result getTestResult(boolean includeChildren) {
+ if (fAssumptionFailed) {
+ return Result.IGNORED;
+ }
+ return getStatus().convertToResult();
+ }
+
+ /**
+ * Returns the parent test element container or <code>null</code> if the test
+ * element is the test run session.
+ *
+ * @return the parent test suite
+ */
+ public TestSuiteElement getParentContainer() {
+ return fParent;
+ }
+
+ @Override
+ public FailureTrace getFailureTrace() {
+ Result testResult = getTestResult(false);
+ if ((testResult == Result.ERROR || testResult == Result.FAILURE
+ || (testResult == Result.IGNORED) && fTrace != null)) {
+ return fTrace;
+ }
+ return null;
+ }
+
+ @Override
+ public TestSuiteElement getParent() {
+ return fParent;
+ }
+
+ @Override
+ public String getId() {
+ return fId;
+ }
+
+ @Override
+ public String getTestName() {
+ return fTestName;
+ }
+
+ /**
+ * Sets the current test element status
+ *
+ * @param status one of {@link Status#NOT_RUN}, {@link Status#OK},
+ * {@link Status#ERROR} or {@link Status#FAILURE}.
+ */
+ public void setStatus(Status status) {
+ if (status == Status.RUNNING) {
+ testStartedInstant = Instant.now();
+ } else if (status.convertToProgressState() == ProgressState.COMPLETED && testStartedInstant != null) {
+ this.fDuration = Duration.between(testStartedInstant, Instant.now());
+ }
+
+ fStatus = status;
+ TestSuiteElement parent = getParent();
+ if (parent != null) {
+ parent.childChangedStatus(this, status);
+ }
+ }
+
+ /**
+ * Sets the extended status for this test element
+ *
+ * @param status one of {@link Status#NOT_RUN}, {@link Status#OK},
+ * {@link Status#ERROR} or {@link Status#FAILURE}.
+ * @param failureTrace stacktracee/error message or null
+ */
+ public void setStatus(Status status, FailureTrace failureTrace) {
+ if (failureTrace != null && fTrace != null) {
+ // don't overwrite first trace if same test run logs multiple errors
+ fTrace = new FailureTrace(fTrace.getTrace() + failureTrace.getTrace(), fTrace.getExpected(),
+ fTrace.getActual());
+ } else {
+ fTrace = failureTrace;
+ }
+ setStatus(status);
+ }
+
+ /**
+ * Returns the status of this test element
+ * <ul>
+ * <li>{@link Status#NOT_RUN}: the test has not executed</li>
+ * <li>{@link Status#OK}: the test is successful</li>
+ * <li>{@link Status#ERROR}: the test had an error</li>
+ * <li>{@link Status#FAILURE}: the test had an assertion failure</li>
+ * </ul>
+ *
+ * @return returns one of {@link Status#NOT_RUN}, {@link Status#OK},
+ * {@link Status#ERROR} or {@link Status#FAILURE}.
+ */
+ public Status getStatus() {
+ return fStatus;
+ }
+
+ /**
+ * Sets a duration value for a test element
+ *
+ * @param duration a duration value
+ */
+ public void setDuration(Duration duration) {
+ this.fDuration = duration;
+ }
+
+ @Override
+ public Duration getDuration() {
+ return this.fDuration;
+ }
+
+ /**
+ * Sets up the assumption failure flag for this test
+ *
+ * @param assumptionFailed a flag indicating the assumption failure
+ */
+ public void setAssumptionFailed(boolean assumptionFailed) {
+ fAssumptionFailed = assumptionFailed;
+ }
+
+ /**
+ * Indicates if there was an assumption failure
+ *
+ * @return true if there was a comparison failure, otherwise return false
+ */
+ public boolean isAssumptionFailure() {
+ return fAssumptionFailed;
+ }
+
+ @Override
+ public String toString() {
+ return getTestName() + '[' + getProgressState() + " - " + getTestResult(true) + ']'; //$NON-NLS-1$
+ }
+
+ @Override
+ public String getDisplayName() {
+ return fDisplayName != null ? fDisplayName : getTestName();
+ }
+
+ @Override
+ public String getData() {
+ return fData;
+ }
+
+ @Override
+ public TestRunSession getTestRunSession() {
+ return getParent().getTestRunSession();
+ }
+
+ /**
+ * Returns the total number of expected test case elements, or the total number
+ * of ran test case elements if completed, or <code>null</null> if tests are
+ * still running and we can't know the final count.
+ *
+ * @return a total number of test cases
+ */
+ abstract Integer getFinalTestCaseCount();
+
+ /**
+ * Returns the number of started test case elements
+ *
+ * @return a number of started test cases
+ */
+ abstract int countStartedTestCases();
+
+ /**
+ * Returns the number of failed test case elements
+ *
+ * @return a number of failed test cases
+ */
+ abstract int getCurrentFailureCount();
+
+ /**
+ * Returns the number of assumption failures
+ *
+ * @return a number of assumption failures
+ */
+ abstract int getCurrentAssumptionFailureCount();
+
+ /**
+ * Returns the number of ignored test case elements
+ *
+ * @return a number of ignored test cases
+ */
+ abstract int getCurrentIgnoredCount();
+
+ /**
+ * Returns the number of test case elements with errors
+ *
+ * @return a number of test cases with errors
+ */
+ abstract int getCurrentErrorCount();
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunListenerAdapter.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunListenerAdapter.java
new file mode 100644
index 000000000..f3a04a1d5
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunListenerAdapter.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.model;
+
+import java.time.Duration;
+
+import org.eclipse.unittest.internal.launcher.TestListenerRegistry;
+import org.eclipse.unittest.internal.launcher.TestRunListener;
+import org.eclipse.unittest.model.ITestCaseElement;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestElement.FailureTrace;
+import org.eclipse.unittest.model.ITestElement.Result;
+
+import org.eclipse.core.runtime.ListenerList;
+
+/**
+ * Notifier for the callback listener API {@link TestRunListener}.
+ */
+public class TestRunListenerAdapter implements ITestSessionListener {
+
+ private final TestRunSession fSession;
+
+ /**
+ * Constructs a {@link TestRunListenerAdapter} object
+ *
+ * @param session a {@link TestRunSession} object
+ */
+ public TestRunListenerAdapter(TestRunSession session) {
+ fSession = session;
+ }
+
+ private ListenerList<TestRunListener> getListenerList() {
+ return TestListenerRegistry.getDefault().getUnitTestRunListeners();
+ }
+
+ private void fireSessionStarted() {
+ for (TestRunListener listener : getListenerList()) {
+ listener.sessionStarted(fSession);
+ }
+ }
+
+ private void fireSessionFinished() {
+ for (TestRunListener listener : getListenerList()) {
+ listener.sessionFinished(fSession);
+ }
+ }
+
+ private void fireTestCaseStarted(ITestCaseElement testCaseElement) {
+ for (TestRunListener listener : getListenerList()) {
+ listener.testCaseStarted(testCaseElement);
+ }
+ }
+
+ private void fireTestCaseFinished(ITestCaseElement testCaseElement) {
+ for (TestRunListener listener : getListenerList()) {
+ listener.testCaseFinished(testCaseElement);
+ }
+ }
+
+ @Override
+ public void sessionStarted() {
+ // wait until all test are added
+ }
+
+ @Override
+ public void sessionCompleted(Duration duration) {
+ fireSessionFinished();
+ }
+
+ @Override
+ public void sessionAborted(Duration duration) {
+ fireSessionFinished();
+ }
+
+ @Override
+ public void testAdded(ITestElement testElement) {
+ // do nothing
+ }
+
+ @Override
+ public void runningBegins() {
+ fireSessionStarted();
+ }
+
+ @Override
+ public void testStarted(ITestCaseElement testCaseElement) {
+ fireTestCaseStarted(testCaseElement);
+ }
+
+ @Override
+ public void testEnded(ITestCaseElement testCaseElement) {
+ fireTestCaseFinished(testCaseElement);
+ }
+
+ @Override
+ public void testFailed(ITestElement testElement, Result status, FailureTrace trace) {
+ // ignore
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunSession.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunSession.java
new file mode 100644
index 000000000..333ee659d
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunSession.java
@@ -0,0 +1,749 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.model;
+
+import java.text.DateFormat;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.launcher.TestViewSupportRegistry;
+import org.eclipse.unittest.launcher.ITestRunnerClient;
+import org.eclipse.unittest.model.ITestCaseElement;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestRunSession;
+import org.eclipse.unittest.model.ITestSuiteElement;
+import org.eclipse.unittest.ui.ITestViewSupport;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.ILaunchesListener2;
+import org.eclipse.debug.core.Launch;
+import org.eclipse.debug.core.model.ISourceLocator;
+
+/**
+ * A test run session holds all information about a test run, i.e. launch
+ * configuration, launch, test tree (including results).
+ */
+public class TestRunSession extends TestSuiteElement implements ITestRunSession, ITestRunSessionReport {
+
+ /**
+ * The launch, or <code>null</code> iff this session was run externally.
+ */
+ private final ILaunch fLaunch;
+ private final String fTestRunName;
+ private final ITestViewSupport fTestRunnerSupport;
+
+ /**
+ * Test runner client or <code>null</code>.
+ */
+ private ITestRunnerClient fTestRunnerClient;
+
+ private final ListenerList<ITestSessionListener> fSessionListeners;
+ private final TestSessionNotifier fSessionNotifier = new TestSessionNotifier();
+
+ /**
+ * The test run session's cached result, or <code>null</code> if
+ * <code>fTestRoot != null</code>.
+ */
+ private Result fTestResult;
+
+ /**
+ * Map from testId to testElement.
+ */
+ private HashMap<String, TestElement> fIdToTest;
+
+ volatile Instant fStartTime;
+ volatile Integer fPredefinedTestCount;
+
+ volatile boolean fIsAborted;
+ private Integer predefinedTestCount;
+ private boolean completedOrAborted;
+
+ /**
+ * Constructs a test run session object.
+ *
+ * @param testRunName name of the test run
+ * @param startTime a start time of a test run
+ * @param launchConfiguration a launch configuration for a test run
+ */
+ public TestRunSession(String testRunName, Instant startTime, ILaunchConfiguration launchConfiguration) {
+ super(null, "-1", testRunName, null, null, null); //$NON-NLS-1$
+ // TODO: check assumptions about non-null fields
+
+ fLaunch = new NoopLaunch(launchConfiguration, ILaunchManager.RUN_MODE, null);
+ fTestRunnerSupport = TestViewSupportRegistry.newTestRunnerViewSupport(launchConfiguration).orElse(null);
+
+ Assert.isNotNull(testRunName);
+ fTestRunName = testRunName;
+
+ fIdToTest = new HashMap<>();
+
+ fTestRunnerClient = null;
+ fStartTime = startTime;
+
+ fSessionListeners = new ListenerList<>();
+ }
+
+ /**
+ * Constructs a test run session object from a launch.
+ *
+ * @param launch an {@link ILaunch} object
+ */
+ public TestRunSession(ILaunch launch) {
+ super(null, "-1", "<TestRunSession>", null, null, null); //$NON-NLS-1$ //$NON-NLS-2$
+ Assert.isNotNull(launch);
+
+ fLaunch = launch;
+
+ ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration();
+ if (launchConfiguration != null) {
+ fTestRunName = launchConfiguration.getName();
+ fTestRunnerSupport = TestViewSupportRegistry.newTestRunnerViewSupport(launchConfiguration).orElse(null);
+ } else {
+ fTestRunName = "<TestRunSession>"; //$NON-NLS-1$
+ fTestRunnerSupport = null;
+ }
+
+ fIdToTest = new HashMap<>();
+
+ if (fTestRunnerSupport != null) {
+ fTestRunnerClient = fTestRunnerSupport.newTestRunnerClient(this);
+ }
+
+ final ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ launchManager.addLaunchListener(new ILaunchesListener2() {
+ @Override
+ public void launchesTerminated(ILaunch[] launches) {
+ if (Arrays.asList(launches).contains(fLaunch)) {
+ launchManager.removeLaunchListener(this);
+ }
+ }
+
+ @Override
+ public void launchesRemoved(ILaunch[] launches) {
+ if (Arrays.asList(launches).contains(fLaunch)) {
+ launchManager.removeLaunchListener(this);
+ }
+ }
+
+ @Override
+ public void launchesChanged(ILaunch[] launches) {
+ // do nothing
+ }
+
+ @Override
+ public void launchesAdded(ILaunch[] launches) {
+ // do nothing
+ }
+ });
+ fSessionListeners = new ListenerList<>();
+ setStatus(Status.RUNNING);
+ addTestSessionListener(new TestRunListenerAdapter(this));
+ fTestRunnerClient.startMonitoring();
+ }
+
+ /**
+ * Resets a test run session object (used when re-creating a test run session
+ * from an XML file(
+ *
+ * TODO Consider removal as it's only use in XML parsing
+ */
+ public void reset() {
+ fTestResult = null;
+ fIdToTest = new HashMap<>();
+ }
+
+ @Override
+ public ProgressState getProgressState() {
+ if (isRunning()) {
+ return ProgressState.RUNNING;
+ }
+ if (isStopped()) {
+ return ProgressState.ABORTED;
+ }
+ return ProgressState.COMPLETED;
+ }
+
+ @Override
+ public FailureTrace getFailureTrace() {
+ return null;
+ }
+
+ @Override
+ public TestSuiteElement getParentContainer() {
+ return null;
+ }
+
+ @Override
+ public TestRunSession getTestRunSession() {
+ return this;
+ }
+
+ /**
+ * Returns the Test Runner View Support for which this test run session has been
+ * launched, or <code>null</code> if not available.
+ *
+ * @return the test runner view support, or <code>null</code> is not available.
+ */
+ public ITestViewSupport getTestViewSupport() {
+ return fTestRunnerSupport;
+ }
+
+ /**
+ * @return the launch, or <code>null</code> iff this session was run externally
+ */
+ @Override
+ public ILaunch getLaunch() {
+ return fLaunch;
+ }
+
+ @Override
+ public String getTestRunName() {
+ return fTestRunName;
+ }
+
+ @Override
+ public int getCurrentErrorCount() {
+ return getChildren().stream().mapToInt(TestElement::getCurrentErrorCount).sum();
+ }
+
+ @Override
+ public int getCurrentFailureCount() {
+ return getChildren().stream().mapToInt(TestElement::getCurrentFailureCount).sum();
+ }
+
+ @Override
+ public int getCurrentAssumptionFailureCount() {
+ return getChildren().stream().mapToInt(TestElement::getCurrentAssumptionFailureCount).sum();
+ }
+
+ @Override
+ public int getCurrentIgnoredCount() {
+ return getChildren().stream().mapToInt(TestElement::getCurrentIgnoredCount).sum();
+ }
+
+ /**
+ * Returns start time for a run session
+ *
+ * @return an {@link Instant} object indicating a test run session start time
+ */
+ public Instant getStartTime() {
+ return fStartTime;
+ }
+
+ /**
+ * Indicates if the test run session has been stopped or terminated
+ *
+ * @return <code>true</code> if the session has been stopped or terminated,
+ * otherwise returns <code>false</code>
+ */
+ @Override
+ public boolean isStopped() {
+ return fIsAborted;
+ }
+
+ /**
+ * Adds an {@link ITestSessionListener} listener
+ *
+ * @param listener an {@link ITestSessionListener} object
+ */
+ public synchronized void addTestSessionListener(ITestSessionListener listener) {
+ fSessionListeners.add(listener);
+ }
+
+ /**
+ * Removes an {@link ITestSessionListener} listener
+ *
+ * @param listener an {@link ITestSessionListener} object
+ */
+ public void removeTestSessionListener(ITestSessionListener listener) {
+ fSessionListeners.remove(listener);
+ }
+
+ @Override
+ public boolean isStarting() {
+ return getStartTime() == null && fLaunch != null && !fLaunch.isTerminated();
+ }
+
+ /**
+ * Forces a test run session to abort its execution
+ */
+ public void abortTestRun() {
+ fIsAborted = true;
+ if (fTestRunnerClient != null) {
+ fTestRunnerClient.stopTest();
+ fTestRunnerClient.stopMonitoring();
+ }
+ }
+
+ @Override
+ public boolean isRunning() {
+ return getStartTime() != null && fTestRunnerClient != null && !completedOrAborted;
+ }
+
+ @Override
+ public TestElement getTestElement(String id) {
+ return fIdToTest.get(id);
+ }
+
+ private TestElement addTreeEntry(String id, String testName, boolean isSuite, Integer testCount,
+ boolean isDynamicTest, TestSuiteElement parent, String displayName, String data) {
+ return createTestElement(parent != null ? parent : this, id, testName, isSuite, testCount, isDynamicTest,
+ displayName, data);
+ }
+
+ /**
+ * Creates a test element, either {@link ITestSuiteElement} or
+ * {@link ITestCaseElement} instance, depending on the arguments.
+ *
+ * @param parent a parent test suite element
+ * @param id an identifier of the test element
+ * @param testName a name of the test element
+ * @param isSuite a flag indicating if the test element should be
+ * represented by a test suite element
+ * @param testCount a number of predefined test cases in case of test suite
+ * element
+ * @param isDynamicTest a flag indicating that test suite is dynamic (that
+ * doesn't have predefined tests)
+ * @param displayName a display name for the test element
+ * @param data some test runner specific data, can be <code>null</code>
+ * @return a created {@link ITestSuiteElement} or {@link ITestCaseElement}
+ * instance
+ */
+ public TestElement createTestElement(TestSuiteElement parent, String id, String testName, boolean isSuite,
+ Integer testCount, boolean isDynamicTest, String displayName, String data) {
+ TestElement testElement;
+ if (isSuite) {
+ TestSuiteElement testSuiteElement = new TestSuiteElement(parent != null ? parent : this, id, testName,
+ testCount, displayName, data);
+ testElement = testSuiteElement;
+ } else {
+ testElement = new TestCaseElement(parent != null ? parent : this, id, testName, displayName, isDynamicTest,
+ data);
+ }
+ fIdToTest.put(id, testElement);
+ return testElement;
+ }
+
+ private final class NoopLaunch extends Launch {
+ private NoopLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) {
+ super(launchConfiguration, mode, locator);
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return true;
+ }
+
+ @Override
+ public boolean isDisconnected() {
+ return true;
+ }
+ }
+
+ /**
+ * Listens to events from the and translates {@link ITestRunnerClient} them into
+ * high-level model events (broadcasted to {@link ITestSessionListener}s).
+ */
+ private class TestSessionNotifier {
+
+ private boolean firstStart;
+
+ /**
+ * Notifies on a test run started
+ *
+ * @param testCount number of tests in this run
+ */
+ public void testRunStarted(Integer testCount) {
+ fStartTime = Instant.now();
+ fPredefinedTestCount = testCount;
+
+ for (ITestSessionListener listener : fSessionListeners) {
+ listener.sessionStarted();
+ }
+ }
+
+ /**
+ * Notifies on a test run ended
+ *
+ * @param duration a duration of this test run
+ */
+ public void testRunEnded(Duration duration) {
+ for (ITestSessionListener listener : fSessionListeners) {
+ listener.sessionCompleted(duration);
+ }
+ }
+
+ /**
+ * Notifies on a test run stopped (aborted)
+ *
+ * @param duration a duration of this test run
+ */
+ public void testRunStopped(Duration duration) {
+ fIsAborted = true;
+
+ for (ITestSessionListener listener : fSessionListeners) {
+ listener.sessionAborted(duration);
+ }
+ }
+
+ /**
+ * Notifies on a test tree entry is created
+ *
+ * @param testId an identifier of a test entry
+ * @param testName a name of test
+ * @param isSuite <code>true</code> indicates that a test entry is a test
+ * suite, <code>false</code> - a test case
+ * @param testCount a number of tests in a test suite or <code>null</code>
+ * @param isDynamicTest indicates if a test is a dynamic test (doesn't have a
+ * predefined number of tests)
+ * @param parent a parent test suite element
+ * @param displayName a display name for the test
+ * @param uniqueId an unique identifier of test entry or <code>null</code>
+ * @return an {@link ITestElement} object instance
+ */
+ public ITestElement testTreeEntry(String testId, String testName, boolean isSuite, Integer testCount,
+ boolean isDynamicTest, ITestSuiteElement parent, String displayName, String uniqueId) {
+ ITestElement testElement = addTreeEntry(testId, testName, isSuite, testCount, isDynamicTest,
+ (TestSuiteElement) parent, displayName, uniqueId);
+
+ for (ITestSessionListener listener : fSessionListeners) {
+ listener.testAdded(testElement);
+ }
+ return testElement;
+ }
+
+ /**
+ * Notifies on a test started
+ *
+ * @param test a test element object
+ */
+ public void testStarted(ITestElement test) {
+ if (!(test instanceof TestCaseElement)) {
+ return;
+ }
+ if (firstStart) {
+ for (ITestSessionListener listener : fSessionListeners) {
+ listener.runningBegins();
+ }
+ firstStart = false;
+ }
+ setStatus(test, Status.RUNNING);
+
+ for (ITestSessionListener listener : fSessionListeners) {
+ listener.testStarted((ITestCaseElement) test);
+ }
+ }
+
+ /**
+ * Notifies on a test ended
+ *
+ * @param testElement a test element object
+ * @param isIgnored indicates a skipped (not run) test element
+ */
+ public void testEnded(ITestElement testElement, boolean isIgnored) {
+ if (testElement == null) {
+ return;
+ }
+ if (!(testElement instanceof TestCaseElement)) {
+ if (isIgnored) {
+ ((TestElement) testElement).setAssumptionFailed(true);
+ setStatus(testElement, Status.OK);
+ } else {
+ logUnexpectedTest(testElement.getId(), testElement);
+ }
+ return;
+ }
+ TestCaseElement testCaseElement = (TestCaseElement) testElement;
+ if (isIgnored) {
+ testCaseElement.setIgnored(true);
+ }
+
+ if (testCaseElement.getStatus() == Status.RUNNING)
+ setStatus(testCaseElement, Status.OK);
+
+ for (ITestSessionListener listener : fSessionListeners) {
+ listener.testEnded(testCaseElement);
+ }
+ }
+
+ /**
+ * Notifies on a failed test element
+ *
+ * @param testElement a failed test element
+ * @param status a result status of test execution
+ * @param isAssumptionFailed indicates if the failure is an assumption failure
+ * @param trace a failure trace
+ */
+ public void testFailed(ITestElement testElement, Result status, boolean isAssumptionFailed,
+ FailureTrace trace) {
+ if (testElement == null) {
+ return;
+ }
+
+ if (isAssumptionFailed) {
+ ((TestElement) testElement).setAssumptionFailed(true);
+ status = Result.OK;
+ }
+
+ registerTestFailureStatus((TestElement) testElement, status, trace);
+
+ for (ITestSessionListener listener : fSessionListeners) {
+ listener.testFailed(testElement, status, trace);
+ }
+ }
+
+ private void logUnexpectedTest(String testId, ITestElement testElement) {
+ UnitTestPlugin
+ .log(new Exception("Unexpected TestElement type for testId '" + testId + "': " + testElement)); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ /**
+ * Registers a failure status for a test element
+ *
+ * @param testElement a test element
+ * @param status a result of test execution
+ * @param failureTrace a failure trace
+ */
+ public void registerTestFailureStatus(TestElement testElement, Result status, FailureTrace failureTrace) {
+ testElement.setStatus(Status.fromResult(status), failureTrace);
+ }
+
+ /**
+ * Registers an ended test element
+ *
+ * @param testElement a test element
+ * @param completed <code>true</code> indicates that the test was completed.
+ * <code>false</code> otherwise
+ */
+ public void registerTestEnded(TestElement testElement, boolean completed) {
+ if (testElement instanceof TestCaseElement) {
+ if (!completed) {
+ return;
+ }
+ TestCaseElement testCaseElement = (TestCaseElement) testElement;
+ if (!testCaseElement.getStatus().isErrorOrFailure())
+ setStatus(testElement, Status.OK);
+ }
+ }
+
+ /**
+ * Sets a {@link Status} for a test element
+ *
+ * @param testElement a test element
+ * @param status an execution status
+ */
+ private void setStatus(ITestElement testElement, Status status) {
+ ((TestElement) testElement).setStatus(status);
+ }
+
+ /**
+ * Returns an array of all failed {@link ITestElement}s
+ *
+ * @return an array of failed {@link ITestElement}s
+ */
+ public List<TestElement> getAllFailedTestElements() {
+ List<TestElement> failures = new ArrayList<>();
+ addFailures(failures, this);
+ return Collections.unmodifiableList(failures);
+ }
+
+ private void addFailures(Collection<TestElement> failures, TestElement testElement) {
+ Result testResult = testElement.getTestResult(true);
+ if (testResult == Result.ERROR || testResult == Result.FAILURE) {
+ failures.add(testElement);
+ }
+ if (testElement instanceof TestSuiteElement) {
+ TestSuiteElement testSuiteElement = (TestSuiteElement) testElement;
+ for (TestElement child : testSuiteElement.getChildren()) {
+ addFailures(failures, child);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return fTestRunName + " " + DateFormat.getDateTimeInstance().format(new Date(fStartTime.toEpochMilli())); //$NON-NLS-1$
+ }
+
+ @Override
+ public TestSuiteElement getParent() {
+ return null;
+ }
+
+ @Override
+ public String getTestName() {
+ return getTestRunName();
+ }
+
+ /**
+ * An abstract base class for a listener safe runnable
+ */
+ public abstract class ListenerSafeRunnable implements ISafeRunnable {
+ @Override
+ public void handleException(Throwable exception) {
+ UnitTestPlugin.log(exception);
+ }
+ }
+
+ @Override
+ public TestCaseElement newTestCase(String testId, String testName, ITestSuiteElement parent, String displayName,
+ String data) {
+ return (TestCaseElement) fSessionNotifier.testTreeEntry(testId, testName, false, Integer.valueOf(1), false,
+ parent, displayName, data);
+ }
+
+ @Override
+ public TestSuiteElement newTestSuite(String testId, String testName, Integer testCount, ITestSuiteElement parent,
+ String displayName, String data) {
+ return (TestSuiteElement) fSessionNotifier.testTreeEntry(testId, testName, true, testCount, testCount == null,
+ parent, displayName, data);
+ }
+
+ @Override
+ public void notifyTestSessionAborted(final Duration reportDuration, Exception cause) {
+ if (isStopped()) {
+ return;
+ }
+ if (reportDuration != null) {
+ setDuration(reportDuration);
+ }
+ fTestRunnerClient.stopMonitoring();
+ this.completedOrAborted = true;
+ SafeRunner.run(new ListenerSafeRunnable() {
+ @Override
+ public void run() {
+ fSessionNotifier.testRunStopped(fDuration);
+ }
+ });
+ }
+
+ @Override
+ public void notifyTestSessionCompleted(final Duration reportDuration) {
+ if (isStopped()) {
+ return;
+ }
+ if (reportDuration != null) {
+ setDuration(reportDuration);
+ }
+ fTestRunnerClient.stopMonitoring();
+ this.completedOrAborted = true;
+ SafeRunner.run(new ListenerSafeRunnable() {
+ @Override
+ public void run() {
+ fSessionNotifier.testRunEnded(fDuration);
+ }
+ });
+ }
+
+ @Override
+ public void notifyTestEnded(ITestElement test, boolean isIgnored) {
+ if (isStopped()) {
+ return;
+ }
+ SafeRunner.run(new ListenerSafeRunnable() {
+ @Override
+ public void run() {
+ fSessionNotifier.testEnded(test, isIgnored);
+ }
+ });
+ }
+
+ @Override
+ public void notifyTestStarted(ITestElement test) {
+ if (isStopped()) {
+ return;
+ }
+ SafeRunner.run(new ListenerSafeRunnable() {
+ @Override
+ public void run() {
+ fSessionNotifier.testStarted(test);
+ }
+ });
+ }
+
+ @Override
+ public void notifyTestSessionStarted(final Integer count) {
+ if (isStopped()) {
+ return;
+ }
+ this.predefinedTestCount = count;
+ SafeRunner.run(new ListenerSafeRunnable() {
+ @Override
+ public void run() {
+ fSessionNotifier.testRunStarted(count);
+ }
+ });
+ }
+
+ @Override
+ public void notifyTestFailed(ITestElement test, Result status, boolean isAssumptionFailed,
+ FailureTrace failureTrace) {
+ if (isStopped()) {
+ return;
+ }
+ if (status != Result.FAILURE && status != Result.ERROR) {
+ throw new IllegalArgumentException("Status has to be FAILURE or ERROR"); //$NON-NLS-1$
+ }
+ SafeRunner.run(new ListenerSafeRunnable() {
+ @Override
+ public void run() {
+ fSessionNotifier.testFailed(test, status, isAssumptionFailed, failureTrace);
+ }
+ });
+ }
+
+ @Override
+ public Integer getFinalTestCaseCount() {
+ if (predefinedTestCount != null) {
+ return predefinedTestCount;
+ }
+ if (getChildren().isEmpty()) {
+ return null;
+ }
+ if (!isRunning()) {
+ int res = 0;
+ for (TestElement child : getChildren()) {
+ Integer childCount = child.getFinalTestCaseCount();
+ if (childCount == null) {
+ return null;
+ }
+ res += childCount.intValue();
+ }
+ return Integer.valueOf(res);
+ }
+ return null;
+ }
+
+ @Override
+ public Result getTestResult(boolean includeChildren) {
+ return this.fTestResult != null ? this.fTestResult : super.getTestResult(includeChildren);
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestSuiteElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestSuiteElement.java
new file mode 100644
index 000000000..f285502df
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestSuiteElement.java
@@ -0,0 +1,287 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.model;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestSuiteElement;
+
+/**
+ * A test suite element. Holds all information about a test case
+ */
+public class TestSuiteElement extends TestElement implements ITestSuiteElement {
+
+ private final List<TestElement> fChildren;
+ private Status fChildrenStatus;
+ private Integer expectedTestCount;
+
+ /**
+ * Constructs a test suite object
+ *
+ * @param parent a parent {@link TestSuiteElement} object
+ * @param id an identifier of a test suite
+ * @param testName a name of test suite
+ * @param expectedChildrenCount an expected children count
+ * @param displayName a display name of test suite
+ * @param data an optional additional data for a test suite
+ */
+ public TestSuiteElement(TestSuiteElement parent, String id, String testName, Integer expectedChildrenCount,
+ String displayName, String data) {
+ super(parent, id, testName, displayName, data);
+ this.expectedTestCount = expectedChildrenCount;
+ fChildren = new ArrayList<>(expectedChildrenCount == null ? 0 : expectedChildrenCount.intValue());
+ }
+
+ @Override
+ public Result getTestResult(boolean includeChildren) {
+ if (includeChildren) {
+ return getStatus().convertToResult();
+ } else {
+ return super.getStatus().convertToResult();
+ }
+ }
+
+ @Override
+ public List<TestElement> getChildren() {
+ return Collections.unmodifiableList(fChildren);
+ }
+
+ /**
+ * Adds a child {@link ITestElement} to this test suite element
+ *
+ * @param child a child {@link ITestElement}
+ */
+ public void addChild(TestElement child) {
+ fChildren.add(child);
+ }
+
+ /**
+ * Removes a child {@link ITestElement} from this test suite element
+ *
+ * @param child a child {@link ITestElement}
+ */
+ public void removeChild(TestElement child) {
+ fChildren.remove(child);
+ }
+
+ @Override
+ public Status getStatus() {
+ Status suiteStatus = getSuiteStatus();
+ if (fChildrenStatus != null) {
+ // must combine children and suite status here, since failures can occur e.g. in
+ // @AfterClass
+ return combineStatus(fChildrenStatus, suiteStatus);
+ } else {
+ return suiteStatus;
+ }
+ }
+
+ private Status getCumulatedStatus() {
+ TestElement[] children = fChildren.toArray(new TestElement[fChildren.size()]); // copy list to avoid concurreny
+ // problems
+ if (children.length == 0)
+ return getSuiteStatus();
+
+ Status cumulated = children[0].getStatus();
+
+ for (int i = 1; i < children.length; i++) {
+ Status childStatus = children[i].getStatus();
+ cumulated = combineStatus(cumulated, childStatus);
+ }
+ // not necessary, see special code in Status.combineProgress()
+// if (suiteStatus.isErrorOrFailure() && cumulated.isNotRun())
+// return suiteStatus; //progress is Done if error in Suite and no children run
+ return cumulated;
+ }
+
+ /**
+ * Returns a test suite execution status
+ *
+ * @return a test suite execution status
+ */
+ public Status getSuiteStatus() {
+ return super.getStatus();
+ }
+
+ /**
+ * Notifies on the status changes in a specified child {@link ITestElement}
+ * element
+ *
+ * @param child a child {@link ITestElement} element
+ * @param childStatus a new status value
+ */
+ public void childChangedStatus(ITestElement child, Status childStatus) {
+ int childCount = fChildren.size();
+ if (child == fChildren.get(0) && childStatus.isRunning()) {
+ // is first child, and is running -> copy status
+ internalSetChildrenStatus(childStatus);
+ return;
+ }
+ TestElement lastChild = fChildren.get(childCount - 1);
+ if (child == lastChild) {
+ if (childStatus.isDone()) {
+ // all children done, collect cumulative status
+ internalSetChildrenStatus(getCumulatedStatus());
+ return;
+ }
+ // go on (child could e.g. be a TestSuiteElement with RUNNING_FAILURE)
+
+ } else if (!lastChild.getStatus().isNotRun()) {
+ // child is not last, but last child has been run -> child has been rerun or is
+ // rerunning
+ internalSetChildrenStatus(getCumulatedStatus());
+ return;
+ }
+
+ // finally, set RUNNING_FAILURE/ERROR if child has failed but suite has not
+ // failed:
+ if (childStatus.isFailure()) {
+ if (fChildrenStatus == null || !fChildrenStatus.isErrorOrFailure()) {
+ internalSetChildrenStatus(Status.RUNNING_FAILURE);
+ return;
+ }
+ } else if (childStatus.isError()) {
+ if (fChildrenStatus == null || !fChildrenStatus.isError()) {
+ internalSetChildrenStatus(Status.RUNNING_ERROR);
+ return;
+ }
+ }
+ }
+
+ private void internalSetChildrenStatus(Status status) {
+ if (fChildrenStatus == status)
+ return;
+
+ if (status == Status.RUNNING) {
+ if (fDuration != null) {
+ // re-running child: ignore change
+ } else {
+ testStartedInstant = Instant.now();
+ }
+ } else if (status.convertToProgressState() == ProgressState.COMPLETED && fDuration == null
+ && testStartedInstant != null) {
+ fDuration = Duration.between(testStartedInstant, Instant.now());
+ }
+
+ fChildrenStatus = status;
+
+ TestSuiteElement parent = getParent();
+ if (parent != null) {
+ parent.childChangedStatus(this, getStatus());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "TestSuite: " + getTestName() + " : " + super.toString() + " (" + fChildren.size() + ")"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ }
+
+ private static Status combineStatus(Status one, Status two) {
+ Status progress = combineProgress(one, two);
+ Status error = combineError(one, two);
+ return combineProgressAndErrorStatus(progress, error);
+ }
+
+ private static Status combineProgress(Status one, Status two) {
+ if (one.isNotRun() && two.isNotRun()) {
+ return Status.NOT_RUN;
+ }
+ if (one.isDone() && two.isDone()) {
+ return Status.OK;
+ }
+ if (!one.isRunning() && !two.isRunning()) {
+ return Status.OK; // one done, one not-run -> a parent failed and its children are not run
+ }
+ return Status.RUNNING;
+ }
+
+ private static Status combineError(Status one, Status two) {
+ if (one.isError() || two.isError()) {
+ return Status.ERROR;
+ }
+ if (one.isFailure() || two.isFailure()) {
+ return Status.FAILURE;
+ }
+ return Status.OK;
+ }
+
+ private static Status combineProgressAndErrorStatus(Status progress, Status error) {
+ if (progress.isDone()) {
+ if (error.isError()) {
+ return Status.ERROR;
+ }
+ if (error.isFailure()) {
+ return Status.FAILURE;
+ }
+ return Status.OK;
+ }
+
+ if (progress.isNotRun()) {
+ return Status.NOT_RUN;
+ }
+
+ if (error.isError()) {
+ return Status.RUNNING_ERROR;
+ }
+ if (error.isFailure()) {
+ return Status.RUNNING_FAILURE;
+ }
+ return Status.RUNNING;
+ }
+
+ @Override
+ Integer getFinalTestCaseCount() {
+ if (expectedTestCount != null) {
+ return expectedTestCount;
+ }
+ if (getStatus().isDone()) {
+ return Integer.valueOf(getChildren().stream().map(TestElement::getFinalTestCaseCount)
+ .filter(Objects::nonNull).mapToInt(Integer::intValue).sum());
+ }
+ return null;
+ }
+
+ @Override
+ public int countStartedTestCases() {
+ return getChildren().stream().mapToInt(TestElement::countStartedTestCases).sum();
+ }
+
+ @Override
+ int getCurrentFailureCount() {
+ return getChildren().stream().mapToInt(TestElement::getCurrentFailureCount).sum();
+ }
+
+ @Override
+ int getCurrentAssumptionFailureCount() {
+ return getChildren().stream().mapToInt(TestElement::getCurrentAssumptionFailureCount).sum();
+ }
+
+ @Override
+ int getCurrentIgnoredCount() {
+ return getChildren().stream().mapToInt(TestElement::getCurrentIgnoredCount).sum();
+ }
+
+ @Override
+ int getCurrentErrorCount() {
+ return getChildren().stream().mapToInt(TestElement::getCurrentErrorCount).sum();
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestLaunchListener.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestLaunchListener.java
new file mode 100644
index 000000000..5801547e3
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestLaunchListener.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.model;
+
+import java.util.HashSet;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.launcher.TestListenerRegistry;
+import org.eclipse.unittest.internal.launcher.TestRunListener;
+import org.eclipse.unittest.internal.launcher.TestViewSupportRegistry;
+import org.eclipse.unittest.internal.launcher.UnitTestLaunchConfigurationConstants;
+import org.eclipse.unittest.ui.ITestViewSupport;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchListener;
+
+/**
+ * Used to track new launches. We need to do this so that we only attach a
+ * TestRunner once to a launch. Once a test runner is connected, it is removed
+ * from the set.
+ */
+public class UnitTestLaunchListener implements ILaunchListener {
+
+ /**
+ * Used to track new launches. We need to do this so that we only attach a
+ * TestRunner once to a launch. Once a test runner is connected, it is removed
+ * from the set.
+ */
+ private HashSet<ILaunch> fTrackedLaunches = new HashSet<>(20);
+
+ @Override
+ public void launchAdded(ILaunch launch) {
+ ILaunchConfiguration config = launch.getLaunchConfiguration();
+ if (config == null)
+ return;
+
+ try {
+ if (!config.hasAttribute(UnitTestLaunchConfigurationConstants.ATTR_UNIT_TEST_VIEW_SUPPORT))
+ return;
+ } catch (CoreException e1) {
+ UnitTestPlugin.log(e1);
+ return;
+ }
+
+ ITestViewSupport testRunnerViewSupport = TestViewSupportRegistry.newTestRunnerViewSupport(config).orElse(null);
+ if (testRunnerViewSupport == null)
+ return;
+
+ fTrackedLaunches.add(launch);
+ }
+
+ @Override
+ public void launchRemoved(final ILaunch launch) {
+ fTrackedLaunches.remove(launch);
+ }
+
+ @Override
+ public void launchChanged(final ILaunch launch) {
+ if (!fTrackedLaunches.contains(launch))
+ return;
+
+ // Load session on 1st change (usually 1st process added), although it's not
+ // much reliable. Each TestRunnerClient should take care of listening to the
+ // launch to get the right IProcess or stream or whatever else i useful
+ if (UnitTestModel.getInstance().getTestRunSessions().stream()
+ .noneMatch(session -> launch.equals(session.getLaunch()))) {
+ TestRunSession testRunSession = new TestRunSession(launch);
+ UnitTestModel.getInstance().addTestRunSession(testRunSession);
+ for (TestRunListener listener : TestListenerRegistry.getDefault().getUnitTestRunListeners()) {
+ listener.sessionLaunched(testRunSession);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestModel.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestModel.java
new file mode 100644
index 000000000..2e270c7ba
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestModel.java
@@ -0,0 +1,287 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.model;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.SAXException;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.UnitTestPreferencesConstants;
+import org.eclipse.unittest.internal.junitXmlReport.TestRunHandler;
+import org.eclipse.unittest.model.ITestRunSession;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * Central registry for Unit Test test runs.
+ */
+public final class UnitTestModel {
+
+ private final ListenerList<ITestRunSessionListener> fTestRunSessionListeners = new ListenerList<>();
+ /**
+ * Active test run sessions, youngest first.
+ */
+ private final LinkedList<TestRunSession> fTestRunSessions = new LinkedList<>();
+
+ private static UnitTestModel INSTANCE = null;
+
+ private UnitTestModel() {
+
+ }
+
+ /**
+ * Returns a {@link UnitTestModel} object instance
+ *
+ * @return a {@link UnitTestModel} object instance
+ */
+ public static synchronized UnitTestModel getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new UnitTestModel();
+ }
+ return INSTANCE;
+ }
+
+ /**
+ * Starts the model (called by the {@link UnitTestPlugin} on startup).
+ */
+ public void start() {
+ /*
+ * TODO: restore on restart: - only import headers! - only import last n
+ * sessions; remove all other files in historyDirectory
+ */
+// File historyDirectory= UnitTestPlugin.getHistoryDirectory();
+// File[] swapFiles= historyDirectory.listFiles();
+// if (swapFiles != null) {
+// Arrays.sort(swapFiles, new Comparator() {
+// public int compare(Object o1, Object o2) {
+// String name1= ((File) o1).getName();
+// String name2= ((File) o2).getName();
+// return name1.compareTo(name2);
+// }
+// });
+// for (int i= 0; i < swapFiles.length; i++) {
+// final File file= swapFiles[i];
+// SafeRunner.run(new ISafeRunnable() {
+// public void run() throws Exception {
+// importTestRunSession(file );
+// }
+// public void handleException(Throwable exception) {
+// UnitTestPlugin.log(exception);
+// }
+// });
+// }
+// }
+
+// addTestRunSessionListener(new LegacyTestRunSessionListener());
+ }
+
+ /**
+ * Stops the model (called by the {@link UnitTestPlugin} on shutdown).
+ */
+ public void stop() {
+// for (Iterator iter= fTestRunSessions.iterator(); iter.hasNext();) {
+// final TestRunSession session= (TestRunSession) iter.next();
+// SafeRunner.run(new ISafeRunnable() {
+// public void run() throws Exception {
+// session.swapOut();
+// }
+// public void handleException(Throwable exception) {
+// UnitTestPlugin.log(exception);
+// }
+// });
+// }
+ }
+
+ /**
+ * Adds an {@link ITestRunSessionListener} object
+ *
+ * @param listener a listener object
+ */
+ public void addTestRunSessionListener(ITestRunSessionListener listener) {
+ fTestRunSessionListeners.add(listener);
+ }
+
+ /**
+ * Removes an {@link ITestRunSessionListener} object
+ *
+ * @param listener a listener object
+ */
+ public void removeTestRunSessionListener(ITestRunSessionListener listener) {
+ fTestRunSessionListeners.remove(listener);
+ }
+
+ /**
+ * Returns a list of {@link TestRunSession} objects
+ *
+ * @return a list of {@link TestRunSession} objects
+ */
+ public synchronized List<TestRunSession> getTestRunSessions() {
+ return new ArrayList<>(fTestRunSessions);
+ }
+
+ /**
+ * Adds a specified {@link TestRunSession} object into the list of processed
+ * test run sessions.
+ *
+ * The list length is limited by the value of
+ * {@link UnitTestPreferencesConstants#MAX_TEST_RUNS} preference.
+ *
+ * @param testRunSession a {@link TestRunSession} object to be added
+ * @see org.eclipse.unittest.internal.UnitTestPreferencesConstants#MAX_TEST_RUNS
+ * @see org.eclipse.unittest.internal.model.UnitTestModel#removeTestRunSession(TestRunSession)
+ */
+ void addTestRunSession(TestRunSession testRunSession) {
+ Assert.isNotNull(testRunSession);
+ ArrayList<TestRunSession> toRemove = new ArrayList<>();
+
+ synchronized (this) {
+ Assert.isLegal(!fTestRunSessions.contains(testRunSession));
+ fTestRunSessions.addFirst(testRunSession);
+
+ int maxCount = Platform.getPreferencesService().getInt(UnitTestPlugin.PLUGIN_ID,
+ UnitTestPreferencesConstants.MAX_TEST_RUNS, 10, null);
+ int size = fTestRunSessions.size();
+ if (size > maxCount) {
+ List<TestRunSession> excess = fTestRunSessions.subList(maxCount, size);
+ for (Iterator<TestRunSession> iter = excess.iterator(); iter.hasNext();) {
+ TestRunSession oldSession = iter.next();
+ if (oldSession.isStopped()) {
+ toRemove.add(oldSession);
+ iter.remove();
+ }
+ }
+ }
+ }
+
+ toRemove.forEach(this::notifyTestRunSessionRemoved);
+ notifyTestRunSessionAdded(testRunSession);
+ }
+
+ /**
+ * Imports a test run session from an URL
+ *
+ * @param url an URL to source file
+ * @param monitor a progress monitor object
+ * @return an {@link ITestRunSession} object instance
+ *
+ * @throws InvocationTargetException in case of problems during import operation
+ * @throws InterruptedException in case of import operation is interrupted
+ */
+ public ITestRunSession importTestRunSession(String url, IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException {
+ monitor.beginTask(ModelMessages.UnitTestModel_importing_from_url, IProgressMonitor.UNKNOWN);
+ final String trimmedUrl = url.trim().replaceAll("\r\n?|\n", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ final TestRunHandler handler = new TestRunHandler(monitor);
+
+ final CoreException[] exception = { null };
+ final TestRunSession[] session = { null };
+
+ Thread importThread = new Thread("UnitTest URL importer") { //$NON-NLS-1$
+ @Override
+ public void run() {
+ try {
+ SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+// parserFactory.setValidating(true); // TODO: add DTD and debug flag
+ SAXParser parser = parserFactory.newSAXParser();
+ parser.parse(trimmedUrl, handler);
+ session[0] = handler.getTestRunSession();
+ } catch (OperationCanceledException e) {
+ // canceled
+ } catch (ParserConfigurationException e) {
+ storeImportError(e);
+ } catch (SAXException e) {
+ storeImportError(e);
+ } catch (IOException e) {
+ storeImportError(e);
+ } catch (IllegalArgumentException e) {
+ // Bug in parser: can throw IAE even if URL is not null
+ storeImportError(e);
+ }
+ }
+
+ private void storeImportError(Exception e) {
+ exception[0] = new CoreException(new org.eclipse.core.runtime.Status(IStatus.ERROR,
+ UnitTestPlugin.PLUGIN_ID, ModelMessages.UnitTestModel_could_not_import, e));
+ }
+ };
+ importThread.start();
+
+ while (session[0] == null && exception[0] == null && !monitor.isCanceled()) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // that's OK
+ }
+ }
+ if (session[0] == null) {
+ if (exception[0] != null) {
+ throw new InvocationTargetException(exception[0]);
+ } else {
+ importThread.interrupt(); // have to kill the thread since we don't control URLConnection and XML
+ // parsing
+ throw new InterruptedException();
+ }
+ }
+
+ addTestRunSession(session[0]);
+ monitor.done();
+ return session[0];
+ }
+
+ /**
+ * Removes the given {@link TestRunSession} and notifies all registered
+ * {@link ITestRunSessionListener}s.
+ *
+ * @param testRunSession the session to remove
+ * @see org.eclipse.unittest.internal.model.UnitTestModel#addTestRunSession(TestRunSession)
+ */
+ void removeTestRunSession(TestRunSession testRunSession) {
+ boolean existed;
+ synchronized (this) {
+ existed = fTestRunSessions.remove(testRunSession);
+ }
+ if (existed) {
+ notifyTestRunSessionRemoved(testRunSession);
+ }
+ }
+
+ private void notifyTestRunSessionRemoved(TestRunSession testRunSession) {
+ for (ITestRunSessionListener listener : fTestRunSessionListeners) {
+ listener.sessionRemoved(testRunSession);
+ }
+ }
+
+ private void notifyTestRunSessionAdded(ITestRunSession testRunSession) {
+ for (ITestRunSessionListener listener : fTestRunSessionListeners) {
+ listener.sessionAdded(testRunSession);
+ }
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/BasicElementLabels.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/BasicElementLabels.java
new file mode 100644
index 000000000..feab70e81
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/BasicElementLabels.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import java.io.File;
+
+import org.eclipse.osgi.util.TextProcessor;
+
+import org.eclipse.core.runtime.IPath;
+
+import org.eclipse.core.resources.IResource;
+
+/**
+ * A label provider for basic elements like paths. The label provider will make
+ * sure that the labels are correctly shown in RTL environments.
+ */
+public class BasicElementLabels {
+
+ private BasicElementLabels() {
+ }
+
+ /**
+ * Adds special marks so that that the given string is readable in a BIDI
+ * environment.
+ *
+ * @param string the string
+ * @param delimiters the additional delimiters
+ * @return the processed styled string
+ */
+ private static String markLTR(String string, String delimiters) {
+ return TextProcessor.process(string, delimiters);
+ }
+
+ /**
+ * Returns the label of a path.
+ *
+ * @param path the path
+ * @param isOSPath if <code>true</code>, the path represents an OS path, if
+ * <code>false</code> it is a workspace path.
+ * @return the label of the path to be used in the UI.
+ */
+ public static String getPathLabel(IPath path, boolean isOSPath) {
+ String label;
+ if (isOSPath) {
+ label = path.toOSString();
+ } else {
+ label = path.makeRelative().toString();
+ }
+ return markLTR(label, "/\\:."); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns the label of the path of a file.
+ *
+ * @param file the file
+ * @return the label of the file path to be used in the UI.
+ */
+ public static String getPathLabel(File file) {
+ return markLTR(file.getAbsolutePath(), "/\\:."); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns the label for a file pattern like '*.java'
+ *
+ * @param name the pattern
+ * @return the label of the pattern.
+ */
+ public static String getFilePattern(String name) {
+ return markLTR(name, "*.?/\\:."); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns the label for a URL, URI or URL part. Example is
+ * 'http://www.x.xom/s.html#1'
+ *
+ * @param name the URL string
+ * @return the label of the URL.
+ */
+ public static String getURLPart(String name) {
+ return markLTR(name, ":@?-#/\\:."); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns a label for a resource name.
+ *
+ * @param resource the resource
+ * @return the label of the resource name.
+ */
+ public static String getResourceName(IResource resource) {
+ return markLTR(resource.getName(), ":."); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns a label for a resource name.
+ *
+ * @param resourceName the resource name
+ * @return the label of the resource name.
+ */
+ public static String getResourceName(String resourceName) {
+ return markLTR(resourceName, ":."); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns a label for a version name. Example is '1.4.1'
+ *
+ * @param name the version string
+ * @return the version label
+ */
+ public static String getVersionName(String name) {
+ return markLTR(name, ":."); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns a label for Java element name. Example is 'new Test<? extends List>()
+ * { ...}'. This method should only be used for simple element names. Use
+ * JavaElementLabels to create a label from a Java element.
+ *
+ * @param name the Java element name.
+ * @return the label for the Java element
+ */
+ public static String getJavaElementName(String name) {
+ return markLTR(name, "<>()?,{}.:"); //$NON-NLS-1$
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultDialog.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultDialog.java
new file mode 100644
index 000000000..7da6c656e
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultDialog.java
@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.model.TestElement;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestElement.FailureTrace;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.TrayDialog;
+
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextAttribute;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.TextViewer;
+import org.eclipse.jface.text.presentation.IPresentationDamager;
+import org.eclipse.jface.text.presentation.IPresentationReconciler;
+import org.eclipse.jface.text.presentation.IPresentationRepairer;
+import org.eclipse.jface.text.presentation.PresentationReconciler;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.text.source.SourceViewerConfiguration;
+
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareViewerPane;
+import org.eclipse.compare.IEncodedStreamContentAccessor;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+
+/**
+ * A Compare result dialog
+ */
+public class CompareResultDialog extends TrayDialog {
+ private static final String PREFIX_SUFFIX_PROPERTY = UnitTestPlugin.PLUGIN_ID + ".CompareResultDialog.prefixSuffix"; //$NON-NLS-1$
+
+ private static class CompareResultMergeViewer extends TextMergeViewer {
+ private CompareResultMergeViewer(Composite parent, int style, CompareConfiguration configuration) {
+ super(parent, style, configuration);
+ }
+
+ @Override
+ protected void createControls(Composite composite) {
+ super.createControls(composite);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, IUnitTestHelpContextIds.RESULT_COMPARE_DIALOG);
+ }
+
+// protected void createToolItems(ToolBarManager tbm) {
+// ResourceBundle bundle= CompareUI.getResourceBundle();
+// tbm.add(new IgnoreWhiteSpaceAction(bundle, getCompareConfiguration()));
+// super.createToolItems(tbm);
+// }
+
+ @Override
+ protected void configureTextViewer(TextViewer textViewer) {
+ if (textViewer instanceof SourceViewer) {
+ int[] prefixSuffixOffsets = (int[]) getCompareConfiguration().getProperty(PREFIX_SUFFIX_PROPERTY);
+ ((SourceViewer) textViewer).configure(new CompareResultViewerConfiguration(prefixSuffixOffsets));
+ }
+ }
+ }
+
+ private static class CompareResultViewerConfiguration extends SourceViewerConfiguration {
+ private static class SimpleDamagerRepairer implements IPresentationDamager, IPresentationRepairer {
+ private IDocument fDocument;
+ private final int[] fPrefixSuffixOffsets2;
+
+ public SimpleDamagerRepairer(int[] prefixSuffixOffsets) {
+ fPrefixSuffixOffsets2 = prefixSuffixOffsets;
+ }
+
+ @Override
+ public void setDocument(IDocument document) {
+ fDocument = document;
+ }
+
+ @Override
+ public IRegion getDamageRegion(ITypedRegion partition, DocumentEvent event, boolean changed) {
+ return new Region(0, fDocument.getLength());
+ }
+
+ @Override
+ public void createPresentation(TextPresentation presentation, ITypedRegion damage) {
+ presentation.setDefaultStyleRange(new StyleRange(0, fDocument.getLength(), null, null));
+ int prefix = fPrefixSuffixOffsets2[0];
+ int suffix = fPrefixSuffixOffsets2[1];
+ TextAttribute attr = new TextAttribute(Display.getDefault().getSystemColor(SWT.COLOR_RED));
+ presentation.addStyleRange(new StyleRange(prefix, fDocument.getLength() - suffix - prefix,
+ attr.getForeground(), attr.getBackground(), attr.getStyle()));
+ }
+ }
+
+ private final int[] fPrefixSuffixOffsets;
+
+ public CompareResultViewerConfiguration(int[] prefixSuffixOffsets) {
+ fPrefixSuffixOffsets = prefixSuffixOffsets;
+ }
+
+ @Override
+ public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {
+ PresentationReconciler reconciler = new PresentationReconciler();
+ SimpleDamagerRepairer dr = new SimpleDamagerRepairer(fPrefixSuffixOffsets);
+ reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
+ reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
+ return reconciler;
+ }
+ }
+
+ private static class CompareElement implements ITypedElement, IEncodedStreamContentAccessor {
+ private String fContent;
+
+ public CompareElement(String content) {
+ fContent = content;
+ }
+
+ @Override
+ public String getName() {
+ return "<no name>"; //$NON-NLS-1$
+ }
+
+ @Override
+ public Image getImage() {
+ return null;
+ }
+
+ @Override
+ public String getType() {
+ return "txt"; //$NON-NLS-1$
+ }
+
+ @Override
+ public InputStream getContents() {
+ return new ByteArrayInputStream(fContent.getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Override
+ public String getCharset() throws CoreException {
+ return "UTF-8"; //$NON-NLS-1$
+ }
+ }
+
+ private TextMergeViewer fViewer;
+ private FailureTrace trace;
+ private String fTestName;
+
+ /**
+ * Lengths of common prefix and suffix. Note: this array is passed to the
+ * DamagerRepairer and the lengths are updated on content change.
+ */
+ private final int[] fPrefixSuffix = new int[2];
+
+ private CompareViewerPane fCompareViewerPane;
+
+ /**
+ * Constructs a compare results dialog
+ *
+ * @param parentShell a parent shell object
+ * @param element a {@link TestElement} object
+ */
+ public CompareResultDialog(Shell parentShell, TestElement element) {
+ super(parentShell);
+ setShellStyle((getShellStyle() & ~SWT.APPLICATION_MODAL) | SWT.TOOL);
+ setFailedTest(element);
+ }
+
+ @Override
+ protected boolean isResizable() {
+ return true;
+ }
+
+ private void setFailedTest(TestElement failedTest) {
+ fTestName = failedTest.getTestName();
+ trace = failedTest.getFailureTrace();
+ computePrefixSuffix();
+ }
+
+ @Override
+ protected IDialogSettings getDialogBoundsSettings() {
+ return getDialogSettingsSection(getClass().getName());
+ }
+
+ /**
+ * Returns the section with the given name in this dialog settings.
+ *
+ * @param name the key
+ * @return {@link IDialogSettings} (the section), or <code>null</code> if none
+ */
+ private IDialogSettings getDialogSettingsSection(String name) {
+ IDialogSettings dialogSettings = UnitTestPlugin.getDefault().getDialogSettings();
+ IDialogSettings section = dialogSettings.getSection(name);
+ if (section == null) {
+ section = dialogSettings.addNewSection(name);
+ }
+ return section;
+ }
+
+ private void computePrefixSuffix() {
+ String expected = trace.getExpected();
+ String actual = trace.getActual();
+ int end = Math.min(expected.length(), actual.length());
+ int i = 0;
+ for (; i < end; i++)
+ if (expected.charAt(i) != actual.charAt(i))
+ break;
+ fPrefixSuffix[0] = i;
+
+ int j = expected.length() - 1;
+ int k = actual.length() - 1;
+ int l = 0;
+ for (; k >= i && j >= i; k--, j--) {
+ if (expected.charAt(j) != actual.charAt(k))
+ break;
+ l++;
+ }
+ fPrefixSuffix[1] = l;
+ }
+
+ @Override
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ newShell.setText(Messages.CompareResultDialog_title);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(newShell, IUnitTestHelpContextIds.RESULT_COMPARE_DIALOG);
+ }
+
+ @Override
+ protected void createButtonsForButtonBar(Composite parent) {
+ createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite composite = (Composite) super.createDialogArea(parent);
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 1;
+ composite.setLayout(layout);
+
+ fCompareViewerPane = new CompareViewerPane(composite, SWT.BORDER | SWT.FLAT);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL);
+ data.widthHint = convertWidthInCharsToPixels(120);
+ data.heightHint = convertHeightInCharsToPixels(13);
+ fCompareViewerPane.setLayoutData(data);
+
+ Control previewer = createPreviewer(fCompareViewerPane);
+ fCompareViewerPane.setContent(previewer);
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ previewer.setLayoutData(gd);
+ applyDialogFont(parent);
+ return composite;
+ }
+
+ private Control createPreviewer(Composite parent) {
+ final CompareConfiguration compareConfiguration = new CompareConfiguration();
+ compareConfiguration.setLeftLabel(Messages.CompareResultDialog_expectedLabel);
+ compareConfiguration.setLeftEditable(false);
+ compareConfiguration.setRightLabel(Messages.CompareResultDialog_actualLabel);
+ compareConfiguration.setRightEditable(false);
+ compareConfiguration.setProperty(CompareConfiguration.IGNORE_WHITESPACE, Boolean.FALSE);
+ compareConfiguration.setProperty(PREFIX_SUFFIX_PROPERTY, fPrefixSuffix);
+
+ fViewer = new CompareResultMergeViewer(parent, SWT.NONE, compareConfiguration);
+ setCompareViewerInput();
+
+ Control control = fViewer.getControl();
+ control.addDisposeListener(e -> compareConfiguration.dispose());
+ return control;
+ }
+
+ private void setCompareViewerInput() {
+ if (!fViewer.getControl().isDisposed()) {
+ fViewer.setInput(
+ new DiffNode(new CompareElement(trace.getExpected()), new CompareElement(trace.getActual())));
+ fCompareViewerPane.setText(fTestName);
+ }
+ }
+
+ /**
+ * Sets a failed {@link ITestElement} as input for the CompareResultDialog
+ *
+ * @param failedTest a failed test element
+ */
+ public void setInput(TestElement failedTest) {
+ setFailedTest(failedTest);
+ setCompareViewerInput();
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultsAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultsAction.java
new file mode 100644
index 000000000..40082dc84
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultsAction.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.unittest.internal.model.TestElement;
+import org.eclipse.unittest.model.ITestElement;
+
+import org.eclipse.jface.action.Action;
+
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Action to enable/disable stack trace filtering.
+ */
+public class CompareResultsAction extends Action {
+
+ private FailureTraceUIBlock fView;
+ private CompareResultDialog fOpenDialog;
+
+ /**
+ * Constructs a compare result object
+ *
+ * @param view a {@link FailureTraceUIBlock} object
+ */
+ public CompareResultsAction(FailureTraceUIBlock view) {
+ super(Messages.CompareResultsAction_label);
+ setDescription(Messages.CompareResultsAction_description);
+ setToolTipText(Messages.CompareResultsAction_tooltip);
+
+ setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/compare.png")); //$NON-NLS-1$
+ setHoverImageDescriptor(Images.getImageDescriptor("elcl16/compare.png")); //$NON-NLS-1$
+ setImageDescriptor(Images.getImageDescriptor("elcl16/compare.png")); //$NON-NLS-1$
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.ENABLEFILTER_ACTION);
+ fView = view;
+ }
+
+ @Override
+ public void run() {
+ TestElement failedTest = fView.getFailedTest();
+ if (fOpenDialog != null) {
+ fOpenDialog.setInput(failedTest);
+ fOpenDialog.getShell().setActive();
+
+ } else {
+ fOpenDialog = new CompareResultDialog(fView.getShell(), failedTest);
+ fOpenDialog.create();
+ fOpenDialog.getShell().addDisposeListener(e -> fOpenDialog = null);
+ fOpenDialog.setBlockOnOpen(false);
+ fOpenDialog.open();
+ }
+ }
+
+ /**
+ * Updates the CompareResultDialog with a failed {@link ITestElement} as input
+ *
+ * @param failedTest a failed test element
+ */
+ public void updateOpenDialog(TestElement failedTest) {
+ if (fOpenDialog != null) {
+ fOpenDialog.setInput(failedTest);
+ }
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CopyFailureListAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CopyFailureListAction.java
new file mode 100644
index 000000000..68cf68bcc
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CopyFailureListAction.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.unittest.internal.model.TestElement;
+import org.eclipse.unittest.model.ITestElement.FailureTrace;
+
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.dialogs.MessageDialog;
+
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Copies the names of the methods that failed and their traces to the
+ * clipboard.
+ */
+public class CopyFailureListAction extends Action {
+
+ private final Clipboard fClipboard;
+ private final TestRunnerViewPart fRunner;
+
+ /**
+ * Constructs a copy failure list action object
+ *
+ * @param runner a test runner view part object
+ * @param clipboard a clipboard object
+ */
+ public CopyFailureListAction(TestRunnerViewPart runner, Clipboard clipboard) {
+ super(Messages.CopyFailureList_action_label);
+ fRunner = runner;
+ fClipboard = clipboard;
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.COPYFAILURELIST_ACTION);
+ }
+
+ @Override
+ public void run() {
+ TextTransfer plainTextTransfer = TextTransfer.getInstance();
+
+ try {
+ fClipboard.setContents(new String[] { getAllFailureTraces() }, new Transfer[] { plainTextTransfer });
+ } catch (SWTError e) {
+ if (e.code != DND.ERROR_CANNOT_SET_CLIPBOARD)
+ throw e;
+ if (MessageDialog.openQuestion(fRunner.getSite().getShell(), Messages.CopyFailureList_problem,
+ Messages.CopyFailureList_clipboard_busy))
+ run();
+ }
+ }
+
+ /**
+ * Returns the failure trace lines as a string
+ *
+ * @return a failure traces string
+ */
+ public String getAllFailureTraces() {
+ StringBuilder buf = new StringBuilder();
+ String lineDelim = System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$
+ for (TestElement failure : fRunner.getCurrentTestRunSession().getAllFailedTestElements()) {
+ buf.append(failure.getTestName()).append(lineDelim);
+ FailureTrace failureTrace = failure.getFailureTrace();
+ String trace = failureTrace != null ? failureTrace.getTrace() : null;
+ if (trace != null) {
+ int start = 0;
+ while (start < trace.length()) {
+ int idx = trace.indexOf('\n', start);
+ if (idx != -1) {
+ String line = trace.substring(start, idx);
+ buf.append(line).append(lineDelim);
+ start = idx + 1;
+ } else {
+ start = Integer.MAX_VALUE;
+ }
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CounterPanel.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CounterPanel.java
new file mode 100644
index 000000000..8e7959b99
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CounterPanel.java
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import java.text.MessageFormat;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * A panel with counters for the number of Runs, Errors and Failures.
+ */
+public class CounterPanel extends Composite {
+ protected Text fNumberOfErrors;
+ protected Text fNumberOfFailures;
+ protected Text fNumberOfRuns;
+ protected Integer fTotal;
+ protected int fIgnoredCount;
+ protected int fAssumptionFailedCount;
+
+ private final Image fErrorIcon = Images.createImage("ovr16/error_ovr.png"); //$NON-NLS-1$
+ private final Image fFailureIcon = Images.createImage("ovr16/failed_ovr.png"); //$NON-NLS-1$
+
+ /**
+ * Constructs a CounterPanel object
+ *
+ * @param parent a parent composite
+ */
+ public CounterPanel(Composite parent) {
+ super(parent, SWT.WRAP);
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.numColumns = 9;
+ gridLayout.makeColumnsEqualWidth = false;
+ gridLayout.marginWidth = 0;
+ setLayout(gridLayout);
+
+ fNumberOfRuns = createLabel(Messages.CounterPanel_label_runs, null, " 0/0 "); //$NON-NLS-1$
+ fNumberOfErrors = createLabel(Messages.CounterPanel_label_errors, fErrorIcon, " 0 "); //$NON-NLS-1$
+ fNumberOfFailures = createLabel(Messages.CounterPanel_label_failures, fFailureIcon, " 0 "); //$NON-NLS-1$
+
+ addDisposeListener(e -> disposeIcons());
+ }
+
+ private void disposeIcons() {
+ fErrorIcon.dispose();
+ fFailureIcon.dispose();
+ }
+
+ private Text createLabel(String name, Image image, String init) {
+ Label label = new Label(this, SWT.NONE);
+ if (image != null) {
+ image.setBackground(label.getBackground());
+ label.setImage(image);
+ }
+ label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING));
+
+ label = new Label(this, SWT.NONE);
+ label.setText(name);
+ label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING));
+ // label.setFont(JFaceResources.getBannerFont());
+
+ Text value = new Text(this, SWT.READ_ONLY);
+ value.setText(init);
+ fixReadonlyTextBackground(value);
+ value.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.HORIZONTAL_ALIGN_BEGINNING));
+ return value;
+ }
+
+ /**
+ * Resets the counters presented on the panel
+ */
+ public void reset() {
+ fTotal = null;
+ setErrorValue(0);
+ setFailureValue(0);
+ setRunValue(0, 0, 0);
+ }
+
+ /**
+ * Sets the total count value
+ *
+ * @param value total count value, can be <code>null</code> if unknown yet
+ */
+ public void setTotal(Integer value) {
+ fTotal = value;
+ }
+
+ /**
+ * Sets the run counter values
+ *
+ * @param value a run counter value
+ * @param ignoredCount an ignored tests counter value
+ * @param assumptionFailureCount a number of assumption failure counter value
+ */
+ public void setRunValue(int value, int ignoredCount, int assumptionFailureCount) {
+ String runString;
+ String runStringTooltip;
+ String totalString = fTotal == null ? "?" : fTotal.toString(); //$NON-NLS-1$
+ if (ignoredCount == 0 && assumptionFailureCount == 0) {
+ runString = MessageFormat.format(Messages.CounterPanel_runcount, Integer.toString(value), totalString);
+ runStringTooltip = runString;
+ } else if (ignoredCount != 0 && assumptionFailureCount == 0) {
+ runString = MessageFormat.format(Messages.CounterPanel_runcount_skipped, Integer.toString(value),
+ totalString, Integer.toString(ignoredCount));
+ runStringTooltip = MessageFormat.format(Messages.CounterPanel_runcount_ignored, Integer.toString(value),
+ totalString, Integer.toString(ignoredCount));
+ } else if (ignoredCount == 0 && assumptionFailureCount != 0) {
+ runString = MessageFormat.format(Messages.CounterPanel_runcount_skipped, Integer.toString(value),
+ totalString, Integer.toString(assumptionFailureCount));
+ runStringTooltip = MessageFormat.format(Messages.CounterPanel_runcount_assumptionsFailed,
+ Integer.toString(value), totalString, Integer.toString(assumptionFailureCount));
+ } else {
+ runString = MessageFormat.format(Messages.CounterPanel_runcount_skipped, Integer.toString(value),
+ totalString, Integer.toString(ignoredCount + assumptionFailureCount));
+ runStringTooltip = MessageFormat.format(Messages.CounterPanel_runcount_ignored_assumptionsFailed,
+ Integer.toString(value), totalString, Integer.toString(ignoredCount),
+ Integer.toString(assumptionFailureCount));
+ }
+ fNumberOfRuns.setText(runString);
+ fNumberOfRuns.setToolTipText(runStringTooltip);
+
+ if (fIgnoredCount == 0 && ignoredCount > 0 || fIgnoredCount != 0 && ignoredCount == 0) {
+ layout();
+ } else if (fAssumptionFailedCount == 0 && assumptionFailureCount > 0
+ || fAssumptionFailedCount != 0 && assumptionFailureCount == 0) {
+ layout();
+ } else {
+ fNumberOfRuns.redraw();
+ redraw();
+ }
+ fIgnoredCount = ignoredCount;
+ fAssumptionFailedCount = assumptionFailureCount;
+ }
+
+ /**
+ * Sets an error counter value
+ *
+ * @param value am error counter value
+ */
+ public void setErrorValue(int value) {
+ fNumberOfErrors.setText(Integer.toString(value));
+ redraw();
+ }
+
+ /**
+ * Sets a failure counter value
+ *
+ * @param value a failure counter value
+ */
+ public void setFailureValue(int value) {
+ fNumberOfFailures.setText(Integer.toString(value));
+ redraw();
+ }
+
+ /**
+ * Fixes https://bugs.eclipse.org/71765 by setting the background color to
+ * {@code SWT.COLOR_WIDGET_BACKGROUND}.
+ * <p>
+ * Should be applied to all SWT.READ_ONLY Texts in dialogs (or at least those
+ * which don't have an SWT.BORDER). Search regex:
+ * {@code new Text\([^,]+,[^\)]+SWT\.READ_ONLY}
+ *
+ * @param textField the text field
+ */
+ public static void fixReadonlyTextBackground(Text textField) {
+ textField.setBackground(textField.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/EnableStackFilterAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/EnableStackFilterAction.java
new file mode 100644
index 000000000..df2ecc85e
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/EnableStackFilterAction.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.unittest.internal.UnitTestPreferencesConstants;
+
+import org.eclipse.jface.action.Action;
+
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Action to enable/disable stack trace filtering.
+ */
+public class EnableStackFilterAction extends Action {
+
+ private FailureTraceUIBlock fView;
+
+ /**
+ * Constructs an enable stack filter action object
+ *
+ * @param view a {@link FailureTraceUIBlock} object
+ */
+ public EnableStackFilterAction(FailureTraceUIBlock view) {
+ super(Messages.EnableStackFilterAction_action_label);
+ setDescription(Messages.EnableStackFilterAction_action_description);
+ setToolTipText(Messages.EnableStackFilterAction_action_tooltip);
+
+ setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/cfilter.png")); //$NON-NLS-1$
+ setHoverImageDescriptor(Images.getImageDescriptor("elcl16/cfilter.png")); //$NON-NLS-1$
+ setImageDescriptor(Images.getImageDescriptor("elcl16/cfilter.png")); //$NON-NLS-1$
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.ENABLEFILTER_ACTION);
+
+ fView = view;
+ setChecked(UnitTestPreferencesConstants.getFilterStack());
+ }
+
+ @Override
+ public void run() {
+ UnitTestPreferencesConstants.setFilterStack(isChecked());
+ fView.refresh();
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTableDisplay.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTableDisplay.java
new file mode 100644
index 000000000..9d0a27adc
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTableDisplay.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/**
+ *
+ */
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+/**
+ * A failure table display
+ */
+public class FailureTableDisplay implements ITraceDisplay {
+ private final Table fTable;
+
+ private final Image fExceptionIcon = Images.createImage("obj16/exc_catch.png"); //$NON-NLS-1$
+
+ private final Image fStackIcon = Images.createImage("obj16/stkfrm_obj.png"); //$NON-NLS-1$
+
+ /**
+ * Constructs a failure table display
+ *
+ * @param table a table object
+ */
+ public FailureTableDisplay(Table table) {
+ fTable = table;
+ fTable.getParent().addDisposeListener(e -> disposeIcons());
+ }
+
+ @Override
+ public void addTraceLine(int lineType, String label) {
+ TableItem tableItem = newTableItem();
+ switch (lineType) {
+ case TextualTrace.LINE_TYPE_EXCEPTION:
+ tableItem.setImage(fExceptionIcon);
+ break;
+ case TextualTrace.LINE_TYPE_STACKFRAME:
+ tableItem.setImage(fStackIcon);
+ break;
+ case TextualTrace.LINE_TYPE_NORMAL:
+ default:
+ break;
+ }
+ tableItem.setText(label);
+ }
+
+ /**
+ * Returns an exception icon image
+ *
+ * @return an exception icon image
+ */
+ public Image getExceptionIcon() {
+ return fExceptionIcon;
+ }
+
+ /**
+ * Returns a stack icon image
+ *
+ * @return a stack icon image
+ */
+ public Image getStackIcon() {
+ return fStackIcon;
+ }
+
+ /**
+ * Returns a table object
+ *
+ * @return a table object
+ */
+ public Table getTable() {
+ return fTable;
+ }
+
+ private void disposeIcons() {
+ if (fExceptionIcon != null && !fExceptionIcon.isDisposed())
+ fExceptionIcon.dispose();
+ if (fStackIcon != null && !fStackIcon.isDisposed())
+ fStackIcon.dispose();
+ }
+
+ /**
+ * Returns a new table item
+ *
+ * @return a table item object instance
+ */
+ public TableItem newTableItem() {
+ return new TableItem(fTable, SWT.NONE);
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTraceUIBlock.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTraceUIBlock.java
new file mode 100644
index 000000000..9800f4c60
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTraceUIBlock.java
@@ -0,0 +1,285 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.eclipse.unittest.internal.UnitTestPreferencesConstants;
+import org.eclipse.unittest.internal.model.TestElement;
+import org.eclipse.unittest.internal.model.TestRunSession;
+import org.eclipse.unittest.model.ITestRunSession;
+import org.eclipse.unittest.ui.ITestViewSupport;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.ToolBar;
+
+import org.eclipse.core.text.StringMatcher;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.util.OpenStrategy;
+
+/**
+ * A pane that shows a stack trace of a failed test.
+ *
+ * @noextend This class is not intended to be subclassed by clients.
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public class FailureTraceUIBlock implements IMenuListener {
+
+ private static final int MAX_LABEL_LENGTH = 256;
+ private Table fTable;
+ private TestRunnerViewPart fTestRunner;
+ private String fInputTrace;
+ private final Clipboard fClipboard;
+ private TestElement fFailure;
+ private CompareResultsAction fCompareAction;
+ private final FailureTableDisplay fFailureTableDisplay;
+ private ShowStackTraceInConsoleViewAction fShowTraceInConsoleAction;
+
+ /**
+ * Constructs a {@link FailureTraceUIBlock} object
+ *
+ * @param parent a parent composite
+ * @param clipboard a {@link Clipboard} instance
+ * @param testRunner a Test Runner view part
+ * @param toolBar a {@link ToolBar} instance
+ */
+ public FailureTraceUIBlock(Composite parent, Clipboard clipboard, TestRunnerViewPart testRunner, ToolBar toolBar) {
+ Assert.isNotNull(clipboard);
+
+ // fill the failure trace viewer toolbar
+ ToolBarManager failureToolBarmanager = new ToolBarManager(toolBar);
+ fShowTraceInConsoleAction = new ShowStackTraceInConsoleViewAction();
+ fShowTraceInConsoleAction.setDelegate(null);
+ fShowTraceInConsoleAction.setEnabled(false);
+ failureToolBarmanager.add(fShowTraceInConsoleAction);
+ failureToolBarmanager.add(new EnableStackFilterAction(this));
+ fCompareAction = new CompareResultsAction(this);
+ fCompareAction.setEnabled(false);
+ failureToolBarmanager.add(fCompareAction);
+ failureToolBarmanager.update(true);
+ fTable = new Table(parent, SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL);
+ fTestRunner = testRunner;
+ fClipboard = clipboard;
+
+ OpenStrategy handler = new OpenStrategy(fTable);
+ handler.addOpenListener(e -> {
+ if (fTable.getSelectionIndex() == 0 && fFailure.getFailureTrace() != null
+ && fFailure.getFailureTrace().isComparisonFailure()) {
+ fCompareAction.run();
+ }
+ if (fTable.getSelection().length != 0) {
+ IAction a = createOpenEditorAction(getSelectedText());
+ if (a != null)
+ a.run();
+ }
+ });
+
+ initMenu();
+
+ fFailureTableDisplay = new FailureTableDisplay(fTable);
+ }
+
+ private void initMenu() {
+ MenuManager menuMgr = new MenuManager();
+ menuMgr.setRemoveAllWhenShown(true);
+ menuMgr.addMenuListener(this);
+ Menu menu = menuMgr.createContextMenu(fTable);
+ fTable.setMenu(menu);
+ }
+
+ @Override
+ public void menuAboutToShow(IMenuManager manager) {
+ if (fTable.getSelectionCount() > 0) {
+ IAction a = createOpenEditorAction(getSelectedText());
+ if (a != null)
+ manager.add(a);
+ manager.add(new UnitTestCopyAction(FailureTraceUIBlock.this, fClipboard));
+ }
+ // fix for bug 68058
+ if (fFailure != null && fFailure.getFailureTrace() != null
+ && fFailure.getFailureTrace().isComparisonFailure()) {
+ manager.add(fCompareAction);
+ }
+ }
+
+ /**
+ * Returns the current trace
+ *
+ * @return a current trace or <code>null</code>
+ */
+ public String getTrace() {
+ return fInputTrace;
+ }
+
+ private String getSelectedText() {
+ return fTable.getSelection()[0].getText();
+ }
+
+ private IAction createOpenEditorAction(String traceLine) {
+ return fFailure.getTestRunSession().getTestViewSupport()
+ .createOpenEditorAction(fTestRunner.getSite().getShell(), fFailure, traceLine);
+ }
+
+ /**
+ * Returns the composite used to present the trace
+ *
+ * @return The composite
+ */
+ public Composite getComposite() {
+ return fTable;
+ }
+
+ /**
+ * Refresh the table from the trace.
+ */
+ public void refresh() {
+ updateTable(fInputTrace);
+ }
+
+ /**
+ * Shows a TestFailure
+ *
+ * @param test the failed test
+ */
+ public void showFailure(TestElement test) {
+ fFailure = test;
+ String trace = ""; //$NON-NLS-1$
+ updateActions(test);
+ updateEnablement(test);
+ if (test != null && test.getFailureTrace() != null) {
+ trace = test.getFailureTrace().getTrace();
+ }
+ if (Objects.equals(fInputTrace, trace)) {
+ return;
+ }
+ fInputTrace = trace;
+ updateTable(trace);
+ }
+
+ private void updateActions(TestElement test) {
+ ITestViewSupport testViewSupport = test != null ? test.getTestRunSession().getTestViewSupport() : null;
+ fShowTraceInConsoleAction.setDelegate(testViewSupport != null && test.getFailureTrace() != null
+ ? testViewSupport.createShowStackTraceInConsoleViewActionDelegate(test)
+ : null);
+ }
+
+ private void updateEnablement(TestElement test) {
+ boolean enableCompare = test != null && test.getFailureTrace() != null
+ && test.getFailureTrace().isComparisonFailure();
+ fCompareAction.setEnabled(enableCompare);
+ if (enableCompare) {
+ fCompareAction.updateOpenDialog(test);
+ }
+
+ boolean enableShowTraceInConsole = test != null && test.getFailureTrace() != null;
+ fShowTraceInConsoleAction.setEnabled(enableShowTraceInConsole);
+ }
+
+ private void updateTable(String trace) {
+ if (trace == null || trace.trim().isEmpty()) {
+ clear();
+ return;
+ }
+ trace = trace.trim();
+ fTable.setRedraw(false);
+ fTable.removeAll();
+ new TextualTrace(trace, getFilterPatterns()).display(fFailureTableDisplay, MAX_LABEL_LENGTH);
+ fTable.setRedraw(true);
+ }
+
+ private Collection<StringMatcher> getFilterPatterns() {
+ if (UnitTestPreferencesConstants.getFilterStack())
+ return getFilterPatterns(fFailure.getTestRunSession());
+ return Collections.emptySet();
+ }
+
+ /**
+ * Returns an array of Filter patterns for Stacktraces/Error messages
+ *
+ * @param session a {@link ITestRunSession} to ask the filter pattern for
+ * @return an array of filter patterns
+ */
+ public Collection<StringMatcher> getFilterPatterns(ITestRunSession session) {
+ if (session == null) {
+ return Collections.emptySet();
+ }
+ ITestViewSupport viewSupport = ((TestRunSession) session).getTestViewSupport();
+ if (viewSupport != null) {
+ Collection<StringMatcher> res = viewSupport.getTraceExclusionFilterPatterns();
+ if (res != null) {
+ return res;
+ }
+ }
+ return Collections.emptySet();
+ }
+
+ /**
+ * Shows other information than a stack trace.
+ *
+ * @param text the informational message to be shown
+ */
+ public void setInformation(String text) {
+ clear();
+ TableItem tableItem = fFailureTableDisplay.newTableItem();
+ tableItem.setText(text);
+ }
+
+ /**
+ * Clears the non-stack trace info
+ */
+ public void clear() {
+ fTable.removeAll();
+ fInputTrace = null;
+ }
+
+ /**
+ * Returns a failed test element
+ *
+ * @return a failed test element
+ */
+ public TestElement getFailedTest() {
+ return fFailure;
+ }
+
+ /**
+ * Returns a shell object
+ *
+ * @return a shell object
+ */
+ public Shell getShell() {
+ return fTable.getShell();
+ }
+
+ /**
+ * Disposes the Failure trace UI Block
+ */
+ public void dispose() {
+ // Nothing to dispose
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ITraceDisplay.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ITraceDisplay.java
new file mode 100644
index 000000000..581ac594a
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ITraceDisplay.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+/**
+ * A trace display interface
+ */
+public interface ITraceDisplay {
+
+ /**
+ * Adds a trace text line
+ *
+ * @param lineType a type of trace line
+ * @param label a trace line text
+ */
+ void addTraceLine(int lineType, String label);
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/IUnitTestHelpContextIds.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/IUnitTestHelpContextIds.java
new file mode 100644
index 000000000..b0d4e5d37
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/IUnitTestHelpContextIds.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+
+/**
+ * Help context ids for the Unit Test UI.
+ */
+public interface IUnitTestHelpContextIds {
+ String PREFIX = UnitTestPlugin.PLUGIN_ID + '.';
+
+ // Actions
+ String COPYTRACE_ACTION = PREFIX + "copy_trace_action_context"; //$NON-NLS-1$
+ String COPYFAILURELIST_ACTION = PREFIX + "copy_failure_list_action_context"; //$NON-NLS-1$
+ String ENABLEFILTER_ACTION = PREFIX + "enable_filter_action_context"; //$NON-NLS-1$
+ String OPENEDITORATLINE_ACTION = PREFIX + "open_editor_atline_action_context"; //$NON-NLS-1$
+ String OPENTEST_ACTION = PREFIX + "open_test_action_context"; //$NON-NLS-1$
+ String RERUN_ACTION = PREFIX + "rerun_test_action_context"; //$NON-NLS-1$
+ String GOTO_REFERENCED_TEST_ACTION_CONTEXT = PREFIX + "goto_referenced_test_action_context"; //$NON-NLS-1$
+ String OUTPUT_SCROLL_LOCK_ACTION = PREFIX + "scroll_lock"; //$NON-NLS-1$
+
+ // view parts
+ String RESULTS_VIEW = PREFIX + "results_view_context"; //$NON-NLS-1$
+ String RESULTS_VIEW_TOGGLE_ORIENTATION_ACTION = PREFIX + "results_view_toggle_call_mode_action_context"; //$NON-NLS-1$
+
+ // Dialogs
+ String TEST_SELECTION_DIALOG = PREFIX + "test_selection_context"; //$NON-NLS-1$
+ String RESULT_COMPARE_DIALOG = PREFIX + "result_compare_context"; //$NON-NLS-1$
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Images.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Images.java
new file mode 100644
index 000000000..6c7423b71
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Images.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import java.net.URL;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+/**
+ * Image related utilities
+ */
+public class Images {
+
+ private static final IPath ICONS_PATH = new Path("$nl$/icons/full"); //$NON-NLS-1$
+
+ /**
+ * Create an {@link ImageDescriptor} from a given path
+ *
+ * @param relativePath relative path to the image
+ * @return an {@link ImageDescriptor}, or <code>null</code> iff there's no image
+ * at the given location and <code>useMissingImageDescriptor</code> is
+ * <code>true</code>
+ */
+ public static ImageDescriptor getImageDescriptor(String relativePath) {
+ IPath path = ICONS_PATH.append(relativePath);
+ return createImageDescriptor(UnitTestPlugin.getDefault().getBundle(), path, true);
+ }
+
+ /**
+ * Creates an {@link Image} from a given path
+ *
+ * @param path path to the image
+ * @return a new image or <code>null</code> if the image could not be created
+ */
+ public static Image createImage(String path) {
+ return getImageDescriptor(path).createImage();
+ }
+
+ /**
+ * Sets the three image descriptors for enabled, disabled, and hovered to an
+ * action. The actions are retrieved from the *lcl16 folders.
+ *
+ * @param action the action
+ * @param iconName the icon name
+ */
+ public static void setLocalImageDescriptors(IAction action, String iconName) {
+ setImageDescriptors(action, "lcl16", iconName); //$NON-NLS-1$
+ }
+
+ private static void setImageDescriptors(IAction action, String type, String relPath) {
+ ImageDescriptor id = createImageDescriptor("d" + type, relPath, false); //$NON-NLS-1$
+ if (id != null)
+ action.setDisabledImageDescriptor(id);
+
+ ImageDescriptor descriptor = createImageDescriptor("e" + type, relPath, true); //$NON-NLS-1$
+ action.setHoverImageDescriptor(descriptor);
+ action.setImageDescriptor(descriptor);
+ }
+
+ /*
+ * Creates an image descriptor for the given prefix and name in the JDT UI
+ * bundle. The path can contain variables like $NL$. If no image could be found,
+ * <code>useMissingImageDescriptor</code> decides if either the 'missing image
+ * descriptor' is returned or <code>null</code>. or <code>null</code>.
+ */
+ private static ImageDescriptor createImageDescriptor(String pathPrefix, String imageName,
+ boolean useMissingImageDescriptor) {
+ IPath path = ICONS_PATH.append(pathPrefix).append(imageName);
+ return createImageDescriptor(UnitTestPlugin.getDefault().getBundle(), path, useMissingImageDescriptor);
+ }
+
+ /**
+ * Creates an image descriptor for the given path in a bundle. The path can
+ * contain variables like $NL$. If no image could be found,
+ * <code>useMissingImageDescriptor</code> decides if either the 'missing image
+ * descriptor' is returned or <code>null</code>.
+ *
+ * @param bundle a bundle
+ * @param path path in the bundle
+ * @param useMissingImageDescriptor if <code>true</code>, returns the shared
+ * image descriptor for a missing image.
+ * Otherwise, returns <code>null</code> if the
+ * image could not be found
+ * @return an {@link ImageDescriptor}, or <code>null</code> iff there's no image
+ * at the given location and <code>useMissingImageDescriptor</code> is
+ * <code>true</code>
+ */
+ private static ImageDescriptor createImageDescriptor(Bundle bundle, IPath path, boolean useMissingImageDescriptor) {
+ URL url = FileLocator.find(bundle, path, null);
+ if (url != null) {
+ return ImageDescriptor.createFromURL(url);
+ }
+ if (useMissingImageDescriptor) {
+ return ImageDescriptor.getMissingImageDescriptor();
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.java
new file mode 100644
index 000000000..3d4bcad5f
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Unit Test View UI Messages
+ */
+public final class Messages extends NLS {
+
+ static {
+ NLS.initializeMessages(Messages.class.getName(), Messages.class);
+ }
+
+ private Messages() {
+ // Do not instantiate
+ }
+
+ public static String CompareResultDialog_actualLabel;
+ public static String CompareResultDialog_expectedLabel;
+ public static String CompareResultDialog_title;
+ public static String CompareResultsAction_description;
+ public static String CompareResultsAction_label;
+ public static String CompareResultsAction_tooltip;
+
+ public static String CopyFailureList_action_label;
+ public static String CopyFailureList_clipboard_busy;
+ public static String CopyFailureList_problem;
+
+ public static String CopyTrace_action_label;
+ public static String CopyTraceAction_clipboard_busy;
+ public static String CopyTraceAction_problem;
+
+ public static String CounterPanel_label_errors;
+ public static String CounterPanel_label_failures;
+ public static String CounterPanel_label_runs;
+ public static String CounterPanel_runcount;
+ public static String CounterPanel_runcount_assumptionsFailed;
+ public static String CounterPanel_runcount_ignored;
+ public static String CounterPanel_runcount_skipped;
+ public static String CounterPanel_runcount_ignored_assumptionsFailed;
+
+ public static String EnableStackFilterAction_action_description;
+ public static String EnableStackFilterAction_action_label;
+ public static String EnableStackFilterAction_action_tooltip;
+
+ public static String ExpandAllAction_text;
+ public static String ExpandAllAction_tooltip;
+
+ public static String CollapseAllAction_text;
+ public static String CollapseAllAction_tooltip;
+
+ public static String RerunAction_label_debug;
+ public static String RerunAction_label_run;
+ public static String RerunAction_label_rerun;
+
+ public static String ScrollLockAction_action_label;
+ public static String ScrollLockAction_action_tooltip;
+
+ public static String ShowNextFailureAction_label;
+ public static String ShowNextFailureAction_tooltip;
+
+ public static String ShowPreviousFailureAction_label;
+ public static String ShowPreviousFailureAction_tooltip;
+
+ public static String ShowStackTraceInConsoleViewAction_description;
+ public static String ShowStackTraceInConsoleViewAction_label;
+ public static String ShowStackTraceInConsoleViewAction_tooltip;
+
+ public static String TestRunnerViewPart_activate_on_failure_only;
+ public static String TestRunnerViewPart_cannotrerun_title;
+ public static String TestRunnerViewPart_cannotrerurn_message;
+ public static String TestRunnerViewPart_configName;
+ public static String TestRunnerViewPart__error_cannotrun;
+ public static String TestRunnerViewPart_error_cannotrerun;
+ public static String TestRunnerViewPart_error_no_tests_found;
+
+ public static String TestRunnerViewPart_jobName;
+
+ public static String TestRunnerViewPart_label_failure;
+ public static String TestRunnerViewPart_Launching;
+ public static String TestRunnerViewPart_message_finish;
+ public static String TestRunnerViewPart_message_stopped;
+ public static String TestRunnerViewPart_message_terminated;
+ public static String TestRunnerViewPart_rerunaction_label;
+ public static String TestRunnerViewPart_rerunaction_tooltip;
+ public static String TestRunnerViewPart_rerunfailuresaction_label;
+ public static String TestRunnerViewPart_rerunfailuresaction_tooltip;
+ public static String TestRunnerViewPart_rerunFailedFirstLaunchConfigName;
+ public static String TestRunnerViewPart_stopaction_text;
+ public static String TestRunnerViewPart_stopaction_tooltip;
+ public static String TestRunnerViewPart_terminate_message;
+ public static String TestRunnerViewPart_terminate_title;
+ public static String TestRunnerViewPart_toggle_automatic_label;
+ public static String TestRunnerViewPart_toggle_horizontal_label;
+ public static String TestRunnerViewPart_toggle_vertical_label;
+ public static String TestRunnerViewPart_titleToolTip;
+ public static String TestRunnerViewPart_wrapperJobName;
+ public static String TestRunnerViewPart_show_execution_time;
+ public static String TestRunnerViewPart_show_failures_only;
+ public static String TestRunnerViewPart_show_ignored_only;
+
+ public static String TestRunnerViewPart_hierarchical_layout;
+ public static String TestSessionLabelProvider_testName_elapsedTimeInSeconds;
+ public static String TestSessionLabelProvider_testName_RunnerVersion;
+
+ public static String TestSessionLabelProvider_testMethodName_className;
+
+ public static String TestRunnerViewPart_message_stopping;
+ public static String TestRunnerViewPart_PasteAction_cannotpaste_message;
+ public static String TestRunnerViewPart_PasteAction_cannotpaste_title;
+ public static String TestRunnerViewPart_PasteAction_label;
+ public static String TestRunnerViewPart_layout_menu;
+ public static String TestRunnerViewPart_editLaunchConfiguration;
+ public static String TestRunnerViewPart_sortAlphabetical;
+ public static String TestRunnerViewPart_sortRunner;
+ public static String TestRunnerViewPart_sort;
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.properties b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.properties
new file mode 100644
index 000000000..701a41a95
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.properties
@@ -0,0 +1,109 @@
+###############################################################################
+# Copyright (c) 2000, 2017 IBM Corporation and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+CopyTrace_action_label=Copy Trace
+CopyTraceAction_problem=Problem Copying to Clipboard
+CopyTraceAction_clipboard_busy=There was a problem when accessing the system clipboard. Retry?
+
+CopyFailureList_action_label=Copy Failure List
+CopyFailureList_problem=Problem Copying Failure List to Clipboard
+CopyFailureList_clipboard_busy=There was a problem when accessing the system clipboard. Retry?
+
+CounterPanel_label_runs=Runs:
+CounterPanel_label_errors=Errors:
+CounterPanel_label_failures=Failures:
+CounterPanel_runcount= {0}/{1}
+CounterPanel_runcount_skipped= {0}/{1} ({2} skipped)
+CounterPanel_runcount_ignored= {0}/{1} ({2} disabled)
+CounterPanel_runcount_assumptionsFailed= {0}/{1} ({2} assumption failures)
+CounterPanel_runcount_ignored_assumptionsFailed= {0}/{1} ({2} disabled, {3} assumption failures)
+
+EnableStackFilterAction_action_label=Filter
+EnableStackFilterAction_action_description=Filter the stack trace
+EnableStackFilterAction_action_tooltip=Filter Stack Trace
+
+ScrollLockAction_action_label=Scroll Lock
+ScrollLockAction_action_tooltip=Scroll Lock
+
+TestRunnerViewPart_jobName=Update Unit Test
+TestRunnerViewPart_wrapperJobName=Unit Test Starter Job
+TestRunnerViewPart_stopaction_text=Stop Unit Test
+TestRunnerViewPart_stopaction_tooltip=Stop Unit Test Run
+TestRunnerViewPart_show_execution_time=Show Execution &Time
+TestRunnerViewPart_show_failures_only=Show &Failures Only
+TestRunnerViewPart_show_ignored_only=Show &Skipped Tests Only
+TestRunnerViewPart_rerunaction_label=Rerun all tests
+TestRunnerViewPart_rerunaction_tooltip=Rerun all tests
+TestRunnerViewPart_hierarchical_layout=Show Tests in &Hierarchy
+TestRunnerViewPart_rerunfailuresaction_label=Rerun Failed Tests
+TestRunnerViewPart_rerunfailuresaction_tooltip=Rerun Failed Tests
+TestRunnerViewPart_rerunFailedFirstLaunchConfigName={0} (Failed Tests)
+TestRunnerViewPart_error_cannotrerun=Could not rerun test
+TestRunnerViewPart_error_no_tests_found=No tests found with test runner ''{0}''.
+TestRunnerViewPart_message_terminated=Terminated
+TestRunnerViewPart_cannotrerun_title=Rerun Test
+TestRunnerViewPart_cannotrerurn_message=To rerun tests they must be launched under the debugger\nand \'Keep Unit Test running\' must be set in the launch configuration.
+TestRunnerViewPart_label_failure=Failure Trace
+TestRunnerViewPart_message_finish= Finished after {0}
+TestRunnerViewPart_message_stopped= Stopped
+TestRunnerViewPart_message_stopping=Stopping...
+TestRunnerViewPart_configName=Rerun {0}
+TestRunnerViewPart__error_cannotrun=Could not run test
+TestRunnerViewPart_layout_menu=&Layout
+TestRunnerViewPart_Launching=Launching {0}...
+TestRunnerViewPart_toggle_automatic_label=&Automatic
+TestRunnerViewPart_toggle_horizontal_label=&Horizontal
+TestRunnerViewPart_toggle_vertical_label=&Vertical
+TestRunnerViewPart_sortAlphabetical=&Alphabetical
+TestRunnerViewPart_sortRunner=&Runner natural order
+TestRunnerViewPart_sort=Sort
+TestRunnerViewPart_activate_on_failure_only=Activate on &Error/Failure Only
+TestRunnerViewPart_PasteAction_cannotpaste_message=Cannot import test results from the clipboard. Please copy a valid URL first.
+TestRunnerViewPart_PasteAction_cannotpaste_title=Paste
+TestRunnerViewPart_PasteAction_label=Import &URL from Clipboard
+TestRunnerViewPart_terminate_title=Rerun Test
+TestRunnerViewPart_terminate_message=Terminate currently running tests?
+TestRunnerViewPart_editLaunchConfiguration=Edit Launch Configuration
+
+# The first parameter is the test name and the second is the Test Kind name
+TestRunnerViewPart_titleToolTip={0} [Runner: {1}]
+TestSessionLabelProvider_testName_elapsedTimeInSeconds={0} \u23F1\uFE0F{1}s
+TestSessionLabelProvider_testName_RunnerVersion={0} [Runner: {1}]
+
+TestSessionLabelProvider_testMethodName_className={0} - {1}
+
+ShowNextFailureAction_label=Next Failure
+ShowNextFailureAction_tooltip=Next Failed Test
+ShowPreviousFailureAction_label=Previous Failure
+ShowPreviousFailureAction_tooltip=Previous Failed Test
+ShowStackTraceInConsoleViewAction_description=Show the failure stack trace in Console view
+ShowStackTraceInConsoleViewAction_label=Show Stack Trace in Console View
+ShowStackTraceInConsoleViewAction_tooltip=Show Stack Trace in Console View
+
+ExpandAllAction_text=Expand All
+ExpandAllAction_tooltip=Expand All Nodes
+
+CollapseAllAction_text=Collapse All
+CollapseAllAction_tooltip=Collapse All Nodes
+
+CompareResultsAction_label=Compare Result
+CompareResultsAction_description=Compare the actual and expected test result
+CompareResultsAction_tooltip=Compare Actual With Expected Test Result
+
+CompareResultDialog_title=Result Comparison
+CompareResultDialog_expectedLabel=Expected
+CompareResultDialog_actualLabel=Actual
+
+RerunAction_label_rerun=Rerun ({0})
+RerunAction_label_run=&Run
+RerunAction_label_debug=&Debug
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ProgressIcons.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ProgressIcons.java
new file mode 100644
index 000000000..4cb6c1661
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ProgressIcons.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.RGB;
+
+/**
+ * Manages a set of images that can show progress in the image itself.
+ */
+public class ProgressIcons {
+ private final ImageData initialImageData;
+
+ private static final class ProgressIconKey {
+ private final int pixels;
+ private final RGB color;
+
+ public ProgressIconKey(int pixels, RGB color) {
+ this.pixels = pixels;
+ this.color = color;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ProgressIconKey)) {
+ return false;
+ }
+ ProgressIconKey other = (ProgressIconKey) obj;
+ return this.pixels == other.pixels && Objects.equals(this.color, other.color);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(Integer.valueOf(pixels), color);
+ }
+ }
+
+ private final Map<ProgressIconKey, Image> progressIcons = new HashMap<>();
+ private Device display;
+
+ /**
+ * Constructs a progress icons object
+ *
+ * @param sourceImage a source image object
+ */
+ public ProgressIcons(Image sourceImage) {
+ this.initialImageData = sourceImage.getImageData();
+ this.display = sourceImage.getDevice();
+ }
+
+ /**
+ * Disposes a progress icons object
+ */
+ public void dispose() {
+ this.progressIcons.values().forEach(Image::dispose);
+ this.progressIcons.clear();
+ }
+
+ /**
+ * Returns an image added with counters
+ *
+ * @param current a current test run number
+ * @param total a total test count
+ * @param hasFailures a flag indicating if a test has failures
+ * @return an image object instance
+ */
+ public Image getImage(int current, Integer total, boolean hasFailures) {
+ int totalAsInt = total != null ? total.intValue() : current + 1;
+ int pixelsToDraw = initialImageData.width * current / totalAsInt;
+ RGB color = display.getSystemColor(hasFailures ? SWT.COLOR_RED : SWT.COLOR_GREEN).getRGB();
+ ProgressIconKey key = new ProgressIconKey(pixelsToDraw, color);
+ return progressIcons.computeIfAbsent(key, progressKey -> {
+ ImageData imageData = (ImageData) initialImageData.clone();
+ int pixelColorCode = imageData.palette.getPixel(color);
+ for (int line = 4 * imageData.height / 5; line < imageData.height; line++) {
+ for (int column = 0; column < pixelsToDraw && column < imageData.width; column++) {
+ imageData.setAlpha(column, line, 255);
+ imageData.setPixel(column, line, pixelColorCode);
+ }
+ }
+ return new Image(display, imageData);
+ });
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/RerunAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/RerunAction.java
new file mode 100644
index 000000000..64c8ce566
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/RerunAction.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.unittest.internal.UnitTestPlugin;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jface.action.Action;
+
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+
+/**
+ * Requests to rerun a test.
+ */
+public class RerunAction extends Action {
+
+ private ILaunchConfiguration fLaunchConfiguration;
+ private String fLaunchMode;
+
+ /**
+ * Constructs a rerun action
+ *
+ * @param launchConfiguration a launch configuration object
+ * @param launchMode a launch mode
+ */
+ public RerunAction(ILaunchConfiguration launchConfiguration, String launchMode) {
+ super(NLS.bind(Messages.RerunAction_label_rerun,
+ DebugPlugin.getDefault().getLaunchManager().getLaunchMode(launchMode).getLabel()));
+ fLaunchConfiguration = launchConfiguration;
+ fLaunchMode = launchMode;
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.RERUN_ACTION);
+ }
+
+ @Override
+ public void run() {
+ try {
+ DebugPlugin.getDefault().getLaunchManager().addLaunch(fLaunchConfiguration.launch(fLaunchMode, null));
+ } catch (CoreException e) {
+ UnitTestPlugin.log(e);
+ }
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ScrollLockAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ScrollLockAction.java
new file mode 100644
index 000000000..1f0eeca17
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ScrollLockAction.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.jface.action.Action;
+
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Toggles console auto-scroll
+ */
+public class ScrollLockAction extends Action {
+
+ private TestRunnerViewPart fRunnerViewPart;
+
+ /**
+ * Constructs a scroll lock toggle action
+ *
+ * @param viewer a test runner viewer part object
+ */
+ public ScrollLockAction(TestRunnerViewPart viewer) {
+ super(Messages.ScrollLockAction_action_label);
+ fRunnerViewPart = viewer;
+ setToolTipText(Messages.ScrollLockAction_action_tooltip);
+ setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/lock.png")); //$NON-NLS-1$
+ setHoverImageDescriptor(Images.getImageDescriptor("elcl16/lock.png")); //$NON-NLS-1$
+ setImageDescriptor(Images.getImageDescriptor("elcl16/lock.png")); //$NON-NLS-1$
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.OUTPUT_SCROLL_LOCK_ACTION);
+ setChecked(false);
+ }
+
+ @Override
+ public void run() {
+ fRunnerViewPart.setAutoScroll(!isChecked());
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/SelectionProviderMediator.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/SelectionProviderMediator.java
new file mode 100644
index 000000000..911df8096
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/SelectionProviderMediator.java
@@ -0,0 +1,198 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Widget;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.StructuredViewer;
+
+/**
+ * A selection provider for view parts with more that one viewer. Tracks the
+ * focus of the viewers to provide the correct selection.
+ */
+public class SelectionProviderMediator implements IPostSelectionProvider {
+
+ private class InternalListener implements ISelectionChangedListener, FocusListener {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ doSelectionChanged(event);
+ }
+
+ @Override
+ public void focusGained(FocusEvent e) {
+ doFocusChanged(e.widget);
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ // do not reset due to focus behavior on GTK
+ // fViewerInFocus= null;
+ }
+ }
+
+ private class InternalPostSelectionListener implements ISelectionChangedListener {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ doPostSelectionChanged(event);
+ }
+
+ }
+
+ private StructuredViewer[] fViewers;
+
+ private StructuredViewer fViewerInFocus;
+ private ListenerList<ISelectionChangedListener> fSelectionChangedListeners;
+ private ListenerList<ISelectionChangedListener> fPostSelectionChangedListeners;
+
+ /**
+ * Constructs a selection provider mediator object
+ *
+ * @param viewers All viewers that can provide a selection
+ * @param viewerInFocus the viewer currently in focus or <code>null</code>
+ */
+ public SelectionProviderMediator(StructuredViewer[] viewers, StructuredViewer viewerInFocus) {
+ Assert.isNotNull(viewers);
+ fViewers = viewers;
+ InternalListener listener = new InternalListener();
+ fSelectionChangedListeners = new ListenerList<>();
+ fPostSelectionChangedListeners = new ListenerList<>();
+ fViewerInFocus = viewerInFocus;
+
+ for (StructuredViewer viewer : fViewers) {
+ viewer.addSelectionChangedListener(listener);
+ viewer.addPostSelectionChangedListener(new InternalPostSelectionListener());
+ Control control = viewer.getControl();
+ control.addFocusListener(listener);
+ }
+ }
+
+ private void doFocusChanged(Widget control) {
+ for (StructuredViewer viewer : fViewers) {
+ if (viewer.getControl() == control) {
+ propagateFocusChanged(viewer);
+ return;
+ }
+ }
+ }
+
+ final void doPostSelectionChanged(SelectionChangedEvent event) {
+ ISelectionProvider provider = event.getSelectionProvider();
+ if (provider == fViewerInFocus) {
+ firePostSelectionChanged();
+ }
+ }
+
+ final void doSelectionChanged(SelectionChangedEvent event) {
+ ISelectionProvider provider = event.getSelectionProvider();
+ if (provider == fViewerInFocus) {
+ fireSelectionChanged();
+ }
+ }
+
+ final void propagateFocusChanged(StructuredViewer viewer) {
+ if (viewer != fViewerInFocus) { // OK to compare by identity
+ fViewerInFocus = viewer;
+ fireSelectionChanged();
+ firePostSelectionChanged();
+ }
+ }
+
+ private void fireSelectionChanged() {
+ if (fSelectionChangedListeners != null) {
+ SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection());
+
+ for (ISelectionChangedListener listener : fSelectionChangedListeners) {
+ listener.selectionChanged(event);
+ }
+ }
+ }
+
+ private void firePostSelectionChanged() {
+ if (fPostSelectionChangedListeners != null) {
+ SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection());
+
+ for (ISelectionChangedListener listener : fPostSelectionChangedListeners) {
+ listener.selectionChanged(event);
+ }
+ }
+ }
+
+ @Override
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ fSelectionChangedListeners.add(listener);
+ }
+
+ @Override
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ fSelectionChangedListeners.remove(listener);
+ }
+
+ @Override
+ public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
+ fPostSelectionChangedListeners.add(listener);
+ }
+
+ @Override
+ public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
+ fPostSelectionChangedListeners.remove(listener);
+ }
+
+ @Override
+ public ISelection getSelection() {
+ if (fViewerInFocus != null) {
+ return fViewerInFocus.getSelection();
+ }
+ return StructuredSelection.EMPTY;
+ }
+
+ @Override
+ public void setSelection(ISelection selection) {
+ if (fViewerInFocus != null) {
+ fViewerInFocus.setSelection(selection);
+ }
+ }
+
+ /**
+ * Sets current selection
+ *
+ * @param selection a selection object
+ * @param reveal a flag indicating if a reveal is needed
+ */
+ public void setSelection(ISelection selection, boolean reveal) {
+ if (fViewerInFocus != null) {
+ fViewerInFocus.setSelection(selection, reveal);
+ }
+ }
+
+ /**
+ * Returns the viewer in focus or null if no viewer has the focus
+ *
+ * @return returns the current viewer in focus
+ */
+ public StructuredViewer getViewerInFocus() {
+ return fViewerInFocus;
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowNextFailureAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowNextFailureAction.java
new file mode 100644
index 000000000..5e2809ec5
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowNextFailureAction.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.jface.action.Action;
+
+/**
+ * Show next failure action
+ */
+public class ShowNextFailureAction extends Action {
+
+ private TestRunnerViewPart fPart;
+
+ /**
+ * Constructs a show next failure action object
+ *
+ * @param part a test runner view part object
+ */
+ public ShowNextFailureAction(TestRunnerViewPart part) {
+ super(Messages.ShowNextFailureAction_label);
+ setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/select_next.png")); //$NON-NLS-1$
+ setHoverImageDescriptor(Images.getImageDescriptor("elcl16/select_next.png")); //$NON-NLS-1$
+ setImageDescriptor(Images.getImageDescriptor("elcl16/select_next.png")); //$NON-NLS-1$
+ setToolTipText(Messages.ShowNextFailureAction_tooltip);
+ fPart = part;
+ }
+
+ @Override
+ public void run() {
+ fPart.selectNextFailure();
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowPreviousFailureAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowPreviousFailureAction.java
new file mode 100644
index 000000000..f5919f564
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowPreviousFailureAction.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.jface.action.Action;
+
+/**
+ * Show previous failure action
+ */
+public class ShowPreviousFailureAction extends Action {
+
+ private TestRunnerViewPart fPart;
+
+ /**
+ * Constructs a show previous failure action object
+ *
+ * @param part a test runner view part object
+ */
+ public ShowPreviousFailureAction(TestRunnerViewPart part) {
+ super(Messages.ShowPreviousFailureAction_label);
+ setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/select_prev.png")); //$NON-NLS-1$
+ setHoverImageDescriptor(Images.getImageDescriptor("elcl16/select_prev.png")); //$NON-NLS-1$
+ setImageDescriptor(Images.getImageDescriptor("elcl16/select_prev.png")); //$NON-NLS-1$
+ setToolTipText(Messages.ShowPreviousFailureAction_tooltip);
+ fPart = part;
+ }
+
+ @Override
+ public void run() {
+ fPart.selectPreviousFailure();
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowStackTraceInConsoleViewAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowStackTraceInConsoleViewAction.java
new file mode 100644
index 000000000..5270eed0b
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowStackTraceInConsoleViewAction.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2016 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+
+/**
+ * Action to show the stack trace of a failed test from Unit Test view's failure
+ * trace in debug's Java stack trace console.
+ */
+public class ShowStackTraceInConsoleViewAction extends Action {
+
+ private Runnable fDelegate;
+
+ /**
+ * Constructs a show stacktrace in console view action object
+ */
+ public ShowStackTraceInConsoleViewAction() {
+ super(Messages.ShowStackTraceInConsoleViewAction_label, IAction.AS_PUSH_BUTTON);
+ setDescription(Messages.ShowStackTraceInConsoleViewAction_description);
+ setToolTipText(Messages.ShowStackTraceInConsoleViewAction_tooltip);
+
+ setHoverImageDescriptor(Images.getImageDescriptor("elcl16/open_console.png")); //$NON-NLS-1$
+ setImageDescriptor(Images.getImageDescriptor("elcl16/open_console.png")); //$NON-NLS-1$
+ setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/open_console.png")); //$NON-NLS-1$
+
+ fDelegate = null;
+ }
+
+ @Override
+ public void run() {
+ if (fDelegate != null) {
+ fDelegate.run();
+ }
+ }
+
+ /**
+ * Sets an action delegate
+ *
+ * @param delegate an action delegate
+ */
+ public void setDelegate(Runnable delegate) {
+ fDelegate = delegate;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return super.isEnabled() && fDelegate != null;
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestRunnerViewPart.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestRunnerViewPart.java
new file mode 100644
index 000000000..b08266cc0
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestRunnerViewPart.java
@@ -0,0 +1,1823 @@
+/*******************************************************************************
+L * Copyright (c) 2000, 2018 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.UnitTestPreferencesConstants;
+import org.eclipse.unittest.internal.model.ITestRunSessionListener;
+import org.eclipse.unittest.internal.model.ITestSessionListener;
+import org.eclipse.unittest.internal.model.ProgressState;
+import org.eclipse.unittest.internal.model.TestCaseElement;
+import org.eclipse.unittest.internal.model.TestElement;
+import org.eclipse.unittest.internal.model.TestRunSession;
+import org.eclipse.unittest.internal.model.UnitTestModel;
+import org.eclipse.unittest.internal.ui.history.History;
+import org.eclipse.unittest.internal.ui.history.HistoryHandler;
+import org.eclipse.unittest.model.ITestCaseElement;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestElement.FailureTrace;
+import org.eclipse.unittest.model.ITestElement.Result;
+import org.eclipse.unittest.model.ITestRunSession;
+import org.eclipse.unittest.model.ITestSuiteElement;
+import org.eclipse.unittest.ui.ITestViewSupport;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.custom.ViewForm;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.DropTargetAdapter;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.URLTransfer;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ILock;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IEditorActionBarContributor;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.handlers.IHandlerActivation;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.menus.CommandContributionItem;
+import org.eclipse.ui.menus.CommandContributionItemParameter;
+import org.eclipse.ui.part.EditorActionBarContributor;
+import org.eclipse.ui.part.ViewPart;
+import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
+import org.eclipse.ui.progress.UIJob;
+import org.eclipse.ui.statushandlers.StatusManager;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.internal.ui.DebugPluginImages;
+import org.eclipse.debug.internal.ui.actions.EditLaunchConfigurationAction;
+
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.IDebugUIConstants;
+
+/**
+ * A ViewPart that shows the results of a test run.
+ */
+@SuppressWarnings("restriction")
+public class TestRunnerViewPart extends ViewPart {
+
+ /**
+ * An identifier of Test Runner View Part
+ */
+ public static final String NAME = UnitTestPlugin.PLUGIN_ID + ".ResultView"; //$NON-NLS-1$
+
+ private static final String RERUN_LAST_COMMAND = UnitTestPlugin.PLUGIN_ID + ".UnitTestShortcut.rerunLast"; //$NON-NLS-1$
+ private static final String RERUN_FAILED_CASES_COMMAND = UnitTestPlugin.PLUGIN_ID
+ + ".UnitTestShortcut.rerunFailedCases"; //$NON-NLS-1$
+
+ static final int REFRESH_INTERVAL = 200;
+
+ /**
+ * A Test Result layout
+ */
+ public enum TestResultsLayout {
+ FLAT, HIERARCHICAL
+ }
+
+ /**
+ * Whether the output scrolls and reveals tests as they are executed.
+ */
+ protected boolean fAutoScroll = true;
+ /**
+ * The current orientation; either <code>VIEW_ORIENTATION_HORIZONTAL</code>
+ * <code>VIEW_ORIENTATION_VERTICAL</code>, or
+ * <code>VIEW_ORIENTATION_AUTOMATIC</code>.
+ */
+ private int fOrientation = VIEW_ORIENTATION_AUTOMATIC;
+ /**
+ * The current orientation; either <code>VIEW_ORIENTATION_HORIZONTAL</code>
+ * <code>VIEW_ORIENTATION_VERTICAL</code>.
+ */
+ private int fCurrentOrientation;
+ /**
+ * The current layout mode (LAYOUT_FLAT or LAYOUT_HIERARCHICAL).
+ */
+ private TestResultsLayout fLayout = TestResultsLayout.HIERARCHICAL;
+
+ private UnitTestProgressBar fProgressBar;
+ private ProgressIcons fProgressImages;
+ protected Image fViewImage;
+ private CounterPanel fCounterPanel;
+ protected boolean fShowOnErrorOnly = false;
+ protected Clipboard fClipboard;
+ protected volatile String fInfoMessage;
+
+ private FailureTraceUIBlock fFailureTrace;
+
+ private TestViewer fTestViewer;
+ /**
+ * Is the UI disposed?
+ */
+ private boolean fIsDisposed = false;
+
+ /**
+ * Actions
+ */
+ private Action fNextAction;
+ private Action fPreviousAction;
+
+ private StopAction fStopAction;
+ private UnitTestCopyAction fCopyAction;
+ private Action fPasteAction;
+
+ private Action fRerunLastTestAction;
+ private IHandlerActivation fRerunLastActivation;
+ private Action fRerunFailedCasesAction;
+ private IHandlerActivation fRerunFailedFirstActivation;
+ private EditLaunchConfigurationAction fEditLaunchConfigAction;
+
+ private Action fFailuresOnlyFilterAction;
+ private Action fIgnoredOnlyFilterAction;
+ private ScrollLockAction fScrollLockAction;
+ private ToggleOrientationAction[] fToggleOrientationActions;
+ private ShowTestHierarchyAction fShowTestHierarchyAction;
+ private ShowTimeAction fShowTimeAction;
+ private ActivateOnErrorAction fActivateOnErrorAction;
+ private IMenuListener fViewMenuListener;
+
+ private TestRunSession fTestRunSession;
+ private TestSessionListener fTestSessionListener;
+
+// private RunnerViewHistory fViewHistory;
+ private TestRunSessionListener fTestRunSessionListener;
+
+ final Image fStackViewIcon;
+ final Image fTestRunOKIcon;
+ final Image fTestRunFailIcon;
+ final Image fTestRunOKDirtyIcon;
+ final Image fTestRunFailDirtyIcon;
+
+ final Image fTestIcon;
+ final Image fTestOkIcon;
+ final Image fTestErrorIcon;
+ final Image fTestFailIcon;
+ final Image fTestAssumptionFailureIcon;
+ final Image fTestRunningIcon;
+ final Image fTestIgnoredIcon;
+
+ final ImageDescriptor fSuiteIconDescriptor = Images.getImageDescriptor("obj16/tsuite.png"); //$NON-NLS-1$
+ final ImageDescriptor fSuiteOkIconDescriptor = Images.getImageDescriptor("obj16/tsuiteok.png"); //$NON-NLS-1$
+ final ImageDescriptor fSuiteErrorIconDescriptor = Images.getImageDescriptor("obj16/tsuiteerror.png"); //$NON-NLS-1$
+ final ImageDescriptor fSuiteFailIconDescriptor = Images.getImageDescriptor("obj16/tsuitefail.png"); //$NON-NLS-1$
+ final ImageDescriptor fSuiteRunningIconDescriptor = Images.getImageDescriptor("obj16/tsuiterun.png"); //$NON-NLS-1$
+
+ final Image fSuiteIcon;
+ final Image fSuiteOkIcon;
+ final Image fSuiteErrorIcon;
+ final Image fSuiteFailIcon;
+ final Image fSuiteRunningIcon;
+
+ final List<Image> fImagesToDispose;
+
+ // Persistence tags.
+ static final String TAG_PAGE = "page"; //$NON-NLS-1$
+ static final String TAG_RATIO = "ratio"; //$NON-NLS-1$
+ static final String TAG_TRACEFILTER = "tracefilter"; //$NON-NLS-1$
+ static final String TAG_ORIENTATION = "orientation"; //$NON-NLS-1$
+ static final String TAG_SCROLL = "scroll"; //$NON-NLS-1$
+ /**
+ */
+ static final String TAG_LAYOUT = "layout"; //$NON-NLS-1$
+ /**
+ */
+ static final String TAG_FAILURES_ONLY = "failuresOnly"; //$NON-NLS-1$
+
+ /**
+ */
+ static final String TAG_IGNORED_ONLY = "ignoredOnly"; //$NON-NLS-1$
+ /**
+ */
+ static final String TAG_SHOW_TIME = "time"; //$NON-NLS-1$
+
+ /**
+ */
+ static final String PREF_LAST_PATH = "lastImportExportPath"; //$NON-NLS-1$
+
+ /**
+ */
+ static final String PREF_LAST_URL = "lastImportURL"; //$NON-NLS-1$
+
+ // orientations
+ static final int VIEW_ORIENTATION_VERTICAL = 0;
+ static final int VIEW_ORIENTATION_HORIZONTAL = 1;
+ static final int VIEW_ORIENTATION_AUTOMATIC = 2;
+
+ private IMemento fMemento;
+
+ Image fOriginalViewImage;
+
+ private SashForm fSashForm;
+
+ private Composite fCounterComposite;
+ private Composite fParent;
+
+ /**
+ * A Job that periodically updates view description, counters, and progress bar.
+ */
+ private UpdateUIJob fUpdateJob;
+
+ /**
+ * A Job that runs as long as a test run is running. It is used to show busyness
+ * for running jobs in the view (title in italics).
+ */
+ private UnitTestIsRunningJob fUnitTestIsRunningJob;
+ private ILock fUnitTestIsRunningLock;
+ public static final Object FAMILY_UNITTEST_RUN = new Object();
+
+ private IPartListener2 fPartListener = new IPartListener2() {
+ @Override
+ public void partActivated(IWorkbenchPartReference ref) {
+ }
+
+ @Override
+ public void partBroughtToTop(IWorkbenchPartReference ref) {
+ }
+
+ @Override
+ public void partInputChanged(IWorkbenchPartReference ref) {
+ }
+
+ @Override
+ public void partClosed(IWorkbenchPartReference ref) {
+ }
+
+ @Override
+ public void partDeactivated(IWorkbenchPartReference ref) {
+ }
+
+ @Override
+ public void partOpened(IWorkbenchPartReference ref) {
+ }
+
+ @Override
+ public void partVisible(IWorkbenchPartReference ref) {
+ if (getSite().getId().equals(ref.getId())) {
+ fPartIsVisible = true;
+ }
+ }
+
+ @Override
+ public void partHidden(IWorkbenchPartReference ref) {
+ if (getSite().getId().equals(ref.getId())) {
+ fPartIsVisible = false;
+ }
+ }
+ };
+
+ protected boolean fPartIsVisible = false;
+
+ private static class UnitTesttPasteAction extends Action {
+ private final Shell fShell;
+ private Clipboard fClipboard;
+
+ public UnitTesttPasteAction(Shell shell, Clipboard clipboard) {
+ super(Messages.TestRunnerViewPart_PasteAction_label);
+ Assert.isNotNull(clipboard);
+ fShell = shell;
+ fClipboard = clipboard;
+ }
+
+ @Override
+ public void run() {
+ String urlData = (String) fClipboard.getContents(URLTransfer.getInstance());
+ if (urlData == null) {
+ urlData = (String) fClipboard.getContents(TextTransfer.getInstance());
+ }
+ if (urlData != null && urlData.length() > 0) {
+ if (isValidUrl(urlData)) {
+ importTestRunSession(urlData);
+ return;
+ }
+ }
+ MessageDialog.openInformation(fShell, Messages.TestRunnerViewPart_PasteAction_cannotpaste_title,
+ Messages.TestRunnerViewPart_PasteAction_cannotpaste_message);
+ }
+
+ private boolean isValidUrl(String urlData) {
+ try {
+ @SuppressWarnings("unused")
+ URL url = new URL(urlData);
+ } catch (MalformedURLException e) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ private class TestRunSessionListener implements ITestRunSessionListener {
+ @Override
+ public void sessionAdded(final ITestRunSession testRunSession) {
+ getDisplay().asyncExec(() -> {
+ if (UnitTestUIPreferencesConstants.getShowInAllViews()
+ || getSite().getWorkbenchWindow() == PlatformUI.getWorkbench().getActiveWorkbenchWindow()) {
+ if (fInfoMessage == null) {
+ String testRunLabel = BasicElementLabels
+ .getJavaElementName(((TestRunSession) testRunSession).getTestRunName());
+ String msg;
+ if (testRunSession.getLaunch() != null) {
+ msg = MessageFormat.format(Messages.TestRunnerViewPart_Launching, testRunLabel);
+ } else {
+ msg = testRunLabel;
+ }
+ registerInfoMessage(msg);
+ }
+
+ setActiveTestRunSession((TestRunSession) testRunSession);
+ }
+ });
+ }
+
+ @Override
+ public void sessionRemoved(final ITestRunSession testRunSession) {
+ getDisplay().asyncExec(() -> {
+ if (testRunSession.equals(fTestRunSession)) {
+ List<TestRunSession> testRunSessions = UnitTestModel.getInstance().getTestRunSessions();
+ if (!testRunSessions.isEmpty()) {
+ setActiveTestRunSession(testRunSessions.get(0));
+ } else {
+ setActiveTestRunSession(null);
+ }
+ }
+ });
+ }
+ }
+
+ private class TestSessionListener implements ITestSessionListener {
+ @Override
+ public void sessionStarted() {
+ fTestViewer.registerViewersRefresh();
+ fShowOnErrorOnly = getShowOnErrorOnly();
+
+ startUpdateJobs();
+
+ fStopAction.setEnabled(true);
+ fRerunLastTestAction.setEnabled(true);
+ fEditLaunchConfigAction.setEnabled(fTestRunSession.getLaunch() != null);
+ }
+
+ @Override
+ public void sessionCompleted(Duration duration) {
+ deregisterTestSessionListener();
+
+ fTestViewer.registerAutoScrollTarget(null);
+
+ final String msg = MessageFormat.format(Messages.TestRunnerViewPart_message_finish,
+ Double.valueOf(duration != null ? duration.toNanos() / 1.0e9 : 0));
+ getDisplay().asyncExec(() -> registerInfoMessage(msg));
+
+ postSyncRunnable(() -> {
+ if (isDisposed())
+ return;
+ fStopAction.setEnabled(lastLaunchStillRunning());
+ updateRerunFailedFirstAction();
+ processChangesInUI();
+ if (hasErrorsOrFailures()) {
+ selectFirstFailure();
+ }
+ /*
+ * if (fDirtyListener == null) { fDirtyListener= new DirtyListener();
+ * JavaCore.addElementChangedListener(fDirtyListener); }
+ */
+ warnOfContentChange();
+ });
+ stopUpdateJobs();
+ showMessageIfNoTests();
+ }
+
+ @Override
+ public void sessionAborted(Duration duration) {
+ deregisterTestSessionListener();
+
+ fTestViewer.registerAutoScrollTarget(null);
+
+ getDisplay().asyncExec(() -> registerInfoMessage(Messages.TestRunnerViewPart_message_stopped));
+ handleStopped();
+ }
+
+ @Override
+ public void runningBegins() {
+ if (!fShowOnErrorOnly)
+ postShowTestResultsView();
+ }
+
+ @Override
+ public void testStarted(ITestCaseElement testCaseElement) {
+ fTestViewer.registerAutoScrollTarget(testCaseElement);
+ fTestViewer.registerViewerUpdate(testCaseElement);
+ registerInfoMessage(testCaseElement.getDisplayName());
+ }
+
+ @Override
+ public void testFailed(ITestElement testElement, ITestElement.Result status, FailureTrace trace) {
+ if (isAutoScroll()) {
+ fTestViewer.registerFailedForAutoScroll(testElement);
+ }
+ fTestViewer.registerViewerUpdate(testElement);
+
+ // show the view on the first error only
+ if (fShowOnErrorOnly && (getErrorsPlusFailures() == 1))
+ postShowTestResultsView();
+ }
+
+ @Override
+ public void testEnded(ITestCaseElement testCaseElement) {
+ fTestViewer.registerViewerUpdate(testCaseElement);
+ }
+
+ @Override
+ public void testAdded(ITestElement testElement) {
+ fTestViewer.registerTestAdded(testElement);
+ }
+ }
+
+ private class UpdateUIJob extends UIJob {
+ private boolean fRunning = true;
+
+ public UpdateUIJob(String name) {
+ super(name);
+ setSystem(true);
+ }
+
+ @Override
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ if (!isDisposed()) {
+ processChangesInUI();
+ }
+ schedule(REFRESH_INTERVAL);
+ return Status.OK_STATUS;
+ }
+
+ public void stop() {
+ fRunning = false;
+ }
+
+ @Override
+ public boolean shouldSchedule() {
+ return fRunning;
+ }
+ }
+
+ private class UnitTestIsRunningJob extends Job {
+ public UnitTestIsRunningJob(String name) {
+ super(name);
+ setSystem(true);
+ }
+
+ @Override
+ public IStatus run(IProgressMonitor monitor) {
+ // wait until the test run terminates
+ fUnitTestIsRunningLock.acquire();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public boolean belongsTo(Object family) {
+ return family == TestRunnerViewPart.FAMILY_UNITTEST_RUN;
+ }
+ }
+
+ private class StopAction extends Action {
+ public StopAction() {
+ setText(Messages.TestRunnerViewPart_stopaction_text);
+ setToolTipText(Messages.TestRunnerViewPart_stopaction_tooltip);
+ Images.setLocalImageDescriptors(this, "stop.png"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void run() {
+ stopTest();
+ setEnabled(false);
+ }
+ }
+
+ private class RerunLastAction extends Action {
+ public RerunLastAction() {
+ setText(Messages.TestRunnerViewPart_rerunaction_label);
+ setToolTipText(Messages.TestRunnerViewPart_rerunaction_tooltip);
+ Images.setLocalImageDescriptors(this, "relaunch.png"); //$NON-NLS-1$
+ setEnabled(false);
+ setActionDefinitionId(RERUN_LAST_COMMAND);
+ }
+
+ @Override
+ public void run() {
+ DebugUITools.launch(fTestRunSession.getLaunch().getLaunchConfiguration(), rerunLaunchMode());
+ }
+ }
+
+ private class RerunFailedCasesAction extends Action {
+ public RerunFailedCasesAction() {
+ setText(Messages.TestRunnerViewPart_rerunfailuresaction_label);
+ setToolTipText(Messages.TestRunnerViewPart_rerunfailuresaction_tooltip);
+ Images.setLocalImageDescriptors(this, "relaunchf.png"); //$NON-NLS-1$
+ setEnabled(false);
+ setActionDefinitionId(RERUN_FAILED_CASES_COMMAND);
+ }
+
+ @Override
+ public void run() {
+ rerunFailedTestCases();
+ }
+ }
+
+ private class ToggleOrientationAction extends Action {
+ private final int fActionOrientation;
+
+ public ToggleOrientationAction(int orientation) {
+ super("", AS_RADIO_BUTTON); //$NON-NLS-1$
+ switch (orientation) {
+ case TestRunnerViewPart.VIEW_ORIENTATION_HORIZONTAL:
+ setText(Messages.TestRunnerViewPart_toggle_horizontal_label);
+ setImageDescriptor(Images.getImageDescriptor("elcl16/th_horizontal.png")); //$NON-NLS-1$
+ break;
+ case TestRunnerViewPart.VIEW_ORIENTATION_VERTICAL:
+ setText(Messages.TestRunnerViewPart_toggle_vertical_label);
+ setImageDescriptor(Images.getImageDescriptor("elcl16/th_vertical.png")); //$NON-NLS-1$
+ break;
+ case TestRunnerViewPart.VIEW_ORIENTATION_AUTOMATIC:
+ setText(Messages.TestRunnerViewPart_toggle_automatic_label);
+ setImageDescriptor(Images.getImageDescriptor("elcl16/th_automatic.png")); //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ fActionOrientation = orientation;
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
+ IUnitTestHelpContextIds.RESULTS_VIEW_TOGGLE_ORIENTATION_ACTION);
+ }
+
+ public int getOrientation() {
+ return fActionOrientation;
+ }
+
+ @Override
+ public void run() {
+ if (isChecked()) {
+ fOrientation = fActionOrientation;
+ computeOrientation();
+ }
+ }
+ }
+
+ private class SortAction extends Action {
+ private final boolean enableAlphabeticalSort;
+
+ public SortAction(boolean enableAlphabeticalSort) {
+ super(enableAlphabeticalSort ? Messages.TestRunnerViewPart_sortAlphabetical
+ : Messages.TestRunnerViewPart_sortRunner, AS_RADIO_BUTTON);
+ this.enableAlphabeticalSort = enableAlphabeticalSort;
+ }
+
+ @Override
+ public void run() {
+ fTestViewer.setAlphabeticalSort(enableAlphabeticalSort);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return fTestViewer.isAlphabeticalSort() == enableAlphabeticalSort;
+ }
+ }
+
+ private class FailuresOnlyFilterAction extends Action {
+ public FailuresOnlyFilterAction() {
+ super(Messages.TestRunnerViewPart_show_failures_only, AS_CHECK_BOX);
+ setToolTipText(Messages.TestRunnerViewPart_show_failures_only);
+ setImageDescriptor(Images.getImageDescriptor("obj16/failures.png")); //$NON-NLS-1$
+ }
+
+ @Override
+ public void run() {
+ setShowFailuresOnly(isChecked());
+ }
+ }
+
+ private class IgnoredOnlyFilterAction extends Action {
+ public IgnoredOnlyFilterAction() {
+ super(Messages.TestRunnerViewPart_show_ignored_only, AS_CHECK_BOX);
+ setToolTipText(Messages.TestRunnerViewPart_show_ignored_only);
+ setImageDescriptor(Images.getImageDescriptor("obj16/testignored.png")); //$NON-NLS-1$
+ }
+
+ @Override
+ public void run() {
+ setShowIgnoredOnly(isChecked());
+ }
+ }
+
+ private class ShowTimeAction extends Action {
+ public ShowTimeAction() {
+ super(Messages.TestRunnerViewPart_show_execution_time, IAction.AS_CHECK_BOX);
+ }
+
+ @Override
+ public void run() {
+ setShowExecutionTime(isChecked());
+ }
+ }
+
+ private class ShowTestHierarchyAction extends Action {
+ public ShowTestHierarchyAction() {
+ super(Messages.TestRunnerViewPart_hierarchical_layout, IAction.AS_CHECK_BOX);
+ setImageDescriptor(Images.getImageDescriptor("elcl16/hierarchicalLayout.png")); //$NON-NLS-1$
+ }
+
+ @Override
+ public void run() {
+ setFilterAndLayout(fFailuresOnlyFilterAction.isChecked(), fIgnoredOnlyFilterAction.isChecked(),
+ isChecked() ? TestResultsLayout.HIERARCHICAL : TestResultsLayout.FLAT);
+ }
+ }
+
+ private class ActivateOnErrorAction extends Action {
+ public ActivateOnErrorAction() {
+ super(Messages.TestRunnerViewPart_activate_on_failure_only, IAction.AS_CHECK_BOX);
+ // setImageDescriptor(UnitTestPlugin.getImageDescriptor("obj16/failures.png"));
+ // //$NON-NLS-1$
+ update();
+ }
+
+ public void update() {
+ setChecked(getShowOnErrorOnly());
+ }
+
+ @Override
+ public void run() {
+ boolean checked = isChecked();
+ fShowOnErrorOnly = checked;
+ InstanceScope.INSTANCE.getNode(UnitTestPlugin.PLUGIN_ID)
+ .putBoolean(UnitTestPreferencesConstants.SHOW_ON_ERROR_ONLY, checked);
+ }
+ }
+
+ /**
+ * Constructs Test Runner View part object
+ */
+ public TestRunnerViewPart() {
+ fImagesToDispose = new ArrayList<>();
+
+ fStackViewIcon = createManagedImage("eview16/stackframe.png");//$NON-NLS-1$
+ fTestRunOKIcon = createManagedImage("eview16/unitsucc.png"); //$NON-NLS-1$
+ fTestRunFailIcon = createManagedImage("eview16/uniterr.png"); //$NON-NLS-1$
+ fTestRunOKDirtyIcon = createManagedImage("eview16/unitsuccq.png"); //$NON-NLS-1$
+ fTestRunFailDirtyIcon = createManagedImage("eview16/uniterrq.png"); //$NON-NLS-1$
+
+ fTestIcon = createManagedImage("obj16/test.png"); //$NON-NLS-1$
+ fTestOkIcon = createManagedImage("obj16/testok.png"); //$NON-NLS-1$
+ fTestErrorIcon = createManagedImage("obj16/testerr.png"); //$NON-NLS-1$
+ fTestFailIcon = createManagedImage("obj16/testfail.png"); //$NON-NLS-1$
+ fTestRunningIcon = createManagedImage("obj16/testrun.png"); //$NON-NLS-1$
+ fTestIgnoredIcon = createManagedImage("obj16/testignored.png"); //$NON-NLS-1$
+ fTestAssumptionFailureIcon = createManagedImage("obj16/testassumptionfailed.png"); //$NON-NLS-1$
+
+ fSuiteIcon = createManagedImage(fSuiteIconDescriptor);
+ fSuiteOkIcon = createManagedImage(fSuiteOkIconDescriptor);
+ fSuiteErrorIcon = createManagedImage(fSuiteErrorIconDescriptor);
+ fSuiteFailIcon = createManagedImage(fSuiteFailIconDescriptor);
+ fSuiteRunningIcon = createManagedImage(fSuiteRunningIconDescriptor);
+ }
+
+ private Image createManagedImage(String path) {
+ return createManagedImage(Images.getImageDescriptor(path));
+ }
+
+ private Image createManagedImage(ImageDescriptor descriptor) {
+ Image image = descriptor.createImage();
+ if (image == null) {
+ image = ImageDescriptor.getMissingImageDescriptor().createImage();
+ }
+ fImagesToDispose.add(image);
+ return image;
+ }
+
+ @Override
+ public void init(IViewSite site, IMemento memento) throws PartInitException {
+ super.init(site, memento);
+ fMemento = memento;
+ IWorkbenchSiteProgressService progressService = getProgressService();
+ if (progressService != null)
+ progressService.showBusyForFamily(TestRunnerViewPart.FAMILY_UNITTEST_RUN);
+ }
+
+ private IWorkbenchSiteProgressService getProgressService() {
+ Object siteService = getSite().getAdapter(IWorkbenchSiteProgressService.class);
+ if (siteService != null)
+ return (IWorkbenchSiteProgressService) siteService;
+ return null;
+ }
+
+ @Override
+ public void saveState(IMemento memento) {
+ if (fSashForm == null) {
+ // part has not been created
+ if (fMemento != null) // Keep the old state;
+ memento.putMemento(fMemento);
+ return;
+ }
+
+// int activePage= fTabFolder.getSelectionIndex();
+// memento.putInteger(TAG_PAGE, activePage);
+ memento.putString(TAG_SCROLL, fScrollLockAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
+ int weigths[] = fSashForm.getWeights();
+ int ratio = (weigths[0] * 1000) / (weigths[0] + weigths[1]);
+ memento.putInteger(TAG_RATIO, ratio);
+ memento.putInteger(TAG_ORIENTATION, fOrientation);
+
+ memento.putString(TAG_FAILURES_ONLY, fFailuresOnlyFilterAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
+ memento.putString(TAG_IGNORED_ONLY, fIgnoredOnlyFilterAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
+ memento.putString(TAG_LAYOUT, fLayout.name());
+ memento.putString(TAG_SHOW_TIME, fShowTimeAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ private void restoreLayoutState(IMemento memento) {
+// Integer page= memento.getInteger(TAG_PAGE);
+// if (page != null) {
+// int p= page.intValue();
+// if (p < fTestRunTabs.size()) { // tab count can decrease if a contributing plug-in is removed
+// fTabFolder.setSelection(p);
+// fActiveRunTab= (TestRunTab)fTestRunTabs.get(p);
+// }
+// }
+ Integer ratio = memento.getInteger(TAG_RATIO);
+ if (ratio != null)
+ fSashForm.setWeights(ratio.intValue(), 1000 - ratio.intValue());
+ Integer orientation = memento.getInteger(TAG_ORIENTATION);
+ if (orientation != null)
+ fOrientation = orientation.intValue();
+ computeOrientation();
+ String scrollLock = memento.getString(TAG_SCROLL);
+ if (scrollLock != null) {
+ fScrollLockAction.setChecked(scrollLock.equals("true")); //$NON-NLS-1$
+ setAutoScroll(!fScrollLockAction.isChecked());
+ }
+
+ String layoutString = memento.getString(TAG_LAYOUT);
+ TestResultsLayout layout = layoutString == null ? TestResultsLayout.HIERARCHICAL
+ : TestResultsLayout.valueOf(layoutString);
+
+ String failuresOnly = memento.getString(TAG_FAILURES_ONLY);
+ boolean showFailuresOnly = false;
+ if (failuresOnly != null)
+ showFailuresOnly = failuresOnly.equals("true"); //$NON-NLS-1$
+
+ String ignoredOnly = memento.getString(TAG_IGNORED_ONLY);
+ boolean showIgnoredOnly = false;
+ if (ignoredOnly != null)
+ showIgnoredOnly = ignoredOnly.equals("true"); //$NON-NLS-1$
+
+ String time = memento.getString(TAG_SHOW_TIME);
+ boolean showTime = true;
+ if (time != null)
+ showTime = time.equals("true"); //$NON-NLS-1$
+
+ setFilterAndLayout(showFailuresOnly, showIgnoredOnly, layout);
+ setShowExecutionTime(showTime);
+ }
+
+ /**
+ * Stops the currently running test and shuts down the RemoteTestRunner
+ */
+ public void stopTest() {
+ if (fTestRunSession != null) {
+ if (fTestRunSession.isRunning()) {
+ setContentDescription(Messages.TestRunnerViewPart_message_stopping);
+ }
+ fTestRunSession.abortTestRun();
+ }
+ }
+
+ private void startUpdateJobs() {
+ postSyncProcessChanges();
+
+ if (fUpdateJob != null) {
+ return;
+ }
+ fUnitTestIsRunningJob = new UnitTestIsRunningJob(Messages.TestRunnerViewPart_wrapperJobName);
+ fUnitTestIsRunningLock = Job.getJobManager().newLock();
+ // acquire lock while a test run is running
+ // the lock is released when the test run terminates
+ // the wrapper job will wait on this lock.
+ fUnitTestIsRunningLock.acquire();
+ getProgressService().schedule(fUnitTestIsRunningJob);
+
+ fUpdateJob = new UpdateUIJob(Messages.TestRunnerViewPart_jobName);
+ fUpdateJob.schedule(REFRESH_INTERVAL);
+ }
+
+ private void stopUpdateJobs() {
+ if (fUpdateJob != null) {
+ fUpdateJob.stop();
+ fUpdateJob = null;
+ }
+ if (fUnitTestIsRunningJob != null && fUnitTestIsRunningLock != null) {
+ fUnitTestIsRunningLock.release();
+ fUnitTestIsRunningJob = null;
+ }
+ postSyncProcessChanges();
+ }
+
+ private void processChangesInUI() {
+ if (fSashForm.isDisposed())
+ return;
+
+ doShowInfoMessage();
+ refreshCounters();
+
+ if (!fPartIsVisible)
+ updateViewTitleProgress();
+ else {
+ updateViewIcon();
+ }
+ updateNextPreviousActions();
+
+ fTestViewer.processChangesInUI();
+ }
+
+ private void updateNextPreviousActions() {
+ boolean hasErrorsOrFailures = !fIgnoredOnlyFilterAction.isChecked() && hasErrorsOrFailures();
+ fNextAction.setEnabled(hasErrorsOrFailures);
+ fPreviousAction.setEnabled(hasErrorsOrFailures);
+ }
+
+ private String rerunLaunchMode() {
+ return fTestRunSession != null && fTestRunSession.getLaunch() != null
+ ? fTestRunSession.getLaunch().getLaunchMode()
+ : ILaunchManager.RUN_MODE;
+ }
+
+ /**
+ * Re-runs the tests executing the failed tests first
+ */
+ private void rerunFailedTestCases() {
+ if (lastLaunchStillRunning()) {
+ // prompt for terminating the existing run
+ if (MessageDialog.openQuestion(getSite().getShell(), Messages.TestRunnerViewPart_terminate_title,
+ Messages.TestRunnerViewPart_terminate_message) && fTestRunSession != null) {
+ fTestRunSession.abortTestRun();
+ }
+ }
+ List<ITestElement> allFailedTestCases = new ArrayList<>();
+ collectFailedTestCases(fTestRunSession, allFailedTestCases);
+ ILaunchConfiguration tmp = fTestRunSession.getTestViewSupport().getRerunLaunchConfiguration(allFailedTestCases);
+ if (tmp != null) {
+ DebugUITools.launch(tmp, rerunLaunchMode());
+ }
+ }
+
+ private void collectFailedTestCases(TestElement testElement, List<ITestElement> allFailedTestCases) {
+ if (testElement == null) {
+ return;
+ }
+ Result result = testElement.getTestResult(true);
+ if (result != Result.ERROR && result != Result.FAILURE) {
+ return;
+ }
+ if (testElement instanceof TestCaseElement) {
+ allFailedTestCases.add(testElement);
+ } else if (testElement instanceof ITestSuiteElement) {
+ ((ITestSuiteElement) testElement).getChildren()
+ .forEach(child -> collectFailedTestCases((TestElement) child, allFailedTestCases));
+ }
+
+ }
+
+ /**
+ * Sets auto-scroll enabled value
+ *
+ * @param scroll <code>true</code> in case of auto-scroll enabled, otherwise -
+ * <code>false</code>
+ */
+ public void setAutoScroll(boolean scroll) {
+ fAutoScroll = scroll;
+ }
+
+ /**
+ * Indicates if autoscroll is enabled
+ *
+ * @return <code>true</code> if the output scroll and reveal is needed for the
+ * tests as they are executed, otherwise returns <code>false</code>
+ */
+ public boolean isAutoScroll() {
+ return fAutoScroll;
+ }
+
+ /**
+ * Selects the next failure in the tests tree
+ */
+ public void selectNextFailure() {
+ fTestViewer.selectFailure(true);
+ }
+
+ /**
+ * Selects the previous failure in the tests tree
+ */
+ public void selectPreviousFailure() {
+ fTestViewer.selectFailure(false);
+ }
+
+ /**
+ * Selects the first failure in the tests tree
+ */
+ protected void selectFirstFailure() {
+ fTestViewer.selectFirstFailure();
+ }
+
+ private boolean hasErrorsOrFailures() {
+ return getErrorsPlusFailures() > 0;
+ }
+
+ private int getErrorsPlusFailures() {
+ if (fTestRunSession == null)
+ return 0;
+ else
+ return fTestRunSession.getCurrentErrorCount() + fTestRunSession.getCurrentFailureCount();
+ }
+
+ private void handleStopped() {
+ postSyncRunnable(() -> {
+ if (isDisposed())
+ return;
+ resetViewIcon();
+ fStopAction.setEnabled(false);
+ updateRerunFailedFirstAction();
+ });
+ stopUpdateJobs();
+ showMessageIfNoTests();
+ }
+
+ private void showMessageIfNoTests() {
+ if (fTestRunSession != null && fTestRunSession.getFinalTestCaseCount() != null
+ && fTestRunSession.getFinalTestCaseCount().intValue() == 0) {
+ Display.getDefault().asyncExec(() -> {
+ String msg = MessageFormat.format(Messages.TestRunnerViewPart_error_no_tests_found, getDisplayName());
+ MessageDialog.openInformation(getSite().getShell(), Messages.TestRunnerViewPart__error_cannotrun, msg);
+ });
+ }
+ }
+
+ private void resetViewIcon() {
+ fViewImage = fOriginalViewImage;
+ firePropertyChange(IWorkbenchPart.PROP_TITLE);
+ }
+
+ private void updateViewIcon() {
+ if (fTestRunSession == null || fTestRunSession.isStopped() || fTestRunSession.isRunning()
+ || fTestRunSession.countStartedTestCases() == 0)
+ fViewImage = fOriginalViewImage;
+ else if (hasErrorsOrFailures())
+ fViewImage = fTestRunFailIcon;
+ else
+ fViewImage = fTestRunOKIcon;
+ firePropertyChange(IWorkbenchPart.PROP_TITLE);
+ }
+
+ private void updateViewTitleProgress() {
+ if (fTestRunSession != null) {
+ if (fTestRunSession.isRunning()) {
+ Image progress = fProgressImages.getImage(fTestRunSession.countStartedTestCases(),
+ fTestRunSession.getFinalTestCaseCount(),
+ fTestRunSession.getCurrentErrorCount() > 0 || fTestRunSession.getCurrentFailureCount() > 0);
+ if (progress != fViewImage) {
+ fViewImage = progress;
+ firePropertyChange(IWorkbenchPart.PROP_TITLE);
+ }
+ } else {
+ updateViewIcon();
+ }
+ } else {
+ resetViewIcon();
+ }
+ }
+
+ /**
+ * Sets an active test run session
+ *
+ * @param testRunSession new active test run session
+ * @return deactivated session, or <code>null</code> iff no session got
+ * deactivated
+ */
+ public TestRunSession setActiveTestRunSession(TestRunSession testRunSession) {
+ /*
+ * - State: fTestRunSession fTestSessionListener Jobs
+ * fTestViewer.processChangesInUI(); - UI: fCounterPanel fProgressBar
+ * setContentDescription / fInfoMessage setTitleToolTip view icons statusLine
+ * fFailureTrace
+ *
+ * action enablement
+ */
+ if (fTestRunSession == testRunSession)
+ return null;
+
+ deregisterTestSessionListener();
+
+ TestRunSession deactivatedSession = fTestRunSession;
+
+ fTestRunSession = testRunSession;
+ fTestViewer.registerActiveSession(testRunSession);
+ History.INSTANCE.watch(testRunSession);
+
+ if (fSashForm.isDisposed()) {
+ stopUpdateJobs();
+ return deactivatedSession;
+ }
+
+ if (testRunSession == null) {
+ setTitleToolTip(null);
+ resetViewIcon();
+ clearStatus();
+ fFailureTrace.clear();
+
+ registerInfoMessage(" "); //$NON-NLS-1$
+ stopUpdateJobs();
+
+ fStopAction.setEnabled(false);
+ fRerunFailedCasesAction.setEnabled(false);
+ fRerunLastTestAction.setEnabled(false);
+
+ } else {
+ if (fTestRunSession.isStarting() || fTestRunSession.isRunning()) {
+ fTestSessionListener = new TestSessionListener();
+ fTestRunSession.addTestSessionListener(fTestSessionListener);
+ }
+ if (!fTestRunSession.isStarting() && !fShowOnErrorOnly) {
+ showTestResultsView();
+ }
+
+ setTitleToolTip();
+
+ clearStatus();
+ fFailureTrace.clear();
+ registerInfoMessage(BasicElementLabels.getJavaElementName(fTestRunSession.getTestRunName()));
+
+ updateRerunFailedFirstAction();
+ fRerunLastTestAction.setEnabled(fTestRunSession.getLaunch() != null);
+ fEditLaunchConfigAction.setEnabled(fTestRunSession.getLaunch() != null);
+
+ fStopAction.setEnabled(fTestRunSession.isRunning());
+ if (fTestRunSession.isRunning()) {
+ startUpdateJobs();
+ } else /* old or fresh session: don't want jobs at this stage */ {
+ stopUpdateJobs();
+ }
+ }
+ getSite().getShell().getDisplay().asyncExec(this::processChangesInUI);
+ return deactivatedSession;
+ }
+
+ private void deregisterTestSessionListener() {
+ if (fTestRunSession != null && fTestSessionListener != null) {
+ fTestRunSession.removeTestSessionListener(fTestSessionListener);
+ fTestSessionListener = null;
+ }
+ }
+
+ private void updateRerunFailedFirstAction() {
+ boolean state = hasErrorsOrFailures() && fTestRunSession.getLaunch().getLaunchConfiguration() != null;
+ fRerunFailedCasesAction.setEnabled(state);
+ }
+
+ /**
+ * Returns the display name of the current test run session
+ *
+ * @return the display name of the current test run session, or
+ * <code>null</code>
+ */
+ public String getDisplayName() {
+ ITestViewSupport testViewSupport = fTestRunSession.getTestViewSupport();
+ return testViewSupport != null ? testViewSupport.getDisplayName() : null;
+ }
+
+ private void setTitleToolTip() {
+ String displayStr = getDisplayName();
+
+ String testRunLabel = BasicElementLabels.getJavaElementName(fTestRunSession.getTestRunName());
+ if (displayStr != null)
+ setTitleToolTip(MessageFormat.format(Messages.TestRunnerViewPart_titleToolTip, testRunLabel, displayStr));
+ else
+ setTitleToolTip(testRunLabel);
+ }
+
+ @Override
+ public synchronized void dispose() {
+ fIsDisposed = true;
+ if (fTestRunSessionListener != null)
+ UnitTestModel.getInstance().removeTestRunSessionListener(fTestRunSessionListener);
+
+ IHandlerService handlerService = getSite().getWorkbenchWindow().getService(IHandlerService.class);
+ handlerService.deactivateHandler(fRerunLastActivation);
+ handlerService.deactivateHandler(fRerunFailedFirstActivation);
+ setActiveTestRunSession(null);
+
+ if (fProgressImages != null)
+ fProgressImages.dispose();
+ getViewSite().getPage().removePartListener(fPartListener);
+
+ disposeImages();
+ if (fClipboard != null)
+ fClipboard.dispose();
+ if (fViewMenuListener != null) {
+ getViewSite().getActionBars().getMenuManager().removeMenuListener(fViewMenuListener);
+ }
+ /*
+ * if (fDirtyListener != null) {
+ * JavaCore.removeElementChangedListener(fDirtyListener); fDirtyListener= null;
+ * }
+ */
+ if (fFailureTrace != null) {
+ fFailureTrace.dispose();
+ }
+ }
+
+ private void disposeImages() {
+ for (Image imageToDispose : fImagesToDispose) {
+ imageToDispose.dispose();
+ }
+ }
+
+ private void postSyncRunnable(Runnable r) {
+ if (!isDisposed())
+ getDisplay().syncExec(r);
+ }
+
+ private void refreshCounters() {
+ // TODO: Inefficient. Either
+ // - keep a boolean fHasTestRun and update only on changes, or
+ // - improve components to only redraw on changes (once!).
+
+ int startedCount;
+ int ignoredCount;
+ Integer totalCount;
+ int errorCount;
+ int failureCount;
+ int assumptionFailureCount;
+ boolean hasErrorsOrFailures;
+ boolean stopped;
+
+ if (fTestRunSession != null) {
+ startedCount = fTestRunSession.countStartedTestCases();
+ ignoredCount = fTestRunSession.getCurrentIgnoredCount();
+ totalCount = fTestRunSession.getFinalTestCaseCount();
+ errorCount = fTestRunSession.getCurrentErrorCount();
+ failureCount = fTestRunSession.getCurrentFailureCount();
+ assumptionFailureCount = fTestRunSession.getCurrentAssumptionFailureCount();
+ hasErrorsOrFailures = errorCount + failureCount > 0;
+ stopped = fTestRunSession.isStopped();
+ } else {
+ startedCount = 0;
+ ignoredCount = 0;
+ totalCount = null;
+ errorCount = 0;
+ failureCount = 0;
+ assumptionFailureCount = 0;
+ hasErrorsOrFailures = false;
+ stopped = false;
+ }
+
+ fCounterPanel.setTotal(totalCount);
+ fCounterPanel.setRunValue(startedCount, ignoredCount, assumptionFailureCount);
+ fCounterPanel.setErrorValue(errorCount);
+ fCounterPanel.setFailureValue(failureCount);
+
+ int ticksDone;
+ if (startedCount == 0) {
+ ticksDone = 0;
+ } else if (totalCount != null && startedCount == totalCount.intValue()
+ && fTestRunSession.getProgressState() == ProgressState.COMPLETED) {
+ ticksDone = totalCount.intValue();
+ } else {
+ ticksDone = startedCount - 1;
+ }
+
+ fProgressBar.reset(hasErrorsOrFailures, stopped, ticksDone,
+ totalCount != null ? totalCount.intValue() : ticksDone + 1);
+ }
+
+ /**
+ * Queues a runnable that shows a test results view
+ */
+ protected void postShowTestResultsView() {
+ postSyncRunnable(() -> {
+ if (isDisposed())
+ return;
+ showTestResultsView();
+ });
+ }
+
+ /**
+ * Makes the test results view visible
+ */
+ public void showTestResultsView() {
+ IWorkbenchWindow window = getSite().getWorkbenchWindow();
+ IWorkbenchPage page = window.getActivePage();
+ TestRunnerViewPart testRunner = null;
+
+ if (page != null) {
+ try { // show the result view
+ testRunner = (TestRunnerViewPart) page.findView(TestRunnerViewPart.NAME);
+ if (testRunner == null) {
+ IWorkbenchPart activePart = page.getActivePart();
+ testRunner = (TestRunnerViewPart) page.showView(TestRunnerViewPart.NAME, null,
+ IWorkbenchPage.VIEW_VISIBLE);
+ // restore focus
+ page.activate(activePart);
+ } else {
+ page.bringToTop(testRunner);
+ }
+ } catch (PartInitException pie) {
+ UnitTestPlugin.log(pie);
+ }
+ }
+ }
+
+ /**
+ * Shows an info message
+ */
+ protected void doShowInfoMessage() {
+ if (fInfoMessage != null) {
+ setContentDescription(fInfoMessage);
+ fInfoMessage = null;
+ }
+ }
+
+ /**
+ * Registers a test information message
+ *
+ * @param message an information message to register
+ */
+ public void registerInfoMessage(String message) {
+ fInfoMessage = message;
+ }
+
+ private SashForm createSashForm(Composite parent) {
+ fSashForm = new SashForm(parent, SWT.VERTICAL);
+
+ ViewForm top = new ViewForm(fSashForm, SWT.NONE);
+
+ Composite empty = new Composite(top, SWT.NONE);
+ empty.setLayout(new Layout() {
+ @Override
+ protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
+ return new Point(1, 1); // (0, 0) does not work with super-intelligent ViewForm
+ }
+
+ @Override
+ protected void layout(Composite composite, boolean flushCache) {
+ }
+ });
+ top.setTopLeft(empty); // makes ViewForm draw the horizontal separator line ...
+ fTestViewer = new TestViewer(top, fClipboard, this);
+ top.setContent(fTestViewer.getTestViewerControl());
+
+ ViewForm bottom = new ViewForm(fSashForm, SWT.NONE);
+
+ CLabel label = new CLabel(bottom, SWT.NONE);
+ label.setText(Messages.TestRunnerViewPart_label_failure);
+ label.setImage(fStackViewIcon);
+ bottom.setTopLeft(label);
+ ToolBar failureToolBar = new ToolBar(bottom, SWT.FLAT | SWT.WRAP);
+ bottom.setTopCenter(failureToolBar);
+ fFailureTrace = new FailureTraceUIBlock(bottom, fClipboard, this, failureToolBar);
+ bottom.setContent(fFailureTrace.getComposite());
+
+ fSashForm.setWeights(50, 50);
+ return fSashForm;
+ }
+
+ private void clearStatus() {
+ getStatusLine().setMessage(null);
+ getStatusLine().setErrorMessage(null);
+ }
+
+ @Override
+ public void setFocus() {
+ if (fTestViewer != null)
+ fTestViewer.getTestViewerControl().setFocus();
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ fParent = parent;
+ addResizeListener(parent);
+ fClipboard = new Clipboard(parent.getDisplay());
+
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ parent.setLayout(gridLayout);
+
+// fViewHistory= new RunnerViewHistory();
+ configureToolBar();
+
+ fCounterComposite = createProgressCountPanel(parent);
+ fCounterComposite.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
+ SashForm sashForm = createSashForm(parent);
+ sashForm.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ IActionBars actionBars = getViewSite().getActionBars();
+
+ fCopyAction = new UnitTestCopyAction(fFailureTrace, fClipboard);
+ fCopyAction.setActionDefinitionId(ActionFactory.COPY.getCommandId());
+ actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), fCopyAction);
+
+ fPasteAction = new UnitTesttPasteAction(parent.getShell(), fClipboard);
+ fPasteAction.setActionDefinitionId(ActionFactory.PASTE.getCommandId());
+ actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), fPasteAction);
+
+ initPageSwitcher();
+ addDropAdapter(parent);
+
+ fOriginalViewImage = getTitleImage();
+ fProgressImages = new ProgressIcons(fOriginalViewImage);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, IUnitTestHelpContextIds.RESULTS_VIEW);
+
+ getViewSite().getPage().addPartListener(fPartListener);
+
+ setFilterAndLayout(false, false, TestResultsLayout.HIERARCHICAL);
+ setShowExecutionTime(true);
+ if (fMemento != null) {
+ restoreLayoutState(fMemento);
+ }
+ fMemento = null;
+
+ fTestRunSessionListener = new TestRunSessionListener();
+ UnitTestModel.getInstance().addTestRunSessionListener(fTestRunSessionListener);
+ UnitTestModel.getInstance().addTestRunSessionListener(History.INSTANCE);
+
+ // always show youngest test run in view. simulate "sessionAdded" event to do
+ // that
+ List<TestRunSession> testRunSessions = UnitTestModel.getInstance().getTestRunSessions();
+ if (!testRunSessions.isEmpty()) {
+ fTestRunSessionListener.sessionAdded(testRunSessions.get(0));
+ }
+ }
+
+ private void addDropAdapter(Composite parent) {
+ DropTarget dropTarget = new DropTarget(parent,
+ DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK | DND.DROP_DEFAULT);
+ dropTarget.setTransfer(TextTransfer.getInstance());
+ class DropAdapter extends DropTargetAdapter {
+ @Override
+ public void dragEnter(DropTargetEvent event) {
+ event.detail = DND.DROP_COPY;
+ event.feedback = DND.FEEDBACK_NONE;
+ }
+
+ @Override
+ public void dragOver(DropTargetEvent event) {
+ event.detail = DND.DROP_COPY;
+ event.feedback = DND.FEEDBACK_NONE;
+ }
+
+ @Override
+ public void dragOperationChanged(DropTargetEvent event) {
+ event.detail = DND.DROP_COPY;
+ event.feedback = DND.FEEDBACK_NONE;
+ }
+
+ @Override
+ public void drop(final DropTargetEvent event) {
+ if (TextTransfer.getInstance().isSupportedType(event.currentDataType)) {
+ String url = (String) event.data;
+ importTestRunSession(url);
+ }
+ }
+ }
+ dropTarget.addDropListener(new DropAdapter());
+ }
+
+ private void initPageSwitcher() {
+ /*
+ * @SuppressWarnings("unused") PageSwitcher pageSwitcher= new PageSwitcher(this)
+ * {
+ *
+ * @Override public Object[] getPages() { return
+ * fViewHistory.getHistoryEntries().toArray(); }
+ *
+ * @Override public String getName(Object page) { return
+ * fViewHistory.getText((TestRunSession) page); }
+ *
+ * @Override public ImageDescriptor getImageDescriptor(Object page) { return
+ * fViewHistory.getImageDescriptor(page); }
+ *
+ * @Override public void activatePage(Object page) {
+ * fViewHistory.setActiveEntry((TestRunSession) page); }
+ *
+ * @Override public int getCurrentPageIndex() { return
+ * fViewHistory.getHistoryEntries().indexOf(fViewHistory.getCurrentEntry()); }
+ * };
+ */
+ }
+
+ private void addResizeListener(Composite parent) {
+ parent.addControlListener(ControlListener.controlResizedAdapter(e -> {
+ computeOrientation();
+ }));
+ }
+
+ void computeOrientation() {
+ if (fOrientation != VIEW_ORIENTATION_AUTOMATIC) {
+ fCurrentOrientation = fOrientation;
+ setOrientation(fCurrentOrientation);
+ } else {
+ Point size = fParent.getSize();
+ if (size.x != 0 && size.y != 0) {
+ if (size.x > size.y)
+ setOrientation(VIEW_ORIENTATION_HORIZONTAL);
+ else
+ setOrientation(VIEW_ORIENTATION_VERTICAL);
+ }
+ }
+ }
+
+ private void configureToolBar() {
+ IActionBars actionBars = getViewSite().getActionBars();
+ IToolBarManager toolBar = actionBars.getToolBarManager();
+ IMenuManager viewMenu = actionBars.getMenuManager();
+ fNextAction = new ShowNextFailureAction(this);
+ fNextAction.setEnabled(false);
+ actionBars.setGlobalActionHandler(ActionFactory.NEXT.getId(), fNextAction);
+
+ fPreviousAction = new ShowPreviousFailureAction(this);
+ fPreviousAction.setEnabled(false);
+ actionBars.setGlobalActionHandler(ActionFactory.PREVIOUS.getId(), fPreviousAction);
+ fStopAction = new StopAction();
+ fStopAction.setEnabled(false);
+
+ fRerunLastTestAction = new RerunLastAction();
+ IHandlerService handlerService = getSite().getWorkbenchWindow().getService(IHandlerService.class);
+ IHandler handler = new AbstractHandler() {
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ fRerunLastTestAction.run();
+ return null;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return fRerunLastTestAction.isEnabled();
+ }
+ };
+ fRerunLastActivation = handlerService.activateHandler(RERUN_LAST_COMMAND, handler);
+
+ fRerunFailedCasesAction = new RerunFailedCasesAction();
+ handler = new AbstractHandler() {
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ fRerunFailedCasesAction.run();
+ return null;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return fRerunFailedCasesAction.isEnabled();
+ }
+ };
+ fRerunFailedFirstActivation = handlerService.activateHandler(RERUN_FAILED_CASES_COMMAND, handler);
+ fEditLaunchConfigAction = new EditLaunchConfigurationAction() {
+ @Override
+ protected ILaunchConfiguration getLaunchConfiguration() {
+ return fTestRunSession != null ? fTestRunSession.getLaunch().getLaunchConfiguration() : null;
+ }
+
+ @Override
+ protected String getMode() {
+ return rerunLaunchMode();
+ }
+
+ @Override
+ protected boolean isTerminated() {
+ return true; // always allow to re-run
+ }
+ };
+ fEditLaunchConfigAction.setToolTipText(Messages.TestRunnerViewPart_editLaunchConfiguration);
+ fEditLaunchConfigAction.setImageDescriptor(
+ DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_OBJS_MODIFICATION_WATCHPOINT));
+ fEditLaunchConfigAction.setDisabledImageDescriptor(
+ DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_OBJS_MODIFICATION_WATCHPOINT_DISABLED));
+
+ fFailuresOnlyFilterAction = new FailuresOnlyFilterAction();
+ fIgnoredOnlyFilterAction = new IgnoredOnlyFilterAction();
+
+ fScrollLockAction = new ScrollLockAction(this);
+ fScrollLockAction.setChecked(!fAutoScroll);
+
+ fToggleOrientationActions = new ToggleOrientationAction[] {
+ new ToggleOrientationAction(VIEW_ORIENTATION_VERTICAL),
+ new ToggleOrientationAction(VIEW_ORIENTATION_HORIZONTAL),
+ new ToggleOrientationAction(VIEW_ORIENTATION_AUTOMATIC) };
+
+ toolBar.add(fNextAction);
+ toolBar.add(fPreviousAction);
+ toolBar.add(fFailuresOnlyFilterAction);
+ toolBar.add(fIgnoredOnlyFilterAction);
+ toolBar.add(fScrollLockAction);
+ toolBar.add(new Separator());
+ toolBar.add(fRerunLastTestAction);
+ toolBar.add(fRerunFailedCasesAction);
+ toolBar.add(fStopAction);
+ toolBar.add(fEditLaunchConfigAction);
+ toolBar.add(new Separator());
+ IContributionItem historyIte = new CommandContributionItem(new CommandContributionItemParameter(getSite(),
+ HistoryHandler.COMMAND_ID, HistoryHandler.COMMAND_ID, SWT.PUSH));
+ toolBar.add(historyIte);
+
+ fShowTestHierarchyAction = new ShowTestHierarchyAction();
+ fShowTimeAction = new ShowTimeAction();
+ viewMenu.add(fShowTestHierarchyAction);
+ viewMenu.add(fShowTimeAction);
+ viewMenu.add(new Separator());
+
+ MenuManager layoutSubMenu = new MenuManager(Messages.TestRunnerViewPart_layout_menu);
+ for (ToggleOrientationAction toggleOrientationAction : fToggleOrientationActions) {
+ layoutSubMenu.add(toggleOrientationAction);
+ }
+ viewMenu.add(layoutSubMenu);
+ MenuManager sortSubmenu = new MenuManager(Messages.TestRunnerViewPart_sort);
+ sortSubmenu.add(new SortAction(true));
+ sortSubmenu.add(new SortAction(false));
+ viewMenu.add(sortSubmenu);
+ viewMenu.add(new Separator());
+
+ viewMenu.add(fFailuresOnlyFilterAction);
+ viewMenu.add(fIgnoredOnlyFilterAction);
+
+ fActivateOnErrorAction = new ActivateOnErrorAction();
+ viewMenu.add(fActivateOnErrorAction);
+ fViewMenuListener = manager -> fActivateOnErrorAction.update();
+
+ viewMenu.addMenuListener(fViewMenuListener);
+
+ actionBars.updateActionBars();
+ }
+
+ private IStatusLineManager getStatusLine() {
+ // we want to show messages globally hence we
+ // have to go through the active part
+ IViewSite site = getViewSite();
+ IWorkbenchPage page = site.getPage();
+ IWorkbenchPart activePart = page.getActivePart();
+
+ if (activePart instanceof IViewPart) {
+ IViewPart activeViewPart = (IViewPart) activePart;
+ IViewSite activeViewSite = activeViewPart.getViewSite();
+ return activeViewSite.getActionBars().getStatusLineManager();
+ }
+
+ if (activePart instanceof IEditorPart) {
+ IEditorPart activeEditorPart = (IEditorPart) activePart;
+ IEditorActionBarContributor contributor = activeEditorPart.getEditorSite().getActionBarContributor();
+ if (contributor instanceof EditorActionBarContributor)
+ return ((EditorActionBarContributor) contributor).getActionBars().getStatusLineManager();
+ }
+ // no active part
+ return getViewSite().getActionBars().getStatusLineManager();
+ }
+
+ /**
+ * Creates a progress count panel
+ *
+ * @param parent a parent composite
+ * @return a progress count ppanel composite object
+ */
+ protected Composite createProgressCountPanel(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ composite.setLayout(layout);
+ setCounterColumns(layout);
+
+ fCounterPanel = new CounterPanel(composite);
+ fCounterPanel.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
+ fProgressBar = new UnitTestProgressBar(composite);
+ fProgressBar.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
+ return composite;
+ }
+
+ /**
+ * Handles the test element selection
+ *
+ * @param test a selected test element
+ */
+ public void handleTestSelected(TestElement test) {
+ showFailure(test);
+ fCopyAction.handleTestSelected(test);
+ }
+
+ private void showFailure(final TestElement test) {
+ postSyncRunnable(() -> {
+ if (!isDisposed()) {
+ fFailureTrace.showFailure(test);
+ }
+ });
+ }
+
+ /**
+ * Returns a current test run session object
+ *
+ * @return the current test run session, or <code>null</code>
+ */
+ public TestRunSession getCurrentTestRunSession() {
+ return fTestRunSession;
+ }
+
+ private boolean isDisposed() {
+ return fIsDisposed || fCounterPanel.isDisposed();
+ }
+
+ private Display getDisplay() {
+ return getViewSite().getShell().getDisplay();
+ }
+
+ @Override
+ public Image getTitleImage() {
+ if (fOriginalViewImage == null) {
+ fOriginalViewImage = super.getTitleImage();
+ }
+
+ if (fViewImage == null) {
+ return super.getTitleImage();
+ }
+ return fViewImage;
+ }
+
+ void codeHasChanged() {
+ /*
+ * if (fDirtyListener != null) {
+ * JavaCore.removeElementChangedListener(fDirtyListener); fDirtyListener= null;
+ * }
+ */
+ if (fViewImage == fTestRunOKIcon)
+ fViewImage = fTestRunOKDirtyIcon;
+ else if (fViewImage == fTestRunFailIcon)
+ fViewImage = fTestRunFailDirtyIcon;
+
+ Runnable r = () -> {
+ if (isDisposed())
+ return;
+ firePropertyChange(IWorkbenchPart.PROP_TITLE);
+ };
+ if (!isDisposed())
+ getDisplay().asyncExec(r);
+ }
+
+ private void postSyncProcessChanges() {
+ postSyncRunnable(this::processChangesInUI);
+ }
+
+ /**
+ * Warns on content change
+ */
+ public void warnOfContentChange() {
+ IWorkbenchSiteProgressService service = getProgressService();
+ if (service != null)
+ service.warnOfContentChange();
+ }
+
+ /**
+ * Indicates if the last test launch is kept alive
+ *
+ * @return <code>true</code> in case of the last test launch is kept alive,
+ * otherwise returns <code>false</code>
+ */
+ public boolean lastLaunchStillRunning() {
+ return fTestRunSession != null && !fTestRunSession.getLaunch().isTerminated();
+ }
+
+ private void setOrientation(int orientation) {
+ if ((fSashForm == null) || fSashForm.isDisposed())
+ return;
+ boolean horizontal = orientation == VIEW_ORIENTATION_HORIZONTAL;
+ fSashForm.setOrientation(horizontal ? SWT.HORIZONTAL : SWT.VERTICAL);
+ for (ToggleOrientationAction toggleOrientationAction : fToggleOrientationActions)
+ toggleOrientationAction.setChecked(fOrientation == toggleOrientationAction.getOrientation());
+ fCurrentOrientation = orientation;
+ GridLayout layout = (GridLayout) fCounterComposite.getLayout();
+ setCounterColumns(layout);
+ fParent.layout();
+ }
+
+ private void setCounterColumns(GridLayout layout) {
+ if (fCurrentOrientation == VIEW_ORIENTATION_HORIZONTAL)
+ layout.numColumns = 2;
+ else
+ layout.numColumns = 1;
+ }
+
+ static boolean getShowOnErrorOnly() {
+ return Platform.getPreferencesService().getBoolean(UnitTestPlugin.PLUGIN_ID,
+ UnitTestPreferencesConstants.SHOW_ON_ERROR_ONLY, false, null);
+ }
+
+ static void importTestRunSession(final String url) {
+ try {
+ PlatformUI.getWorkbench().getProgressService()
+ .busyCursorWhile(monitor -> UnitTestModel.getInstance().importTestRunSession(url, monitor));
+ } catch (InterruptedException e) {
+ // cancelled
+ } catch (InvocationTargetException e) {
+ CoreException ce = (CoreException) e.getCause();
+ StatusManager.getManager().handle(ce.getStatus(), StatusManager.SHOW | StatusManager.LOG);
+ }
+ }
+
+ /**
+ * Returns the Failure Trace UI Block
+ *
+ * @return the current failure trace OI block
+ */
+ public FailureTraceUIBlock getFailureTrace() {
+ return fFailureTrace;
+ }
+
+ void setShowFailuresOnly(boolean failuresOnly) {
+ setFilterAndLayout(failuresOnly, false /* ignoredOnly must be off */, fLayout);
+ }
+
+ void setShowIgnoredOnly(boolean ignoredOnly) {
+ setFilterAndLayout(false /* failuresOnly must be off */, ignoredOnly, fLayout);
+ }
+
+ private void setFilterAndLayout(boolean failuresOnly, boolean ignoredOnly, TestResultsLayout layoutMode) {
+ fShowTestHierarchyAction.setChecked(layoutMode == TestResultsLayout.HIERARCHICAL);
+ fLayout = layoutMode;
+ fFailuresOnlyFilterAction.setChecked(failuresOnly);
+ fIgnoredOnlyFilterAction.setChecked(ignoredOnly);
+ fTestViewer.setShowFailuresOrIgnoredOnly(failuresOnly, ignoredOnly, layoutMode);
+ updateNextPreviousActions();
+ }
+
+ private void setShowExecutionTime(boolean showTime) {
+ fTestViewer.setShowTime(showTime);
+ fShowTimeAction.setChecked(showTime);
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionLabelProvider.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionLabelProvider.java
new file mode 100644
index 000000000..d7a7e98fb
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionLabelProvider.java
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.ui;
+
+import java.text.MessageFormat;
+import java.time.Duration;
+
+import org.eclipse.unittest.internal.model.Status;
+import org.eclipse.unittest.internal.model.TestCaseElement;
+import org.eclipse.unittest.internal.model.TestElement;
+import org.eclipse.unittest.internal.ui.TestRunnerViewPart.TestResultsLayout;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestRunSession;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+import org.eclipse.jface.viewers.StyledCellLabelProvider;
+import org.eclipse.jface.viewers.StyledString;
+
+/**
+ * A Test Session label provider implementation.
+ */
+class TestSessionLabelProvider extends LabelProvider implements IStyledLabelProvider {
+
+ private final TestRunnerViewPart fTestRunnerPart;
+ private final TestResultsLayout fLayoutMode;
+
+ private boolean fShowTime;
+
+ /**
+ * Constructs Test Session Provider object.
+ *
+ * @param testRunnerPart a test runner view part object
+ * @param layoutMode a layout mode
+ */
+ public TestSessionLabelProvider(TestRunnerViewPart testRunnerPart, TestResultsLayout layoutMode) {
+ fTestRunnerPart = testRunnerPart;
+ fLayoutMode = layoutMode;
+ fShowTime = true;
+ }
+
+ @Override
+ public StyledString getStyledText(Object element) {
+ if (!(element instanceof ITestElement)) {
+ return new StyledString(element.toString());
+ }
+ TestElement testElement = (TestElement) element;
+ StyledString text = new StyledString(testElement.getDisplayName());
+ if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) {
+ if (testElement.getParentContainer() instanceof ITestRunSession) {
+ String displayName = fTestRunnerPart.getDisplayName();
+ if (displayName != null) {
+ String decorated = MessageFormat.format(Messages.TestSessionLabelProvider_testName_RunnerVersion,
+ text.getString(), displayName);
+ text = StyledCellLabelProvider.styleDecoratedString(decorated, StyledString.QUALIFIER_STYLER, text);
+ }
+ }
+
+ } else {
+ if (element instanceof TestCaseElement) {
+ String decorated = getTextForFlatLayout((TestCaseElement) testElement, text.getString());
+ text = StyledCellLabelProvider.styleDecoratedString(decorated, StyledString.QUALIFIER_STYLER, text);
+ }
+ }
+ return addElapsedTime(text, testElement.getDuration());
+ }
+
+ private String getTextForFlatLayout(TestCaseElement testCaseElement, String label) {
+ String parentName;
+ String parentDisplayName = testCaseElement.getParent().getDisplayName();
+ if (parentDisplayName != null) {
+ parentName = parentDisplayName;
+ } else {
+ parentName = testCaseElement.getTestName();
+ }
+ return MessageFormat.format(Messages.TestSessionLabelProvider_testMethodName_className, label,
+ BasicElementLabels.getJavaElementName(parentName));
+ }
+
+ private StyledString addElapsedTime(StyledString styledString, Duration duration) {
+ String string = styledString.getString();
+ String decorated = addElapsedTime(string, duration);
+ return StyledCellLabelProvider.styleDecoratedString(decorated, StyledString.COUNTER_STYLER, styledString);
+ }
+
+ private String addElapsedTime(String string, Duration duration) {
+ if (!fShowTime || duration == null) {
+ return string;
+ }
+ return MessageFormat.format(Messages.TestSessionLabelProvider_testName_elapsedTimeInSeconds, string,
+ Double.valueOf(duration.toNanos() / 1.0e9));
+ }
+
+ @Override
+ public String getText(Object element) {
+ if (!(element instanceof ITestElement)) {
+ return element.toString();
+ }
+ TestElement testElement = (TestElement) element;
+ String label = testElement.getDisplayName();
+ if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) {
+ if (testElement instanceof ITestRunSession || (testElement.getParent() instanceof ITestRunSession
+ && testElement.getParent().getChildren().size() <= 1)) {
+ String displayName = fTestRunnerPart.getDisplayName();
+ if (displayName != null) {
+ label = MessageFormat.format(Messages.TestSessionLabelProvider_testName_RunnerVersion, label,
+ displayName);
+ }
+ }
+ } else {
+ if (element instanceof TestCaseElement) {
+ label = getTextForFlatLayout((TestCaseElement) testElement, label);
+ }
+ }
+ return addElapsedTime(label, testElement.getDuration());
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ if (element instanceof TestElement && ((TestElement) element).isAssumptionFailure())
+ return fTestRunnerPart.fTestAssumptionFailureIcon;
+
+ if (element instanceof TestCaseElement) {
+ TestCaseElement testCaseElement = ((TestCaseElement) element);
+ if (testCaseElement.isIgnored())
+ return fTestRunnerPart.fTestIgnoredIcon;
+
+ Status status = testCaseElement.getStatus();
+ if (status.isNotRun())
+ return fTestRunnerPart.fTestIcon;
+ else if (status.isRunning())
+ return fTestRunnerPart.fTestRunningIcon;
+ else if (status.isError())
+ return fTestRunnerPart.fTestErrorIcon;
+ else if (status.isFailure())
+ return fTestRunnerPart.fTestFailIcon;
+ else if (status.isOK())
+ return fTestRunnerPart.fTestOkIcon;
+ else
+ throw new IllegalStateException(element.toString());
+ } else if (element instanceof TestElement) { // suite or session
+ Status status = ((TestElement) element).getStatus();
+ if (status.isNotRun())
+ return fTestRunnerPart.fSuiteIcon;
+ else if (status.isRunning())
+ return fTestRunnerPart.fSuiteRunningIcon;
+ else if (status.isError())
+ return fTestRunnerPart.fSuiteErrorIcon;
+ else if (status.isFailure())
+ return fTestRunnerPart.fSuiteFailIcon;
+ else if (status.isOK())
+ return fTestRunnerPart.fSuiteOkIcon;
+ else
+ throw new IllegalStateException(element.toString());
+ } else {
+ throw new IllegalArgumentException(String.valueOf(element));
+ }
+ }
+
+ /**
+ * Makes the label provider to show time on the generated labels
+ *
+ * @param showTime <code>true</code> in case a time value is to be shown,
+ * otherwise - <code>false</code>
+ */
+ public void setShowTime(boolean showTime) {
+ fShowTime = showTime;
+ fireLabelProviderChanged(new LabelProviderChangedEvent(this));
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTableContentProvider.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTableContentProvider.java
new file mode 100644
index 000000000..93dfb7cee
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTableContentProvider.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.unittest.internal.model.TestCaseElement;
+import org.eclipse.unittest.internal.model.TestSuiteElement;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestRunSession;
+import org.eclipse.unittest.model.ITestSuiteElement;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * A test session table content provider
+ */
+public class TestSessionTableContentProvider implements IStructuredContentProvider {
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ ITestRunSession session = (ITestRunSession) inputElement;
+ List<ITestElement> all = new ArrayList<>();
+ addAll(all, session);
+ return all.toArray();
+ }
+
+ private void addAll(List<ITestElement> all, ITestSuiteElement suite) {
+ for (ITestElement element : suite.getChildren()) {
+ if (element instanceof TestSuiteElement) {
+ if (((TestSuiteElement) element).getSuiteStatus().isErrorOrFailure())
+ all.add(element); // add failed suite to flat list too
+ addAll(all, (TestSuiteElement) element);
+ } else if (element instanceof TestCaseElement) {
+ all.add(element);
+ }
+ }
+ }
+
+ @Override
+ public void dispose() {
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTreeContentProvider.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTreeContentProvider.java
new file mode 100644
index 000000000..239ac6953
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTreeContentProvider.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.unittest.internal.model.TestElement;
+import org.eclipse.unittest.internal.model.TestRunSession;
+import org.eclipse.unittest.internal.model.TestSuiteElement;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * A test session tree content provider
+ */
+public class TestSessionTreeContentProvider implements ITreeContentProvider {
+
+ private static final Object[] NO_CHILDREN = new Object[0];
+
+ @Override
+ public void dispose() {
+ // nothing to dispose
+ }
+
+ @Override
+ public Object[] getChildren(Object parentElement) {
+ if (parentElement instanceof TestSuiteElement) {
+ return ((TestSuiteElement) parentElement).getChildren().toArray();
+ } else {
+ return NO_CHILDREN;
+ }
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ TestRunnerViewPart part = (TestRunnerViewPart) inputElement;
+ TestRunSession session = part.getCurrentTestRunSession();
+ return new Object[] { session.getChildren().size() == 1 ? session.getChildren().get(0) : session };
+ }
+
+ @Override
+ public Object getParent(Object element) {
+ return ((TestElement) element).getParent();
+ }
+
+ @Override
+ public boolean hasChildren(Object element) {
+ if (element instanceof TestSuiteElement) {
+ return !((TestSuiteElement) element).getChildren().isEmpty();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // nothing
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestViewer.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestViewer.java
new file mode 100644
index 000000000..a36384d9a
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestViewer.java
@@ -0,0 +1,871 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.unittest.internal.ui;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.model.Status;
+import org.eclipse.unittest.internal.model.TestCaseElement;
+import org.eclipse.unittest.internal.model.TestElement;
+import org.eclipse.unittest.internal.model.TestRunSession;
+import org.eclipse.unittest.internal.model.TestSuiteElement;
+import org.eclipse.unittest.internal.ui.TestRunnerViewPart.TestResultsLayout;
+import org.eclipse.unittest.model.ITestCaseElement;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestElement.Result;
+import org.eclipse.unittest.model.ITestRunSession;
+import org.eclipse.unittest.model.ITestSuiteElement;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.TableItem;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+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;
+import org.eclipse.ui.part.PageBook;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchManager;
+
+/**
+ * A Test Viewer implementation
+ */
+class TestViewer {
+
+ private final class TestSelectionListener implements ISelectionChangedListener {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ IStructuredSelection selection = (IStructuredSelection) fSelectionProvider.getSelection();
+ TestElement testElement = null;
+ if (selection.size() == 1) {
+ testElement = (TestElement) selection.getFirstElement();
+ }
+ fTestRunnerPart.handleTestSelected(testElement);
+ }
+ }
+
+ private final class TestOpenListener extends SelectionAdapter {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ handleDefaultSelected();
+ }
+ }
+
+ private final class FailuresOnlyFilter extends ViewerFilter {
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ return select(((TestElement) element));
+ }
+
+ public boolean select(TestElement testElement) {
+ Status status = testElement.getStatus();
+ if (status.isErrorOrFailure())
+ return true;
+ else
+ return !fTestRunSession.isRunning() && status == Status.RUNNING; // rerunning
+ }
+ }
+
+ private final class IgnoredOnlyFilter extends ViewerFilter {
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ return select(((TestElement) element));
+ }
+
+ public boolean select(TestElement testElement) {
+ if (hasIgnoredInTestResult(testElement))
+ return true;
+ else
+ return !fTestRunSession.isRunning() && testElement.getStatus() == Status.RUNNING; // rerunning
+ }
+
+ /**
+ * Checks whether a test was skipped i.e. it was ignored (<code>@Ignored</code>)
+ * or had any assumption failure.
+ *
+ * @param testElement the test element (a test suite or a single test case)
+ *
+ * @return <code>true</code> if the test element or any of its children has
+ * {@link Result#IGNORED} test result
+ */
+ private boolean hasIgnoredInTestResult(TestElement testElement) {
+ if (testElement instanceof TestSuiteElement) {
+ List<TestElement> children = ((TestSuiteElement) testElement).getChildren();
+ for (TestElement child : children) {
+ boolean hasIgnoredTestResult = hasIgnoredInTestResult(child);
+ if (hasIgnoredTestResult) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ return testElement.getTestResult(false) == Result.IGNORED;
+ }
+ }
+
+ private static class ReverseList<E> extends AbstractList<E> {
+ private final List<E> fList;
+
+ public ReverseList(List<E> list) {
+ fList = list;
+ }
+
+ @Override
+ public E get(int index) {
+ return fList.get(fList.size() - index - 1);
+ }
+
+ @Override
+ public int size() {
+ return fList.size();
+ }
+ }
+
+ private class ExpandAllAction extends Action {
+ public ExpandAllAction() {
+ setText(Messages.ExpandAllAction_text);
+ setToolTipText(Messages.ExpandAllAction_tooltip);
+ }
+
+ @Override
+ public void run() {
+ fTreeViewer.expandAll();
+ }
+ }
+
+ private class CollapseAllAction extends Action {
+ public CollapseAllAction() {
+ setText(Messages.CollapseAllAction_text);
+ setToolTipText(Messages.CollapseAllAction_tooltip);
+ }
+
+ @Override
+ public void run() {
+ fTreeViewer.collapseAll();
+ }
+ }
+
+ /**
+ * Compares two {@link TestElement}s: - {@link TestSuiteElement}s are placed on
+ * top of {@link TestCaseElement}s - TestElements are alphabetically ordered(by
+ * their names)
+ */
+ private static final ViewerComparator TEST_ELEMENT_ALPHABETIC_ORDER = new ViewerComparator() {
+ @Override
+ public int compare(Viewer viewer, Object e1, Object e2) {
+ // Show test suites on top of test messages
+ int weight1 = (e1 instanceof ITestSuiteElement) ? 0 : 1;
+ int weight2 = (e2 instanceof ITestSuiteElement) ? 0 : 1;
+ if (weight1 != weight2) {
+ return weight1 - weight2;
+ }
+ // Compare by element names
+ return ((TestElement) e1).getTestName().compareTo(((TestElement) e2).getTestName());
+ }
+ };
+
+ private final FailuresOnlyFilter fFailuresOnlyFilter = new FailuresOnlyFilter();
+ private final IgnoredOnlyFilter fIgnoredOnlyFilter = new IgnoredOnlyFilter();
+
+ private final TestRunnerViewPart fTestRunnerPart;
+ private final Clipboard fClipboard;
+
+ private PageBook fViewerbook;
+ private TreeViewer fTreeViewer;
+ private TestSessionTreeContentProvider fTreeContentProvider;
+ private TestSessionLabelProvider fTreeLabelProvider;
+ private TableViewer fTableViewer;
+ private TestSessionLabelProvider fTableLabelProvider;
+ private SelectionProviderMediator fSelectionProvider;
+
+ private TestResultsLayout fLayoutMode;
+ private boolean fTreeHasFilter;
+ private boolean fTableHasFilter;
+
+ private TestRunSession fTestRunSession;
+
+ private boolean fTreeNeedsRefresh;
+ private boolean fTableNeedsRefresh;
+ private HashSet<ITestElement> fNeedUpdate;
+ private ITestCaseElement fAutoScrollTarget;
+
+ private LinkedList<ITestSuiteElement> fAutoClose;
+ private HashSet<ITestSuiteElement> fAutoExpand;
+
+ /**
+ * Constructs a Test Viewer object
+ *
+ * @param parent a parent composite
+ * @param clipboard a {@link Clipboard} instance
+ * @param runner a Test Runner view part
+ */
+ public TestViewer(Composite parent, Clipboard clipboard, TestRunnerViewPart runner) {
+ fTestRunnerPart = runner;
+ fClipboard = clipboard;
+
+ fLayoutMode = TestRunnerViewPart.TestResultsLayout.HIERARCHICAL;
+
+ createTestViewers(parent);
+
+ registerViewersRefresh();
+
+ initContextMenu();
+ }
+
+ private void createTestViewers(Composite parent) {
+ fViewerbook = new PageBook(parent, SWT.NULL);
+
+ fTreeViewer = new TreeViewer(fViewerbook, SWT.V_SCROLL | SWT.SINGLE);
+ fTreeViewer.setUseHashlookup(true);
+ fTreeContentProvider = new TestSessionTreeContentProvider();
+ fTreeViewer.setContentProvider(fTreeContentProvider);
+ fTreeLabelProvider = new TestSessionLabelProvider(fTestRunnerPart,
+ TestRunnerViewPart.TestResultsLayout.HIERARCHICAL);
+// fTreeViewer.setLabelProvider(new ColoringLabelProvider(fTreeLabelProvider));
+ fTreeViewer.setLabelProvider(fTreeLabelProvider);
+
+ fTableViewer = new TableViewer(fViewerbook, SWT.V_SCROLL | SWT.H_SCROLL | SWT.SINGLE);
+ fTableViewer.setUseHashlookup(true);
+ TestSessionTableContentProvider fTableContentProvider = new TestSessionTableContentProvider();
+ fTableViewer.setContentProvider(fTableContentProvider);
+ fTableLabelProvider = new TestSessionLabelProvider(fTestRunnerPart, TestRunnerViewPart.TestResultsLayout.FLAT);
+// fTableViewer.setLabelProvider(new ColoringLabelProvider(fTableLabelProvider));
+ fTableViewer.setLabelProvider(fTableLabelProvider);
+
+ fSelectionProvider = new SelectionProviderMediator(new StructuredViewer[] { fTreeViewer, fTableViewer },
+ fTreeViewer);
+ fSelectionProvider.addSelectionChangedListener(new TestSelectionListener());
+ TestOpenListener testOpenListener = new TestOpenListener();
+ fTreeViewer.getTree().addSelectionListener(testOpenListener);
+ fTableViewer.getTable().addSelectionListener(testOpenListener);
+
+ fTestRunnerPart.getSite().setSelectionProvider(fSelectionProvider);
+
+ fViewerbook.showPage(fTreeViewer.getTree());
+ }
+
+ private void initContextMenu() {
+ MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
+ menuMgr.setRemoveAllWhenShown(true);
+ menuMgr.addMenuListener(this::handleMenuAboutToShow);
+ fTestRunnerPart.getSite().registerContextMenu(menuMgr, fSelectionProvider);
+ Menu menu = menuMgr.createContextMenu(fViewerbook);
+ fTreeViewer.getTree().setMenu(menu);
+ fTableViewer.getTable().setMenu(menu);
+ }
+
+ void handleMenuAboutToShow(IMenuManager manager) {
+ IStructuredSelection selection = (IStructuredSelection) fSelectionProvider.getSelection();
+ if (!selection.isEmpty()) {
+ TestElement testElement = (TestElement) selection.getFirstElement();
+
+ if (testElement instanceof TestSuiteElement) {
+ TestSuiteElement testSuiteElement = (TestSuiteElement) testElement;
+ IAction openTestAction = testSuiteElement.getTestRunSession().getTestViewSupport()
+ .getOpenTestAction(fTestRunnerPart.getSite().getShell(), testSuiteElement);
+ if (openTestAction != null) {
+ manager.add(openTestAction);
+ }
+ manager.add(new Separator());
+ if (!fTestRunnerPart.lastLaunchStillRunning()) {
+ addRerunActions(manager, testSuiteElement);
+ }
+ } else {
+ TestCaseElement testCaseElement = (TestCaseElement) testElement;
+ IAction openTestAction = testElement.getTestRunSession().getTestViewSupport()
+ .getOpenTestAction(fTestRunnerPart.getSite().getShell(), testCaseElement);
+ if (openTestAction != null) {
+ manager.add(openTestAction);
+ }
+ manager.add(new Separator());
+ addRerunActions(manager, testCaseElement);
+ }
+ if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) {
+ manager.add(new Separator());
+ manager.add(new ExpandAllAction());
+ manager.add(new CollapseAllAction());
+ }
+
+ }
+ if (fTestRunSession != null
+ && fTestRunSession.getCurrentFailureCount() + fTestRunSession.getCurrentErrorCount() > 0) {
+ if (fLayoutMode != TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) {
+ manager.add(new Separator());
+ }
+ manager.add(new CopyFailureListAction(fTestRunnerPart, fClipboard));
+ }
+ manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS + "-end")); //$NON-NLS-1$
+ }
+
+ private void addRerunActions(IMenuManager manager, TestElement testCaseElement) {
+ ILaunchConfiguration rerunLaunchConfiguration = testCaseElement.getTestRunSession().getTestViewSupport()
+ .getRerunLaunchConfiguration(Collections.singletonList(testCaseElement));
+ if (rerunLaunchConfiguration == null) {
+ return;
+ }
+ if (fTestRunnerPart.lastLaunchStillRunning()) {
+ manager.add(new RerunAction(rerunLaunchConfiguration, ILaunchManager.RUN_MODE));
+ } else {
+ try {
+ rerunLaunchConfiguration.getType().getSupportedModeCombinations().stream() //
+ .filter(modes -> modes.size() == 1) //
+ .flatMap(Collection::stream) //
+ .forEach(mode -> manager.add(new RerunAction(rerunLaunchConfiguration, mode)));
+ } catch (CoreException e) {
+ UnitTestPlugin.log(e);
+ }
+ }
+ }
+
+ /**
+ * Returns the tree viewer control
+ *
+ * @return tree viewer control object
+ */
+ public Control getTestViewerControl() {
+ return fViewerbook;
+ }
+
+ /**
+ * Registers a given active test session
+ *
+ * @param testRunSession a test session object
+ */
+ public synchronized void registerActiveSession(TestRunSession testRunSession) {
+ fTestRunSession = testRunSession;
+ registerAutoScrollTarget(null);
+ registerViewersRefresh();
+ }
+
+ void handleDefaultSelected() {
+ IStructuredSelection selection = (IStructuredSelection) fSelectionProvider.getSelection();
+ if (selection.size() != 1)
+ return;
+
+ TestElement testElement = (TestElement) selection.getFirstElement();
+ IAction action;
+ if (testElement instanceof ITestSuiteElement) {
+ action = testElement.getTestRunSession().getTestViewSupport()
+ .getOpenTestAction(fTestRunnerPart.getSite().getShell(), (ITestSuiteElement) testElement);
+ } else if (testElement instanceof ITestCaseElement) {
+ action = testElement.getTestRunSession().getTestViewSupport()
+ .getOpenTestAction(fTestRunnerPart.getSite().getShell(), (ITestCaseElement) testElement);
+ } else {
+ throw new IllegalStateException(String.valueOf(testElement));
+ }
+
+ if (action != null && action.isEnabled())
+ action.run();
+ }
+
+ /**
+ * Tunes the label providers to show time on the generated labels
+ *
+ * @param showTime <code>true</code> in case a time value is to be shown,
+ * otherwise - <code>false</code>
+ */
+ public synchronized void setShowTime(boolean showTime) {
+ try {
+ fViewerbook.setRedraw(false);
+ fTreeLabelProvider.setShowTime(showTime);
+ fTableLabelProvider.setShowTime(showTime);
+ } finally {
+ fViewerbook.setRedraw(true);
+ }
+ }
+
+ /**
+ * It makes sense to display either failed or ignored tests, not both together.
+ *
+ * @param failuresOnly whether to show only failed tests
+ * @param ignoredOnly whether to show only skipped tests
+ * @param layoutMode the layout mode
+ */
+ public synchronized void setShowFailuresOrIgnoredOnly(boolean failuresOnly, boolean ignoredOnly,
+ TestResultsLayout layoutMode) {
+ /*
+ * Management of fTreeViewer and fTableViewer
+ * ****************************************** - invisible viewer is updated on
+ * registerViewerUpdate unless its f*NeedsRefresh is true - invisible viewer is
+ * not refreshed upfront - on layout change, new viewer is refreshed if
+ * necessary - filter only applies to "current" layout mode / viewer
+ */
+ try {
+ fViewerbook.setRedraw(false);
+
+ IStructuredSelection selection = null;
+ boolean switchLayout = layoutMode != fLayoutMode;
+ if (switchLayout) {
+ selection = (IStructuredSelection) fSelectionProvider.getSelection();
+ if (layoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) {
+ if (fTreeNeedsRefresh) {
+ clearUpdateAndExpansion();
+ }
+ } else {
+ if (fTableNeedsRefresh) {
+ clearUpdateAndExpansion();
+ }
+ }
+ fLayoutMode = layoutMode;
+ fViewerbook.showPage(getActiveViewer().getControl());
+ }
+ // avoid realizing all TableItems, especially in flat mode!
+ StructuredViewer viewer = getActiveViewer();
+ if (failuresOnly || ignoredOnly) {
+ if (getActiveViewerHasFilter()) {
+ // For simplicity clear both filters (only one of them is used)
+ viewer.removeFilter(fFailuresOnlyFilter);
+ viewer.removeFilter(fIgnoredOnlyFilter);
+ }
+ setActiveViewerHasFilter(true);
+ viewer.setInput(null);
+ // Set either the failures or the skipped tests filter
+ ViewerFilter filter = fFailuresOnlyFilter;
+ if (ignoredOnly == true) {
+ filter = fIgnoredOnlyFilter;
+ }
+ viewer.addFilter(filter);
+ setActiveViewerNeedsRefresh(true);
+
+ } else {
+ if (getActiveViewerHasFilter()) {
+ setActiveViewerNeedsRefresh(true);
+ setActiveViewerHasFilter(false);
+ viewer.setInput(null);
+ viewer.removeFilter(fIgnoredOnlyFilter);
+ viewer.removeFilter(fFailuresOnlyFilter);
+ }
+ }
+ processChangesInUI();
+
+ if (selection != null) {
+ // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=125708
+ // (ITreeSelection not adapted if TreePaths changed):
+ StructuredSelection flatSelection = new StructuredSelection(selection.toList());
+ fSelectionProvider.setSelection(flatSelection, true);
+ }
+
+ } finally {
+ fViewerbook.setRedraw(true);
+ }
+ }
+
+ private boolean getActiveViewerHasFilter() {
+ if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL)
+ return fTreeHasFilter;
+ else
+ return fTableHasFilter;
+ }
+
+ private void setActiveViewerHasFilter(boolean filter) {
+ if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL)
+ fTreeHasFilter = filter;
+ else
+ fTableHasFilter = filter;
+ }
+
+ private StructuredViewer getActiveViewer() {
+ if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL)
+ return fTreeViewer;
+ else
+ return fTableViewer;
+ }
+
+ private boolean getActiveViewerNeedsRefresh() {
+ if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL)
+ return fTreeNeedsRefresh;
+ else
+ return fTableNeedsRefresh;
+ }
+
+ private void setActiveViewerNeedsRefresh(boolean needsRefresh) {
+ if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL)
+ fTreeNeedsRefresh = needsRefresh;
+ else
+ fTableNeedsRefresh = needsRefresh;
+ }
+
+ /**
+ * To be called periodically by the TestRunnerViewPart (in the UI thread).
+ */
+ public void processChangesInUI() {
+ if (fTestRunSession == null) {
+ registerViewersRefresh();
+ fTreeNeedsRefresh = false;
+ fTableNeedsRefresh = false;
+ fTreeViewer.setInput(null);
+ fTableViewer.setInput(null);
+ return;
+ }
+
+ StructuredViewer viewer = getActiveViewer();
+ if (getActiveViewerNeedsRefresh()) {
+ clearUpdateAndExpansion();
+ setActiveViewerNeedsRefresh(false);
+ viewer.setInput(fTestRunnerPart);
+ } else {
+ Object[] toUpdate;
+ synchronized (this) {
+ toUpdate = fNeedUpdate.toArray();
+ fNeedUpdate.clear();
+ }
+ if (!fTreeNeedsRefresh && toUpdate.length > 0) {
+ if (fTreeHasFilter)
+ for (Object element : toUpdate)
+ updateElementInTree((TestElement) element);
+ else {
+ HashSet<Object> toUpdateWithParents = new HashSet<>();
+ toUpdateWithParents.addAll(Arrays.asList(toUpdate));
+ for (Object element : toUpdate) {
+ ITestElement parent = ((ITestElement) element).getParent();
+ while (parent != null) {
+ toUpdateWithParents.add(parent);
+ parent = parent.getParent();
+ }
+ }
+ fTreeViewer.update(toUpdateWithParents.toArray(), null);
+ }
+ }
+ if (!fTableNeedsRefresh && toUpdate.length > 0) {
+ if (fTableHasFilter)
+ for (Object element : toUpdate)
+ updateElementInTable((TestElement) element);
+ else
+ fTableViewer.update(toUpdate, null);
+ }
+ }
+ autoScrollInUI();
+ }
+
+ private void updateElementInTree(final TestElement testElement) {
+ if (isShown(testElement)) {
+ updateShownElementInTree(testElement);
+ } else {
+ TestElement current = testElement;
+ do {
+ if (fTreeViewer.testFindItem(current) != null)
+ fTreeViewer.remove(current);
+ current = current.getParent();
+ } while (!(current instanceof ITestRunSession) && !isShown(current));
+
+ while (current != null && !(current instanceof ITestRunSession)) {
+ fTreeViewer.update(current, null);
+ current = current.getParent();
+ }
+ }
+ }
+
+ private void updateShownElementInTree(ITestElement testElement) {
+ if (testElement == null || testElement instanceof ITestRunSession) // paranoia null check
+ return;
+
+ ITestSuiteElement parent = testElement.getParent();
+ updateShownElementInTree(parent); // make sure parent is shown and up-to-date
+
+ if (fTreeViewer.testFindItem(testElement) == null) {
+ fTreeViewer.add(parent, testElement); // if not yet in tree: add
+ } else {
+ fTreeViewer.update(testElement, null); // if in tree: update
+ }
+ }
+
+ private void updateElementInTable(TestElement element) {
+ if (isShown(element)) {
+ if (fTableViewer.testFindItem(element) == null) {
+ TestElement previous = getNextFailure(element, false);
+ int insertionIndex = -1;
+ if (previous != null) {
+ TableItem item = (TableItem) fTableViewer.testFindItem(previous);
+ if (item != null)
+ insertionIndex = fTableViewer.getTable().indexOf(item);
+ }
+ fTableViewer.insert(element, insertionIndex);
+ } else {
+ fTableViewer.update(element, null);
+ }
+ } else {
+ fTableViewer.remove(element);
+ }
+ }
+
+ private boolean isShown(TestElement current) {
+ return fFailuresOnlyFilter.select(current);
+ }
+
+ private void autoScrollInUI() {
+ if (!fTestRunnerPart.isAutoScroll()) {
+ clearAutoExpand();
+ fAutoClose.clear();
+ return;
+ }
+
+ if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.FLAT) {
+ if (fAutoScrollTarget != null)
+ fTableViewer.reveal(fAutoScrollTarget);
+ return;
+ }
+
+ synchronized (this) {
+ for (ITestSuiteElement suite : fAutoExpand) {
+ fTreeViewer.setExpandedState(suite, true);
+ }
+ clearAutoExpand();
+ }
+
+ ITestCaseElement current = fAutoScrollTarget;
+ fAutoScrollTarget = null;
+
+ ITestSuiteElement parent = current == null ? null : (ITestSuiteElement) fTreeContentProvider.getParent(current);
+ if (fAutoClose.isEmpty() || !fAutoClose.getLast().equals(parent)) {
+ // we're in a new branch, so let's close old OK branches:
+ for (ListIterator<ITestSuiteElement> iter = fAutoClose.listIterator(fAutoClose.size()); iter
+ .hasPrevious();) {
+ ITestSuiteElement previousAutoOpened = iter.previous();
+ if (previousAutoOpened.equals(parent))
+ break;
+
+ if (((TestElement) previousAutoOpened).getStatus() == Status.OK) {
+ // auto-opened the element, and all children are OK -> auto close
+ iter.remove();
+ fTreeViewer.collapseToLevel(previousAutoOpened, AbstractTreeViewer.ALL_LEVELS);
+ }
+ }
+
+ while (parent != null && !fTestRunSession.equals(parent) && fTreeViewer.getExpandedState(parent) == false) {
+ fAutoClose.add(parent); // add to auto-opened elements -> close later if STATUS_OK
+ parent = (ITestSuiteElement) fTreeContentProvider.getParent(parent);
+ }
+ }
+ if (current != null)
+ fTreeViewer.reveal(current);
+ }
+
+ /**
+ * Selects the next failure test element
+ */
+ public void selectFirstFailure() {
+ ITestElement firstFailure = getNextChildFailure(fTestRunSession, true);
+ if (firstFailure != null)
+ getActiveViewer().setSelection(new StructuredSelection(firstFailure), true);
+ }
+
+ /**
+ * Selects a next failure test element
+ *
+ * @param showNext <code>true</code> if a next failed element is to be shown,
+ * otherwise - <code>false</code>
+ */
+ public void selectFailure(boolean showNext) {
+ IStructuredSelection selection = (IStructuredSelection) getActiveViewer().getSelection();
+ TestElement selected = (TestElement) selection.getFirstElement();
+ ITestElement next;
+
+ if (selected == null) {
+ next = getNextChildFailure(fTestRunSession, showNext);
+ } else {
+ next = getNextFailure(selected, showNext);
+ }
+
+ if (next != null)
+ getActiveViewer().setSelection(new StructuredSelection(next), true);
+ }
+
+ private TestElement getNextFailure(TestElement selected, boolean showNext) {
+ if (selected instanceof TestSuiteElement) {
+ TestElement nextChild = getNextChildFailure((TestSuiteElement) selected, showNext);
+ if (nextChild != null)
+ return nextChild;
+ }
+ return getNextFailureSibling(selected, showNext);
+ }
+
+ private TestElement getNextFailureSibling(TestElement current, boolean showNext) {
+ TestSuiteElement parent = current.getParent();
+ if (parent == null)
+ return null;
+
+ List<TestElement> siblings = getSortedChildren(parent);
+ if (!showNext)
+ siblings = new ReverseList<>(siblings);
+
+ int nextIndex = siblings.indexOf(current) + 1;
+ for (int i = nextIndex; i < siblings.size(); i++) {
+ TestElement sibling = siblings.get(i);
+ if (sibling.getStatus().isErrorOrFailure()) {
+ if (sibling instanceof ITestCaseElement) {
+ return sibling;
+ } else {
+ TestSuiteElement testSuiteElement = (TestSuiteElement) sibling;
+ if (testSuiteElement.getChildren().isEmpty()) {
+ return testSuiteElement;
+ }
+ return getNextChildFailure(testSuiteElement, showNext);
+ }
+ }
+ }
+ return getNextFailureSibling(parent, showNext);
+ }
+
+ private TestElement getNextChildFailure(TestSuiteElement root, boolean showNext) {
+ List<TestElement> children = getSortedChildren(root);
+ if (!showNext)
+ children = new ReverseList<>(children);
+ for (TestElement child : children) {
+ if (child.getStatus().isErrorOrFailure()) {
+ if (child instanceof ITestCaseElement) {
+ return child;
+ } else {
+ TestSuiteElement testSuiteElement = (TestSuiteElement) child;
+ if (testSuiteElement.getChildren().isEmpty()) {
+ return testSuiteElement;
+ }
+ return getNextChildFailure(testSuiteElement, showNext);
+ }
+ }
+ }
+ return null;
+ }
+
+ private List<TestElement> getSortedChildren(TestSuiteElement parent) {
+ List<TestElement> siblings = new ArrayList<>(parent.getChildren());
+ ViewerComparator comparator = fTreeViewer.getComparator();
+ if (comparator != null) {
+ siblings.sort((e1, e2) -> comparator.compare(fTreeViewer, e1, e2));
+ }
+ return siblings;
+ }
+
+ /**
+ * Initializes a viewers refresh
+ */
+ public synchronized void registerViewersRefresh() {
+ fTreeNeedsRefresh = true;
+ fTableNeedsRefresh = true;
+ clearUpdateAndExpansion();
+ }
+
+ private void clearUpdateAndExpansion() {
+ fNeedUpdate = new LinkedHashSet<>();
+ fAutoClose = new LinkedList<>();
+ fAutoExpand = new HashSet<>();
+ }
+
+ /**
+ * Registers a test element
+ *
+ * @param testElement the added test
+ */
+ public synchronized void registerTestAdded(ITestElement testElement) {
+ // TODO: performance: would only need to refresh parent of added element
+ fTreeNeedsRefresh = true;
+ fTableNeedsRefresh = true;
+ }
+
+ /**
+ * Initializes an update for a test element
+ *
+ * @param testElement a test element that needs to be updated
+ */
+ public synchronized void registerViewerUpdate(final ITestElement testElement) {
+ fNeedUpdate.add(testElement);
+ }
+
+ private synchronized void clearAutoExpand() {
+ fAutoExpand.clear();
+ }
+
+ /**
+ * Registers an auto-scroll target test case element
+ *
+ * @param testCaseElement a test case element
+ */
+ public void registerAutoScrollTarget(ITestCaseElement testCaseElement) {
+ fAutoScrollTarget = testCaseElement;
+ }
+
+ /**
+ * Registers a failed test element for an auto-scroll
+ *
+ * @param testElement a failed test element
+ */
+ public synchronized void registerFailedForAutoScroll(ITestElement testElement) {
+ ITestSuiteElement parent = (TestSuiteElement) fTreeContentProvider.getParent(testElement);
+ if (parent != null)
+ fAutoExpand.add(parent);
+ }
+
+ /**
+ * Expands the test element tree first level
+ */
+ public void expandFirstLevel() {
+ fTreeViewer.expandToLevel(2);
+ }
+
+ /**
+ * Sets up an alphabetical sort flag
+ *
+ * @param enableAlphabeticalSort <code>true</code> if an alphabetical sort is
+ * enabled, otherwise <code>false</code>
+ */
+ public void setAlphabeticalSort(boolean enableAlphabeticalSort) {
+ fTreeViewer.setComparator(enableAlphabeticalSort ? TEST_ELEMENT_ALPHABETIC_ORDER : null);
+ fTreeViewer.refresh();
+ }
+
+ /**
+ * Indicates if an alphabetical sort is enabled
+ *
+ * @return <code>true</code> if an alphabetical sort is enabled, otherwise
+ * <code>false</code>
+ */
+ public boolean isAlphabeticalSort() {
+ return fTreeViewer.getComparator() == TEST_ELEMENT_ALPHABETIC_ORDER;
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TextualTrace.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TextualTrace.java
new file mode 100644
index 000000000..2d428b1da
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TextualTrace.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/**
+ *
+ */
+package org.eclipse.unittest.internal.ui;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.Collection;
+
+import org.eclipse.core.text.StringMatcher;
+
+/**
+ * A Textual Trace
+ */
+public class TextualTrace {
+ /**
+ * An exception line type
+ */
+ public static final int LINE_TYPE_EXCEPTION = 1;
+
+ /**
+ * An normal line type
+ */
+ public static final int LINE_TYPE_NORMAL = 0;
+
+ /**
+ * An stackframe line type
+ */
+ public static final int LINE_TYPE_STACKFRAME = 2;
+
+ private final String fTrace;
+
+ /**
+ * Constructs a Textual Trace object
+ *
+ * @param trace a trace line
+ * @param filterPatterns a collection of filter string matchers
+ */
+ public TextualTrace(String trace, Collection<StringMatcher> filterPatterns) {
+ super();
+ fTrace = filterStack(trace, filterPatterns);
+ }
+
+ /**
+ * Displays a trace line on a specified display
+ *
+ * @param display a target display
+ * @param maxLabelLength a maximum number of characters to be displayed
+ */
+ public void display(ITraceDisplay display, int maxLabelLength) {
+ StringReader stringReader = new StringReader(fTrace);
+ BufferedReader bufferedReader = new BufferedReader(stringReader);
+ String line;
+
+ try {
+ // first line contains the thrown exception
+ line = readLine(bufferedReader);
+ if (line == null)
+ return;
+
+ displayWrappedLine(display, maxLabelLength, line, LINE_TYPE_EXCEPTION);
+
+ // the stack frames of the trace
+ while ((line = readLine(bufferedReader)) != null) {
+ int type = isAStackFrame(line) ? LINE_TYPE_STACKFRAME : LINE_TYPE_NORMAL;
+ displayWrappedLine(display, maxLabelLength, line, type);
+ }
+ } catch (IOException e) {
+ display.addTraceLine(LINE_TYPE_NORMAL, fTrace);
+ }
+ }
+
+ private void displayWrappedLine(ITraceDisplay display, int maxLabelLength, String line, int type) {
+ final int labelLength = line.length();
+ if (labelLength < maxLabelLength) {
+ display.addTraceLine(type, line);
+ } else {
+ display.addTraceLine(type, line.substring(0, maxLabelLength));
+ int offset = maxLabelLength;
+ while (offset < labelLength) {
+ int nextOffset = Math.min(labelLength, offset + maxLabelLength);
+ display.addTraceLine(LINE_TYPE_NORMAL, line.substring(offset, nextOffset));
+ offset = nextOffset;
+ }
+ }
+ }
+
+ private boolean filterLine(Collection<StringMatcher> patterns, String line) {
+ for (StringMatcher pattern : patterns) {
+ if (pattern.match(line)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private String filterStack(String stackTrace, Collection<StringMatcher> filterPatterns) {
+ if (filterPatterns == null || filterPatterns.isEmpty() || stackTrace == null)
+ return stackTrace;
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ StringReader stringReader = new StringReader(stackTrace);
+ BufferedReader bufferedReader = new BufferedReader(stringReader);
+
+ String line;
+ boolean firstLine = true;
+ try {
+ while ((line = bufferedReader.readLine()) != null) {
+ if (firstLine || !filterLine(filterPatterns, line))
+ printWriter.println(line);
+ firstLine = false;
+ }
+ } catch (IOException e) {
+ return stackTrace; // return the stack unfiltered
+ }
+ return stringWriter.toString();
+ }
+
+ private boolean isAStackFrame(String itemLabel) {
+ // heuristic for detecting a stack frame - works for JDK
+ return itemLabel.startsWith(" at "); //$NON-NLS-1$
+ }
+
+ private String readLine(BufferedReader bufferedReader) throws IOException {
+ String readLine = bufferedReader.readLine();
+ return readLine == null ? null : readLine.replace('\t', ' ');
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UITestRunListener.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UITestRunListener.java
new file mode 100644
index 000000000..26d55526c
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UITestRunListener.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.launcher.TestRunListener;
+import org.eclipse.unittest.model.ITestRunSession;
+
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * This test run listener is the entry point that makes sure the
+ * org.eclipse.unittest plug-in gets loaded when a UnitTest launch configuration
+ * is launched.
+ */
+public class UITestRunListener extends TestRunListener {
+ @Override
+ public void sessionLaunched(ITestRunSession session) {
+ getDisplay().asyncExec(this::showTestRunnerViewPartInActivePage);
+ }
+
+ /**
+ * Creates a Test Runner View Part if it's not yet created and makes it visible
+ * in active page
+ *
+ * @return a {@link TestRunnerViewPart} instance
+ */
+ private TestRunnerViewPart showTestRunnerViewPartInActivePage() {
+ try {
+ // Have to force the creation of view part contents
+ // otherwise the UI will not be updated
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ if (page == null)
+ return null;
+ TestRunnerViewPart view = (TestRunnerViewPart) page.findView(TestRunnerViewPart.NAME);
+ if (view == null) {
+ // create and show the result view if it isn't created yet.
+ return (TestRunnerViewPart) page.showView(TestRunnerViewPart.NAME, null, IWorkbenchPage.VIEW_VISIBLE);
+ } else {
+ page.activate(view);
+ return view;
+ }
+ } catch (PartInitException pie) {
+ UnitTestPlugin.log(pie);
+ return null;
+ }
+ }
+
+ private static Display getDisplay() {
+ Display display = Display.getCurrent();
+ if (display == null) {
+ display = Display.getDefault();
+ }
+ return display;
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestCopyAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestCopyAction.java
new file mode 100644
index 000000000..7acc70088
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestCopyAction.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import org.eclipse.unittest.model.ITestElement;
+
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.SelectionListenerAction;
+
+/**
+ * Copies a test failure stack trace to the clipboard.
+ */
+public class UnitTestCopyAction extends SelectionListenerAction {
+ private FailureTraceUIBlock fView;
+
+ private final Clipboard fClipboard;
+
+ private ITestElement fTestElement;
+
+ /**
+ * Constructs a Unit Test Copy action
+ *
+ * @param view a {@link FailureTraceUIBlock} object
+ * @param clipboard a {@link Clipboard} object
+ */
+ public UnitTestCopyAction(FailureTraceUIBlock view, Clipboard clipboard) {
+ super(Messages.CopyTrace_action_label);
+ Assert.isNotNull(clipboard);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.COPYTRACE_ACTION);
+ fView = view;
+ fClipboard = clipboard;
+ }
+
+ @Override
+ public void run() {
+ String trace = fView.getTrace();
+ String source = null;
+ if (trace != null) {
+ source = convertLineTerminators(trace);
+ } else if (fTestElement != null) {
+ source = fTestElement.getTestName();
+ }
+ if (source == null || source.length() == 0)
+ return;
+
+ TextTransfer plainTextTransfer = TextTransfer.getInstance();
+ try {
+ fClipboard.setContents(new String[] { convertLineTerminators(source) },
+ new Transfer[] { plainTextTransfer });
+ } catch (SWTError e) {
+ if (e.code != DND.ERROR_CANNOT_SET_CLIPBOARD)
+ throw e;
+ if (MessageDialog.openQuestion(fView.getComposite().getShell(), Messages.CopyTraceAction_problem,
+ Messages.CopyTraceAction_clipboard_busy))
+ run();
+ }
+ }
+
+ /**
+ * Handles a test selection
+ *
+ * @param test an {@link ITestElement} object
+ */
+ public void handleTestSelected(ITestElement test) {
+ fTestElement = test;
+ }
+
+ private String convertLineTerminators(String in) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ StringReader stringReader = new StringReader(in);
+ BufferedReader bufferedReader = new BufferedReader(stringReader);
+ String line;
+ try {
+ while ((line = bufferedReader.readLine()) != null) {
+ printWriter.println(line);
+ }
+ } catch (IOException e) {
+ return in; // return the trace unfiltered
+ }
+ return stringWriter.toString();
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestProgressBar.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestProgressBar.java
new file mode 100644
index 000000000..c377589dd
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestProgressBar.java
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * A progress bar with a red/green indication for success or failure.
+ */
+public class UnitTestProgressBar extends Canvas {
+ private static final int DEFAULT_WIDTH = 160;
+ private static final int DEFAULT_HEIGHT = 18;
+
+ private int fCurrentTickCount = 0;
+ private int fMaxTickCount = 0;
+ private int fColorBarWidth = 0;
+ private Color fOKColor;
+ private Color fFailureColor;
+ private Color fStoppedColor;
+ private boolean fError;
+ private boolean fStopped = false;
+
+ /**
+ * Constructs a Unit Test Progress Bar object
+ *
+ * @param parent a parent composite
+ */
+ public UnitTestProgressBar(Composite parent) {
+ super(parent, SWT.NONE);
+
+ addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ fColorBarWidth = scale(fCurrentTickCount);
+ redraw();
+ }
+ });
+ addPaintListener(this::paint);
+ addDisposeListener(event -> {
+ fFailureColor.dispose();
+ fOKColor.dispose();
+ fStoppedColor.dispose();
+ });
+ Display display = parent.getDisplay();
+ fFailureColor = new Color(display, 159, 63, 63);
+ fOKColor = new Color(display, 95, 191, 95);
+ fStoppedColor = new Color(display, 120, 120, 120);
+ }
+
+ /**
+ * Sets a maximum ticks count
+ *
+ * @param max a value of maximum ticks count
+ */
+ public void setMaximum(int max) {
+ fMaxTickCount = max;
+ }
+
+ /**
+ * Resets the progress bar
+ */
+ public void reset() {
+ fError = false;
+ fStopped = false;
+ fCurrentTickCount = 0;
+ fMaxTickCount = 0;
+ fColorBarWidth = 0;
+ redraw();
+ }
+
+ /**
+ * Resets the progress bar using new initial values
+ *
+ * @param hasErrors <code>true</code> if a test has errors, otherwise
+ * <code>false</code>
+ * @param stopped <code>true</code> if a test is stopped, otherwise
+ * <code>false</code>
+ * @param ticksDone a number of ticks done
+ * @param maximum a maximum ticks count
+ */
+ public void reset(boolean hasErrors, boolean stopped, int ticksDone, int maximum) {
+ boolean noChange = fError == hasErrors && fStopped == stopped && fCurrentTickCount == ticksDone
+ && fMaxTickCount == maximum;
+ fError = hasErrors;
+ fStopped = stopped;
+ fCurrentTickCount = ticksDone;
+ fMaxTickCount = maximum;
+ fColorBarWidth = scale(ticksDone);
+ if (!noChange)
+ redraw();
+ }
+
+ private void paintStep(int startX, int endX) {
+ GC gc = new GC(this);
+ setStatusColor(gc);
+ Rectangle rect = getClientArea();
+ startX = Math.max(1, startX);
+ gc.fillRectangle(startX, 1, endX - startX, rect.height - 2);
+ gc.dispose();
+ }
+
+ private void setStatusColor(GC gc) {
+ if (fStopped)
+ gc.setBackground(fStoppedColor);
+ else if (fError)
+ gc.setBackground(fFailureColor);
+ else
+ gc.setBackground(fOKColor);
+ }
+
+ /**
+ * Sets a stopped flag on the progress bar
+ */
+ public void stopped() {
+ fStopped = true;
+ redraw();
+ }
+
+ private int scale(int value) {
+ if (fMaxTickCount > 0) {
+ Rectangle r = getClientArea();
+ if (r.width != 0)
+ return Math.max(0, value * (r.width - 2) / fMaxTickCount);
+ }
+ return value;
+ }
+
+ private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topleft, Color bottomright) {
+ gc.setForeground(topleft);
+ gc.drawLine(x, y, x + w - 1, y);
+ gc.drawLine(x, y, x, y + h - 1);
+
+ gc.setForeground(bottomright);
+ gc.drawLine(x + w, y, x + w, y + h);
+ gc.drawLine(x, y + h, x + w, y + h);
+ }
+
+ private void paint(PaintEvent event) {
+ GC gc = event.gc;
+ Display disp = getDisplay();
+
+ Rectangle rect = getClientArea();
+ gc.fillRectangle(rect);
+ drawBevelRect(gc, rect.x, rect.y, rect.width - 1, rect.height - 1,
+ disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW),
+ disp.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
+
+ setStatusColor(gc);
+ fColorBarWidth = Math.min(rect.width - 2, fColorBarWidth);
+ gc.fillRectangle(1, 1, fColorBarWidth, rect.height - 2);
+ }
+
+ @Override
+ public Point computeSize(int wHint, int hHint, boolean changed) {
+ checkWidget();
+ Point size = new Point(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ if (wHint != SWT.DEFAULT)
+ size.x = wHint;
+ if (hHint != SWT.DEFAULT)
+ size.y = hHint;
+ return size;
+ }
+
+ /**
+ * Steps the progress according to failures count
+ *
+ * @param failures a failures count
+ */
+ public void step(int failures) {
+ fCurrentTickCount++;
+ int x = fColorBarWidth;
+
+ fColorBarWidth = scale(fCurrentTickCount);
+
+ if (!fError && failures > 0) {
+ fError = true;
+ x = 1;
+ }
+ if (fCurrentTickCount == fMaxTickCount)
+ fColorBarWidth = getClientArea().width - 1;
+ paintStep(x, fColorBarWidth);
+ }
+
+ /**
+ * Refreshes the progress bar
+ *
+ * @param hasErrors <code>true</code> if a test has errors, otherwise
+ * <code>false</code>
+ */
+ public void refresh(boolean hasErrors) {
+ fError = hasErrors;
+ redraw();
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestUIPreferencesConstants.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestUIPreferencesConstants.java
new file mode 100644
index 000000000..f1396ea24
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestUIPreferencesConstants.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui;
+
+import org.osgi.service.prefs.BackingStoreException;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+
+/**
+ * Defines constants which are used to refer to values in the plugin's
+ * preference store.
+ */
+public class UnitTestUIPreferencesConstants {
+ /**
+ * Boolean preference controlling whether newly launched Unit tests should be
+ * shown in all Unit Test views (in all windows).
+ */
+ public static final String SHOW_IN_ALL_VIEWS = UnitTestPlugin.PLUGIN_ID + ".show_in_all_views"; //$NON-NLS-1$
+
+ /**
+ * A default value for SHOW_IN_ALL_VIEWS preference
+ */
+ public static final boolean SHOW_IN_ALL_VIEWS_DEFAULT = false; // would need a PreferenceInitializer if this was
+ // changed to true!
+
+ private UnitTestUIPreferencesConstants() {
+ // no instance
+ }
+
+ /**
+ * Returns a value of SHOW_IN_ALL_VIEWS preference
+ *
+ * @return boolean value of SHOW_IN_ALL_VIEWS preference
+ */
+ public static boolean getShowInAllViews() {
+ return Platform.getPreferencesService().getBoolean(UnitTestPlugin.PLUGIN_ID, SHOW_IN_ALL_VIEWS,
+ SHOW_IN_ALL_VIEWS_DEFAULT, null);
+ }
+
+ /**
+ * Sets a value of SHOW_IN_ALL_VIEWS preference
+ *
+ * @param show <code>true</code> if the results view is to be shown in all
+ * views, otherwise <code>false</code>
+ */
+ public static void setShowInAllViews(boolean show) {
+ IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(UnitTestPlugin.PLUGIN_ID);
+ preferences.putBoolean(SHOW_IN_ALL_VIEWS, show);
+ try {
+ preferences.flush();
+ } catch (BackingStoreException e) {
+ UnitTestPlugin.log(e);
+ }
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/History.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/History.java
new file mode 100644
index 000000000..ea41f16db
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/History.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Red Hat Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui.history;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.model.ITestRunSessionListener;
+import org.eclipse.unittest.internal.model.TestRunSession;
+import org.eclipse.unittest.model.ITestRunSession;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * A test run sessions history object
+ */
+public class History implements ITestRunSessionListener {
+
+ private static final String HISTORY_DIR_NAME = "history"; //$NON-NLS-1$
+
+ /**
+ * An instance of test run sessions history object
+ */
+ public static final History INSTANCE = new History();
+
+ private History() {
+ }
+
+ private boolean wasRead = false;
+ private List<HistoryItem> items = new ArrayList<>();
+
+ /**
+ * Creates and returns a directory to store the History information
+ *
+ * @return the file corresponding to History directory
+ * @throws IllegalStateException in case of failed to create or find an existing
+ * directory
+ */
+ public File getDirectory() throws IllegalStateException {
+ File historyDir = UnitTestPlugin.getDefault().getStateLocation().append(HISTORY_DIR_NAME).toFile();
+ if (!historyDir.isDirectory()) {
+ historyDir.mkdir();
+ }
+ return historyDir;
+ }
+
+ /**
+ * Returns a list of history items
+ *
+ * @return a list of history items
+ */
+ public List<HistoryItem> getHistory() {
+ if (!wasRead) {
+ Arrays.stream(getDirectory().listFiles()).map(HistoryItem::new).forEach(items::add);
+ wasRead = true;
+ }
+ return Collections.unmodifiableList(items);
+ }
+
+ /**
+ * Clears the history
+ */
+ public void clear() {
+ for (HistoryItem item : items) {
+ try {
+ item.removeSwapFile();
+ } catch (IOException e) {
+ UnitTestPlugin.log(e);
+ }
+ }
+ items.clear();
+ }
+
+ @Override
+ public void sessionAdded(ITestRunSession testRunSession) {
+ items.add(new HistoryItem((TestRunSession) testRunSession));
+ }
+
+ @Override
+ public void sessionRemoved(ITestRunSession testRunSession) {
+ items.stream().filter(item -> item.getCurrentTestRunSession().filter(testRunSession::equals).isPresent())
+ .forEach(toRemove -> {
+ try {
+ toRemove.removeSwapFile();
+ } catch (IOException e) {
+ UnitTestPlugin.log(e);
+ }
+ });
+ }
+
+ /**
+ * Saves a test run session into history
+ *
+ * @param testRunSession a test run session object
+ */
+ public void watch(TestRunSession testRunSession) {
+ for (HistoryItem item : items) {
+ if (testRunSession == null || item.getCurrentTestRunSession().filter(testRunSession::equals).isEmpty()) {
+ try {
+ item.swapOut();
+ } catch (CoreException e) {
+ UnitTestPlugin.log(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a history item
+ *
+ * @param selected a history item to remove
+ */
+ public void remove(HistoryItem selected) {
+ this.items.remove(selected);
+ try {
+ selected.removeSwapFile();
+ } catch (IOException e) {
+ UnitTestPlugin.log(e);
+ }
+ }
+
+ /**
+ * adds a history item
+ *
+ * @param historyItem history item to add
+ */
+ public void add(HistoryItem historyItem) {
+ items.add(historyItem);
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryDialog.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryDialog.java
new file mode 100644
index 000000000..1bf1a87af
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryDialog.java
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Red Hat Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui.history;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import javax.xml.transform.TransformerFactoryConfigurationError;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.model.TestRunSession;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+
+import org.eclipse.ui.dialogs.SelectionDialog;
+
+/**
+ * A History item selection dialog
+ */
+public class HistoryDialog extends SelectionDialog {
+
+ private static final Comparator<HistoryItem> COMPARING_START_DATE = Comparator.comparing(HistoryItem::getStartDate)
+ .reversed();
+ private Set<TestRunSession> fCurrentlyVisible;
+ private Button fRemoveButton;
+ private Button fExportButton;
+ private TableViewer fTable;
+
+ /**
+ * Constructs a history item selection dialog object
+ *
+ * @param shell a shell object
+ * @param visibleSessions a set of visible {@link TestRunSession} objects
+ */
+ public HistoryDialog(Shell shell, Set<TestRunSession> visibleSessions) {
+ super(shell);
+ fCurrentlyVisible = visibleSessions;
+ setResult(Collections.emptyList());
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ setTitle(Messages.HistoryDialog_title);
+ getShell().setText(Messages.HistoryDialog_title);
+ Composite res = new Composite(parent, SWT.NONE);
+ res.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ res.setLayout(new GridLayout(2, false));
+ this.fTable = createTable(res);
+ fTable.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ createButtons(res);
+ return fTable.getControl();
+ }
+
+ private void createButtons(Composite res) {
+ Composite buttons = new Composite(res, SWT.NONE);
+ buttons.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, false, false));
+ RowLayout rowLayout = new RowLayout(SWT.VERTICAL);
+ rowLayout.fill = true;
+ buttons.setLayout(rowLayout);
+ fRemoveButton = new Button(buttons, SWT.PUSH);
+ fRemoveButton.setText(Messages.HistoryDialog_remove);
+ fRemoveButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
+ for (Object selected : getResult()) {
+ History.INSTANCE.remove((HistoryItem) selected);
+ }
+ fTable.refresh();
+ }));
+ Button importButton = new Button(buttons, SWT.PUSH);
+ importButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
+ FileDialog fileDialog = new FileDialog(getShell());
+ fileDialog.setFilterExtensions(new String[] { "*.xml" }); //$NON-NLS-1$
+ fileDialog.setText(Messages.HistoryDialog_selectImport);
+ String path = fileDialog.open();
+ if (path == null) {
+ return;
+ }
+ Path sourcePath = Path.of(path);
+ Path targetPath = Path.of(History.INSTANCE.getDirectory().getAbsolutePath(),
+ sourcePath.getFileName().toString());
+ try {
+ Files.copy(sourcePath, targetPath);
+ History.INSTANCE.add(new HistoryItem(targetPath.toFile()));
+ } catch (IOException e1) {
+ UnitTestPlugin.log(e1);
+ }
+ fTable.refresh();
+ }));
+ importButton.setText(Messages.HistoryDialog_import);
+ fExportButton = new Button(buttons, SWT.PUSH);
+ fExportButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
+ DirectoryDialog directoryDialog = new DirectoryDialog(getShell());
+ directoryDialog.setText(Messages.HistoryDialog_selectExport);
+ String path = directoryDialog.open();
+ if (path == null) {
+ return;
+ }
+ File directory = new File(path);
+ for (Object object : getResult()) {
+ HistoryItem historyItem = (HistoryItem) object;
+ try {
+ historyItem.storeSessionToFile(new File(directory, historyItem.getFile().getName()));
+ } catch (TransformerFactoryConfigurationError | CoreException e1) {
+ UnitTestPlugin.log(e1);
+ }
+ }
+ fTable.refresh();
+ }));
+ fExportButton.setText(Messages.HistoryDialog_export);
+ updateButtons();
+ }
+
+ private TableViewer createTable(Composite parent) {
+ TableViewer table = new TableViewer(parent);
+ table.setContentProvider(new ArrayContentProvider());
+ table.setComparator(new ViewerComparator() {
+ @Override
+ public int compare(Viewer viewer, Object e1, Object e2) {
+ HistoryItem item1 = (HistoryItem) e1;
+ HistoryItem item2 = (HistoryItem) e2;
+ return COMPARING_START_DATE.compare(item1, item2);
+ }
+ });
+ int fontSize = table.getTable().getFont().getFontData()[0].getHeight();
+ table.getTable().setHeaderVisible(true);
+ TableViewerColumn visibleColumn = new TableViewerColumn(table, SWT.DEFAULT);
+ visibleColumn.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public String getText(Object element) {
+ return ((HistoryItem) element).getCurrentTestRunSession().filter(fCurrentlyVisible::contains)
+ .map(any -> "👁️").orElse(""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ });
+ visibleColumn.getColumn().setWidth(2 * fontSize);
+ TableViewerColumn nameColumn = new TableViewerColumn(table, SWT.DEFAULT);
+ nameColumn.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public String getText(Object element) {
+ return ((HistoryItem) element).getName();
+ }
+ });
+ nameColumn.getColumn().setWidth(20 * fontSize);
+ nameColumn.getColumn().setText(Messages.HistoryDialog_name);
+ TableViewerColumn dateColumn = new TableViewerColumn(table, SWT.DEFAULT);
+ dateColumn.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public String getText(Object element) {
+ Instant startDate = ((HistoryItem) element).getStartDate();
+ return startDate != null
+ ? startDate.atZone(ZoneId.systemDefault()).format(DateTimeFormatter.RFC_1123_DATE_TIME)
+ : ""; //$NON-NLS-1$
+ }
+ });
+ dateColumn.getColumn().setWidth(25 * fontSize);
+ dateColumn.getColumn().setText(Messages.HistoryDialog_date);
+ TableViewerColumn progressColumn = new TableViewerColumn(table, SWT.DEFAULT);
+ progressColumn.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public String getText(Object element) {
+ return ((HistoryItem) element).getCurrentTestRunSession().filter(TestRunSession::isRunning)
+ .map(any -> "🏃").orElse(""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ });
+ progressColumn.getColumn().setWidth(2 * fontSize);
+ progressColumn.getColumn().setText(Messages.HistoryDialog_progress);
+ TableViewerColumn successColumn = new TableViewerColumn(table, SWT.DEFAULT);
+ successColumn.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public String getText(Object element) {
+ int failures = ((HistoryItem) element).getFailureCount();
+ if (failures == 0) {
+ return "✅"; //$NON-NLS-1$
+ }
+ return "❌ " + failures + Messages.HistoryDialog_failures; //$NON-NLS-1$
+ }
+ });
+ successColumn.getColumn().setWidth(15 * fontSize);
+ successColumn.getColumn().setText(Messages.HistoryDialog_result);
+ TableViewerColumn sizeColumn = new TableViewerColumn(table, SWT.DEFAULT);
+ sizeColumn.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public String getText(Object element) {
+ Long size = ((HistoryItem) element).getSizeOnDisk();
+ if (size != null) {
+ return size.toString() + " B"; //$NON-NLS-1$
+ }
+ return Character.toString('?');
+ }
+ });
+ sizeColumn.getColumn().setText(Messages.HistoryDialog_size);
+ sizeColumn.getColumn().setWidth(10 * fontSize);
+ table.setInput(History.INSTANCE.getHistory());
+ table.setSelection(new StructuredSelection(getInitialElementSelections().toArray()));
+ table.addSelectionChangedListener(
+ event -> setSelectionResult(((IStructuredSelection) event.getSelection()).toArray()));
+ return table;
+ }
+
+ @Override
+ protected Button createButton(Composite parent, int id, String label, boolean defaultButton) {
+ return super.createButton(parent, id, id == IDialogConstants.OK_ID ? IDialogConstants.OPEN_LABEL : label,
+ defaultButton);
+ }
+
+ @Override
+ protected void createButtonsForButtonBar(Composite parent) {
+ super.createButtonsForButtonBar(parent);
+ updateButtons();
+ }
+
+ @Override
+ protected void setSelectionResult(Object[] newResult) {
+ super.setSelectionResult(newResult);
+ updateButtons();
+ }
+
+ private void updateButtons() {
+ Object[] selection = getResult();
+ boolean singleItemSelection = selection.length == 1;
+ Stream.of(getButton(IDialogConstants.OK_ID), fRemoveButton, fExportButton) //
+ .filter(Objects::nonNull) //
+ .forEach(button -> button.setEnabled(singleItemSelection));
+ if (singleItemSelection) {
+ HistoryItem item = (HistoryItem) selection[0];
+ boolean isRunning = item.getCurrentTestRunSession().filter(TestRunSession::isRunning).isPresent();
+ fRemoveButton.setEnabled(!isRunning);
+ fExportButton.setEnabled(!isRunning);
+ }
+
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryHandler.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryHandler.java
new file mode 100644
index 000000000..ea36c41de
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryHandler.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Red Hat Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui.history;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.model.TestRunSession;
+import org.eclipse.unittest.internal.ui.TestRunnerViewPart;
+
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.dialogs.SelectionDialog;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * A handler for Show History command
+ */
+public class HistoryHandler extends AbstractHandler {
+
+ /**
+ * An identifier of Show History command
+ */
+ public static final String COMMAND_ID = UnitTestPlugin.PLUGIN_ID + ".history"; //$NON-NLS-1$
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ Shell shell = HandlerUtil.getActiveShell(event);
+ IWorkbenchPage page = HandlerUtil.getActivePart(event).getSite().getPage();
+ Set<TestRunSession> visibleSessions = Arrays.stream(page.getViewReferences()) //
+ .map(ref -> ref.getPart(false)) //
+ .filter(TestRunnerViewPart.class::isInstance) //
+ .map(TestRunnerViewPart.class::cast) //
+ .map(TestRunnerViewPart::getCurrentTestRunSession) //
+ .filter(Objects::nonNull) //
+ .collect(Collectors.toSet());
+ SelectionDialog historyDialog = new HistoryDialog(shell, visibleSessions);
+ historyDialog.setBlockOnOpen(true);
+ if (historyDialog.open() == IDialogConstants.OK_ID) {
+ HistoryItem item = (HistoryItem) historyDialog.getResult()[0];
+ try {
+ TestRunnerViewPart part = findCurrentPartOrOpenNew(HandlerUtil.getActivePart(event));
+ part.setActiveTestRunSession(item.reloadTestRunSession());
+ } catch (CoreException e) {
+ UnitTestPlugin.log(e);
+ }
+ }
+ return null;
+ }
+
+ private TestRunnerViewPart findCurrentPartOrOpenNew(IWorkbenchPart part) throws PartInitException {
+ if (part instanceof TestRunnerViewPart) {
+ return (TestRunnerViewPart) part;
+ }
+ return (TestRunnerViewPart) part.getSite().getPage().showView(TestRunnerViewPart.NAME);
+ }
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryItem.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryItem.java
new file mode 100644
index 000000000..ec541db62
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryItem.java
@@ -0,0 +1,324 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Red Hat Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui.history;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Date;
+import java.util.Optional;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.xml.sax.InputSource;
+
+import org.eclipse.unittest.internal.UnitTestPlugin;
+import org.eclipse.unittest.internal.junitXmlReport.HistoryEntryHandler;
+import org.eclipse.unittest.internal.junitXmlReport.TestRunHandler;
+import org.eclipse.unittest.internal.junitXmlReport.TestRunSessionSerializer;
+import org.eclipse.unittest.internal.model.ITestSessionListener;
+import org.eclipse.unittest.internal.model.ModelMessages;
+import org.eclipse.unittest.internal.model.TestRunSession;
+import org.eclipse.unittest.internal.ui.BasicElementLabels;
+import org.eclipse.unittest.model.ITestCaseElement;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestElement.FailureTrace;
+import org.eclipse.unittest.model.ITestElement.Result;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+
+/**
+ * A history item object
+ */
+public class HistoryItem {
+
+ private File historyFile;
+
+ private TestRunSession session;
+
+ private String name;
+
+ private Instant startTime;
+
+ private int failuresAndErrors;
+
+ /**
+ * Constructs a history item object for a {@link TestRunSession}
+ *
+ * @param session a {@link TestRunSession} object
+ */
+ public HistoryItem(TestRunSession session) {
+ this.session = session;
+ this.name = session.getTestRunName();
+ this.startTime = session.getStartTime();
+ this.failuresAndErrors = session.getCurrentErrorCount() + session.getCurrentFailureCount();
+ session.addTestSessionListener(new ITestSessionListener() {
+ @Override
+ public void testStarted(ITestCaseElement testCaseElement) {
+ // nothing
+ }
+
+ @Override
+ public void testFailed(ITestElement testElement, Result status, FailureTrace trace) {
+ // nothing
+ }
+
+ @Override
+ public void testEnded(ITestCaseElement testCaseElement) {
+ // nothing
+ }
+
+ @Override
+ public void testAdded(ITestElement testElement) {
+ // nothing
+ }
+
+ @Override
+ public void sessionStarted() {
+ getFile(); // Force creating a History File
+ }
+
+ @Override
+ public void sessionCompleted(Duration duration) {
+ try {
+ storeSessionToFile(getFile());
+ } catch (CoreException e) {
+ UnitTestPlugin.log(e);
+ }
+ }
+
+ @Override
+ public void sessionAborted(Duration duration) {
+ sessionCompleted(duration);
+ }
+
+ @Override
+ public void runningBegins() {
+ // nothing
+ }
+ });
+ }
+
+ /**
+ * Constructs a history item object from a file
+ *
+ * @param file a history item file
+ */
+ public HistoryItem(File file) {
+ this.historyFile = file;
+ try {
+ SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+ SAXParser parser = parserFactory.newSAXParser();
+ HistoryEntryHandler handler = new HistoryEntryHandler();
+ parser.parse(getFile(), handler);
+ this.name = handler.getName();
+ this.startTime = handler.getStartTime();
+ this.failuresAndErrors = handler.getFailuresAndErrors();
+ } catch (Exception e) {
+ UnitTestPlugin.log(e);
+ }
+ }
+
+ /**
+ * Reloads a {@link TestRunSession} object
+ *
+ * @return a {@link TestRunSession} object instance
+ * @throws CoreException in case of a problem during the object reading
+ */
+ public TestRunSession reloadTestRunSession() throws CoreException {
+ if (this.session == null && getFile() != null) {
+ try {
+ SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+ SAXParser parser = parserFactory.newSAXParser();
+ TestRunHandler handler = new TestRunHandler(new NullProgressMonitor());
+ parser.parse(getFile(), handler);
+ this.session = handler.getTestRunSession();
+ } catch (Exception e) {
+ throwImportError(getFile(), e);
+ }
+ }
+ return this.session;
+ }
+
+ /**
+ * Returns current {@link TestRunSession} object
+ *
+ * @return a {@link TestRunSession} object
+ */
+ public Optional<TestRunSession> getCurrentTestRunSession() {
+ return Optional.ofNullable(this.session);
+ }
+
+ /**
+ * Removes a swap file for a history item
+ *
+ * @throws IOException in case of I/O failure
+ */
+ public void removeSwapFile() throws IOException {
+ if (historyFile != null && historyFile.exists()) {
+ Files.delete(historyFile.toPath());
+ }
+ }
+
+ /**
+ * Saves a history item into a file
+ *
+ * @param target a target file
+ * @throws TransformerFactoryConfigurationError in case of transformation
+ * operation failure
+ * @throws CoreException in case of storing operation
+ * failure
+ */
+ void storeSessionToFile(File target) throws TransformerFactoryConfigurationError, CoreException {
+ if (this.session == null) {
+ return;
+ }
+ try (FileOutputStream out = new FileOutputStream(target)) {
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ InputSource inputSource = new InputSource();
+ SAXSource source = new SAXSource(new TestRunSessionSerializer(this.session), inputSource);
+ StreamResult result = new StreamResult(out);
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
+ /*
+ * Bug in Xalan: Only indents if proprietary property
+ * org.apache.xalan.templates.OutputProperties.S_KEY_INDENT_AMOUNT is set.
+ *
+ * Bug in Xalan as shipped with J2SE 5.0: Does not read the indent-amount
+ * property at all >:-(.
+ */
+ try {
+ transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2"); //$NON-NLS-1$ //$NON-NLS-2$
+ } catch (IllegalArgumentException e) {
+ // no indentation today...
+ }
+ transformer.transform(source, result);
+ } catch (Exception e) {
+ throwExportError(target, e);
+ }
+ }
+
+ /**
+ * Returns the history item swap file
+ *
+ * @return a history item file
+ */
+ public File getFile() {
+ if (this.historyFile == null) {
+ File historyDir = History.INSTANCE.getDirectory();
+ String isoTime = new SimpleDateFormat("yyyyMMdd-HHmmss.SSS") //$NON-NLS-1$
+ .format(new Date(getStartDate().toEpochMilli()));
+ String swapFileName = session.getTestRunName() + '@' + isoTime + ".xml"; //$NON-NLS-1$
+ this.historyFile = new File(historyDir, swapFileName);
+ }
+
+ return this.historyFile;
+ }
+
+ /**
+ * Stores test session into a swap file
+ *
+ * @throws CoreException in case of a problem
+ */
+ public void swapOut() throws CoreException {
+ if (session != null && session.isStopped()) {
+ storeSessionToFile(getFile());
+ session = null;
+ }
+ }
+
+ /**
+ * Returns a test session name.
+ *
+ * If a test session name is <code>null</code> returns a name of file
+ *
+ * @return a test session name
+ */
+ public String getName() {
+ if (session != null) {
+ return session.getTestRunName();
+ }
+ if (name != null) {
+ return name;
+ }
+ return getFile().getName();
+ }
+
+ /**
+ * Returns a test session start date/time.
+ *
+ * If date/time of a test session cannot be obtained returns a time of last swap
+ * file modification, or "now"
+ *
+ *
+ * @return an {@link Instant} object indicating a test session start date/time
+ */
+ public Instant getStartDate() {
+ if (session != null) {
+ startTime = session.getStartTime();
+ }
+ if (startTime != null) {
+ return startTime;
+ }
+ return Instant.now();
+ }
+
+ /**
+ * Returns a failure count for a test session
+ *
+ * @return a failure count
+ */
+ public int getFailureCount() {
+ if (session != null) {
+ return session.getCurrentErrorCount() + session.getCurrentFailureCount();
+ }
+ return failuresAndErrors;
+ }
+
+ private static void throwExportError(File file, Exception e) throws CoreException {
+ throw new CoreException(
+ new org.eclipse.core.runtime.Status(IStatus.ERROR, UnitTestPlugin.PLUGIN_ID, MessageFormat.format(
+ ModelMessages.UnitTestModel_could_not_write, BasicElementLabels.getPathLabel(file)), e));
+ }
+
+ private static void throwImportError(File file, Exception e) throws CoreException {
+ throw new CoreException(new org.eclipse.core.runtime.Status(IStatus.ERROR, UnitTestPlugin.PLUGIN_ID,
+ MessageFormat.format(ModelMessages.UnitTestModel_could_not_read, BasicElementLabels.getPathLabel(file)),
+ e));
+ }
+
+ /**
+ * Returns a size of a swap file
+ *
+ * @return a size of a swap file
+ */
+ public Long getSizeOnDisk() {
+ File file = getFile();
+ if (file != null && file.isFile()) {
+ return Long.valueOf(file.length());
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/Messages.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/Messages.java
new file mode 100644
index 000000000..73eca9510
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/Messages.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Red Hat Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.unittest.internal.ui.history;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * History messages
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.unittest.internal.ui.history.messages"; //$NON-NLS-1$
+ public static String HistoryDialog_date;
+ public static String HistoryDialog_export;
+ public static String HistoryDialog_failures;
+ public static String HistoryDialog_import;
+ public static String HistoryDialog_name;
+ public static String HistoryDialog_progress;
+ public static String HistoryDialog_remove;
+ public static String HistoryDialog_result;
+ public static String HistoryDialog_selectExport;
+ public static String HistoryDialog_selectImport;
+ public static String HistoryDialog_title;
+ public static String HistoryDialog_size;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/messages.properties b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/messages.properties
new file mode 100644
index 000000000..599521b40
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/messages.properties
@@ -0,0 +1,22 @@
+###############################################################################
+# Copyright (c) 2020 Red Hat, Inc. and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+###############################################################################
+HistoryDialog_date=Date
+HistoryDialog_export=\uD83D\uDCE4 &Export...
+HistoryDialog_failures=\ Failures
+HistoryDialog_import=\uD83D\uDCE5 &Import...
+HistoryDialog_name=Name
+HistoryDialog_progress=Progress
+HistoryDialog_remove=\uD83D\uDDD1\uFE0F &Remove
+HistoryDialog_result=Result
+HistoryDialog_selectExport=Select a directory to export test session report
+HistoryDialog_selectImport=Select test report file to import
+HistoryDialog_title=Select a test session to display
+HistoryDialog_size=Size
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/launcher/ITestRunnerClient.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/launcher/ITestRunnerClient.java
new file mode 100644
index 000000000..61a62c7e3
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/launcher/ITestRunnerClient.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Red Hat Inc. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.launcher;
+
+import org.eclipse.unittest.internal.model.TestRunSession;
+import org.eclipse.unittest.internal.ui.UITestRunListener;
+
+import org.eclipse.debug.core.ILaunch;
+
+/**
+ * An interface to be implemented by a Test Runner Client. Its implementation
+ * should takes care of placing the right listeners to a given
+ * {@link TestRunSession} (usually received in the constructor) and to react to
+ * the various test engine events (can be some notifications via some network,
+ * reading standard output, etc. depending on design of a specified test runner)
+ * by sending notifications to the {@link UITestRunListener}s.
+ */
+public interface ITestRunnerClient {
+
+ /**
+ * Starts monitoring test execution.
+ *
+ * @see #stopMonitoring()
+ */
+ void startMonitoring();
+
+ /**
+ * Requests to stop the tests execution. Usually requested by user; so it should
+ * stop the test runner client (usually calling {@link #stopMonitoring()} and
+ * also related test specific closable objects like an underlying
+ * {@link ILaunch} (unless launch is configured to be kept alive).
+ */
+ void stopTest();
+
+ /**
+ * Stops monitoring and disconnects this test runner client; this is typically
+ * happening when a test run session is marked as terminated.
+ */
+ void stopMonitoring();
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestCaseElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestCaseElement.java
new file mode 100644
index 000000000..4ab28b76d
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestCaseElement.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.model;
+
+/**
+ * Represents a test case element.
+ * <p>
+ * This interface is not intended to be implemented by clients.
+ * </p>
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface ITestCaseElement extends ITestElement {
+
+ /**
+ * Indicates if the test case was ignored
+ *
+ * @return true in case of the test case was ignored, otherwise false
+ */
+ boolean isIgnored();
+
+ /**
+ * Indicates if the test case is dynamic
+ *
+ * @return true in case of dynamic test case element, otherwise false
+ */
+ boolean isDynamicTest();
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestElement.java
new file mode 100644
index 000000000..c59c8b935
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestElement.java
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.model;
+
+import java.time.Duration;
+import java.util.Objects;
+
+import org.eclipse.unittest.internal.model.ProgressState;
+
+/**
+ * Common protocol for test elements. This set consists of
+ * {@link ITestCaseElement}, {@link ITestSuiteElement} and
+ * {@link ITestRunSession}
+ *
+ * <p>
+ * This interface is not intended to be implemented by clients.
+ * </p>
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface ITestElement {
+
+ /**
+ * Result states of a test.
+ */
+ public enum Result {
+ UNDEFINED("Undefined"), //$NON-NLS-1$
+ OK("OK"), //$NON-NLS-1$
+ ERROR("Error"), //$NON-NLS-1$
+ FAILURE("Failure"), //$NON-NLS-1$
+ IGNORED("Ignored"); //$NON-NLS-1$
+
+ private String fName;
+
+ private Result(String name) {
+ fName = name;
+ }
+
+ @Override
+ public String toString() {
+ return fName;
+ }
+ }
+
+ /**
+ * A failure trace of a test.
+ *
+ * This class is not intended to be instantiated or extended by clients.
+ */
+ public static final class FailureTrace {
+ private final String fActual;
+ private final String fExpected;
+ private final String fTrace;
+
+ public FailureTrace(String trace, String expected, String actual) {
+ fActual = actual;
+ fExpected = expected;
+ fTrace = trace;
+ }
+
+ /**
+ * Returns the failure stack trace.
+ *
+ * @return the failure stack trace
+ */
+ public String getTrace() {
+ return fTrace;
+ }
+
+ /**
+ * Returns the expected result or <code>null</code> if the trace is not a
+ * comparison failure.
+ *
+ * @return the expected result or <code>null</code> if the trace is not a
+ * comparison failure.
+ */
+ public String getExpected() {
+ return fExpected;
+ }
+
+ /**
+ * Returns the actual result or <code>null</code> if the trace is not a
+ * comparison failure.
+ *
+ * @return the actual result or <code>null</code> if the trace is not a
+ * comparison failure.
+ */
+ public String getActual() {
+ return fActual;
+ }
+
+ /**
+ * Returns <code>true</code> in case of comparison failure.
+ *
+ * @return <code>true</code> in case of comparison failure, otherwise returns
+ * <code>false</code>
+ */
+ public boolean isComparisonFailure() {
+ return (fExpected != null || fActual != null) && !Objects.equals(fActual, fExpected);
+ }
+ }
+
+ /**
+ * Returns an identifier of the test element
+ *
+ * @return a test element identifier
+ */
+ String getId();
+
+ /**
+ * Returns some runner-specific data, such as complete test description or other
+ * data allowing further operations not covered by the generic test model.
+ *
+ * @return some runner-specific data, such as complete test description or other
+ * data allowing further operations not covered by the generic test
+ * model.
+ */
+ String getData();
+
+ /**
+ * Returns the test run session.
+ *
+ * @return the parent test run session.
+ */
+ ITestRunSession getTestRunSession();
+
+ /**
+ * Returns the estimated total time elapsed while executing this test element.
+ * The total time for a test suite includes the time used for all tests in that
+ * suite. The total time for a test session includes the time used for all tests
+ * in that session.
+ * <p>
+ * <strong>Note:</strong> The elapsed time is only valid for
+ * {@link ProgressState#COMPLETED} test elements.
+ * </p>
+ *
+ * @return total execution duration for the test element, or <code>null</code>
+ * if the state of the element is not {@link ProgressState#COMPLETED}
+ */
+ Duration getDuration();
+
+ /**
+ * Returns the failure trace of this test element or <code>null</code> if the
+ * test has not resulted in an error or failure.
+ *
+ * @return the failure trace of this test or <code>null</code>.
+ */
+ FailureTrace getFailureTrace();
+
+ /**
+ * Returns parent test suite element of this test element
+ *
+ * @return a parent test suite element
+ */
+ ITestSuiteElement getParent();
+
+ /**
+ * Returns the name of the test element
+ *
+ * @return a name of test element
+ */
+ String getTestName();
+
+ /**
+ * Returns the display name of the test. Can be <code>null</code>. In that case,
+ * use {@link ITestElement#getTestName() getTestName()}.
+ *
+ * @return the test display name, can be <code>null</code>
+ */
+ String getDisplayName();
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestRunSession.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestRunSession.java
new file mode 100644
index 000000000..3c47ad701
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestRunSession.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.model;
+
+import java.time.Duration;
+
+import org.eclipse.debug.core.ILaunch;
+
+/**
+ * Represents a test run session.
+ * <p>
+ * This interface is not intended to be implemented by clients.
+ * </p>
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface ITestRunSession extends ITestSuiteElement {
+
+ /**
+ * Returns the {@link ILaunch} from which this test run session has been
+ * started, or <code>null</code> if not available.
+ *
+ * @return the {@link ILaunch} object instance, or <code>null</code> is not
+ * available.
+ */
+ ILaunch getLaunch();
+
+ /**
+ * Returns a test element by its identifier
+ *
+ * @param id a test element identifier
+ * @return a {@link ITestElement} found or <code>null</code>
+ */
+ ITestElement getTestElement(String id);
+
+ /**
+ * Creates a new Test Case and adds it to the model
+ *
+ * @param testId a unique id for the test
+ * @param testName the name of the test
+ * @param parent the parent, can be <code>null</code>
+ * @param displayName the display name of the test
+ * @param data runner specific data
+ * @return the new test case element
+ */
+ ITestCaseElement newTestCase(String testId, String testName, ITestSuiteElement parent, String displayName,
+ String data);
+
+ /**
+ * Creates a new Test Suite and adds it to the model
+ *
+ * @param testId a unique id for the test
+ * @param testName the name of the test
+ * @param testCount the number of tests this suite will run, <code>null</code>
+ * if unknown.
+ * @param parent the parent
+ * @param displayName the display name of the test
+ * @param data runner specific data
+ * @return the new test case element
+ */
+ ITestSuiteElement newTestSuite(String testId, String testName, Integer testCount, ITestSuiteElement parent,
+ String displayName, String data);
+
+ /**
+ * Notifies on a test run ended normally. Individual test success don't matter.
+ * If the test session failed to complete for some reason, use
+ * {@link #notifyTestSessionAborted(Duration, Exception)}.
+ *
+ * @param duration the total elapsed time of the test run, can be
+ * <code>null</code>.
+ * @see #notifyTestSessionAborted(Duration, Exception) notifyTestRunAborted to
+ * use for abnormal termination of the test session.
+ */
+ void notifyTestSessionCompleted(final Duration duration);
+
+ /**
+ * Notifies on a test run aborted, abnormally.
+ *
+ * @param duration duration of the test run session until abortion, can be
+ * <code>null</code>.
+ * @param cause the cause of the abortion, can be shown in log or to user,
+ * can be <code>null</code>.
+ * @see #notifyTestSessionCompleted(Duration) notifyTestRunAborted to use for
+ * normal completion.
+ */
+ void notifyTestSessionAborted(final Duration duration, final Exception cause);
+
+ /**
+ * Notifies on an individual test ended.
+ *
+ * @param test a unique Id identifying the test
+ * @param isIgnored <code>true</code> indicates that the specified test was
+ * ignored, otherwise - <code>false</code>
+ */
+ void notifyTestEnded(ITestElement test, boolean isIgnored);
+
+ /**
+ * Notifies on an individual test started.
+ *
+ * @param test the test
+ */
+ void notifyTestStarted(ITestElement test);
+
+ /**
+ * Notifies on a test run started.
+ *
+ * @param count the number of individual tests that will be run,
+ * <code>null</code> if unknown
+ */
+ void notifyTestSessionStarted(final Integer count);
+
+ /**
+ * Notifies on an individual test failed with a stack trace.
+ *
+ * @param test the test
+ * @param status the outcome of the test; one of
+ * {@link org.eclipse.unittest.model.ITestElement.Result#ERROR}
+ * or
+ * {@link org.eclipse.unittest.model.ITestElement.Result#FAILURE}.
+ * An exception is thrown otherwise
+ * @param isAssumptionFailed indicates that an assumption is failed
+ * @param failureTrace The failure trace
+ * @throws IllegalArgumentException if status doesn't indicate ERROR or FAILURE.
+ */
+ void notifyTestFailed(ITestElement test, Result status, boolean isAssumptionFailed, FailureTrace failureTrace)
+ throws IllegalArgumentException;
+
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestSuiteElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestSuiteElement.java
new file mode 100644
index 000000000..ef544add2
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestSuiteElement.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.model;
+
+import java.util.List;
+
+/**
+ * Represents a test suite element.
+ * <p>
+ * This interface is not intended to be implemented by clients.
+ * </p>
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface ITestSuiteElement extends ITestElement {
+
+ /**
+ * Returns all tests (and test suites) contained in the suite.
+ *
+ * @return returns all tests (and test suites) contained in the suite.
+ */
+ List<? extends ITestElement> getChildren();
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/package.html b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/package.html
new file mode 100644
index 000000000..0115c4c90
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/package.html
@@ -0,0 +1,15 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="IBM">
+ <meta name="GENERATOR" content="Mozilla/4.75 [en] (Windows NT 5.0; U) [Netscape]">
+ <title>Package-level Javadoc</title>
+</head>
+<body>
+Application programming interfaces representing test elements as shown in the Unit Test view.
+<h2>
+Package Specification</h2>
+APIs to access the test elements of the Unit Test view
+</body>
+</html>
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ConfigureViewerSupport.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ConfigureViewerSupport.java
new file mode 100644
index 000000000..db4a2c113
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ConfigureViewerSupport.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Red Hat Inc. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.ui;
+
+import java.util.function.Function;
+
+import org.eclipse.unittest.internal.launcher.UnitTestLaunchConfigurationConstants;
+
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+
+/**
+ * Configures a Launch configuration Working Copy with an identifier of Test
+ * View Support extension
+ */
+public final class ConfigureViewerSupport
+ implements Function<ILaunchConfigurationWorkingCopy, ILaunchConfigurationWorkingCopy> {
+ private final String identifier;
+
+ public ConfigureViewerSupport(String testViewSupportExtensionId) {
+ this.identifier = testViewSupportExtensionId;
+ }
+
+ @Override
+ public ILaunchConfigurationWorkingCopy apply(ILaunchConfigurationWorkingCopy configuration) {
+ if (configuration != null && identifier != null) {
+ configuration.setAttribute(UnitTestLaunchConfigurationConstants.ATTR_UNIT_TEST_VIEW_SUPPORT, identifier);
+ }
+ return configuration;
+ }
+}
diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ITestViewSupport.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ITestViewSupport.java
new file mode 100644
index 000000000..a03075e02
--- /dev/null
+++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ITestViewSupport.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Red Hat Inc. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.unittest.ui;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.unittest.launcher.ITestRunnerClient;
+import org.eclipse.unittest.model.ITestCaseElement;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestRunSession;
+import org.eclipse.unittest.model.ITestSuiteElement;
+
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.text.StringMatcher;
+
+import org.eclipse.jface.action.IAction;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+
+/**
+ * Interface to be implemented by a Test View Support to be returned by
+ * org.org.eclipse.unittest.unittestViewSupport extension.
+ */
+public interface ITestViewSupport {
+ /**
+ * Returns a Test Runner Client.
+ *
+ * @param session the test session. ⚠️ The session may not be fully initialized
+ * at that point, however {@link ITestRunSession#getLaunch()} is
+ * supposed to return the proper launch.
+ *
+ * @return returns a Test Runner Client
+ */
+ ITestRunnerClient newTestRunnerClient(ITestRunSession session);
+
+ /**
+ * Returns filter patterns to exclude lines from stack trace or an error message
+ *
+ * @return filter patterns, matching lines will be hidden in the UI
+ */
+ Collection<StringMatcher> getTraceExclusionFilterPatterns();
+
+ /**
+ * Returns an action to open a specified test elements
+ *
+ * @param shell a parent {@link Shell} instance
+ * @param testCase a test case element
+ * @return an action to open a specified test case element, or <code>null</code>
+ */
+ IAction getOpenTestAction(Shell shell, ITestCaseElement testCase);
+
+ /**
+ * Returns an action to open a specified test suite element
+ *
+ * @param shell a parent {@link Shell} instance
+ * @param testSuite a test suite element
+ * @return an action to open a specified test suite element, or
+ * <code>null</code>
+ */
+ IAction getOpenTestAction(Shell shell, ITestSuiteElement testSuite);
+
+ /**
+ * Returns an action to open a failure trace element
+ *
+ * @param shell a parent {@link Shell} instance
+ * @param failure a test element that is failed
+ * @param traceLine a stack trace or an error message text
+ * @return an action to open a failure trace element, or <code>null</null>
+ */
+ IAction createOpenEditorAction(Shell shell, ITestElement failure, String traceLine);
+
+ /**
+ * Returns an action to copy an existing stack trace/error message into a
+ * console view
+ *
+ * @param failedTest the failed test
+ * @return an {@link Runnable} if it can be created, otherwise -
+ * <code>null</code>
+ */
+ Runnable createShowStackTraceInConsoleViewActionDelegate(ITestElement failedTest);
+
+ /**
+ * Returns a Rerun launch configuration for the given element
+ *
+ * @param testElements the tests to rerun
+ * @return a {@link ILaunchConfiguration}, derived from current test session and
+ * selected element.
+ */
+ ILaunchConfiguration getRerunLaunchConfiguration(List<ITestElement> testElements);
+
+ /**
+ * Returns a Test View Support display name
+ *
+ * @return returns a display name
+ */
+ String getDisplayName();
+}
diff --git a/pom.xml b/pom.xml
index cd956bec6..3c23fdeba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,5 +62,6 @@
<module>org.eclipse.debug.ui</module>
<module>org.eclipse.ui.console</module>
<module>org.eclipse.ui.externaltools</module>
+ <module>org.eclipse.unittest.ui</module>
</modules>
</project>

Back to the top