Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Rubezhny2020-10-22 14:02:35 +0000
committerJeff Johnston2020-11-20 20:59:27 +0000
commit8a8ff4d918b18a04bc249221af3fdebb1d8671da (patch)
tree3ff4fb3c158f9563ac777da39ee54c60db142998
parent39fc1222bee89e490e85bc10a725da72409801ac (diff)
downloadeclipse.jdt.ui-8a8ff4d918b18a04bc249221af3fdebb1d8671da.tar.gz
eclipse.jdt.ui-8a8ff4d918b18a04bc249221af3fdebb1d8671da.tar.xz
eclipse.jdt.ui-8a8ff4d918b18a04bc249221af3fdebb1d8671da.zip
Bug 507626: Debug framework should provide a generic "test report" view
A JUnit test runner client demonstrating the functionality of Unit Test View bundle. See: https://git.eclipse.org/r/c/platform/eclipse.platform.debug/+/171116 for Unit Test bundle proposal Also-By: Mickael Istria <mistria@redhat.com> Also-By: Alexander Kurtakov <akurtako@redhat.com> Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com> Change-Id: If8dd31e6d551c52cf95b5cafad7203db53f16e5d
-rw-r--r--org.eclipse.jdt.core.manipulation/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.jdt.junit.core/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.jdt.junit.runtime/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.jdt.junit/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.jdt.ui.unittest.junit.feature/.project17
-rw-r--r--org.eclipse.jdt.ui.unittest.junit.feature/.settings/org.eclipse.core.resources.prefs2
-rw-r--r--org.eclipse.jdt.ui.unittest.junit.feature/.settings/org.eclipse.core.runtime.prefs2
-rw-r--r--org.eclipse.jdt.ui.unittest.junit.feature/build.properties17
-rw-r--r--org.eclipse.jdt.ui.unittest.junit.feature/feature.properties22
-rw-r--r--org.eclipse.jdt.ui.unittest.junit.feature/feature.xml51
-rw-r--r--org.eclipse.jdt.ui.unittest.junit.feature/pom.xml25
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/.classpath11
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/.project34
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.core.resources.prefs2
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.core.runtime.prefs2
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.jdt.core.prefs538
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.jdt.ui.prefs86
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.pde.api.tools.prefs102
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.pde.prefs36
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF27
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/about.html36
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/build.properties25
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/julaunch.pngbin0 -> 240 bytes
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/julaunch@2x.pngbin0 -> 429 bytes
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/test.pngbin0 -> 384 bytes
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/test@2x.pngbin0 -> 776 bytes
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/plugin.properties28
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/plugin.xml98
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/pom.xml26
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/JUnitTestPlugin.java274
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/internal/launcher/JUnitTabGroup.java57
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/internal/launcher/RemoteTestRunnerClient.java195
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/AdvancedJUnitLaunchConfigurationDelegate.java30
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/IUnitTestHelpContextIds.java48
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationDelegate.java815
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationTab.java1287
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchIncludeExcludeTagsDialog.java216
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchShortcut.java643
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitRemoteTestRunnerClient.java543
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitTestKindUtil.java88
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/Messages.java151
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/Messages.properties103
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/BasicElementLabels.java135
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/IJUnitHelpContextIds.java51
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitMessages.java41
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitMessages.properties24
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitTestViewSupport.java326
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenEditorAction.java181
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenEditorAtLineAction.java63
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenTestAction.java321
-rw-r--r--org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/ShowStackTraceInConsoleViewActionDelegate.java44
-rw-r--r--org.eclipse.jdt.ui/META-INF/MANIFEST.MF8
-rw-r--r--pom.xml4
53 files changed, 6839 insertions, 13 deletions
diff --git a/org.eclipse.jdt.core.manipulation/META-INF/MANIFEST.MF b/org.eclipse.jdt.core.manipulation/META-INF/MANIFEST.MF
index bfded2de20..639de6e5ab 100644
--- a/org.eclipse.jdt.core.manipulation/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.core.manipulation/META-INF/MANIFEST.MF
@@ -49,10 +49,10 @@ Export-Package: org.eclipse.jdt.core.manipulation,
org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types;manipulation=split;mandatory:=manipulation;x-friends:="org.eclipse.jdt.ui",
org.eclipse.jdt.internal.corext.refactoring.util;manipulation=split;mandatory:=manipulation;x-friends:="org.eclipse.jdt.ui",
org.eclipse.jdt.internal.corext.template.java;manipulation=split;mandatory:=manipulation;x-friends:="org.eclipse.jdt.ui,org.eclipse.jdt.debug.ui",
- org.eclipse.jdt.internal.corext.util;manipulation=split;mandatory:=manipulation;x-friends:="org.eclipse.jdt.ui,org.eclipse.jdt.junit",
+ org.eclipse.jdt.internal.corext.util;manipulation=split;mandatory:=manipulation;x-friends:="org.eclipse.jdt.ui,org.eclipse.jdt.junit,org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.internal.jarpackager;x-friends:="org.eclipse.jdt.ui",
org.eclipse.jdt.internal.ui;x-friends:="org.eclipse.jdt.ui",
- org.eclipse.jdt.internal.ui.dialogs;x-friends:="org.eclipse.jdt.ui,org.eclipse.jdt.junit",
+ org.eclipse.jdt.internal.ui.dialogs;x-friends:="org.eclipse.jdt.ui,org.eclipse.jdt.junit,org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.internal.ui.fix;x-friends:="org.eclipse.jdt.ui",
org.eclipse.jdt.internal.ui.javaeditor;x-friends:="org.eclipse.jdt.debug.ui,org.eclipse.jdt.junit,org.eclipse.jdt.ui",
org.eclipse.jdt.internal.ui.preferences;x-friends:="org.eclipse.jdt.ui",
diff --git a/org.eclipse.jdt.junit.core/META-INF/MANIFEST.MF b/org.eclipse.jdt.junit.core/META-INF/MANIFEST.MF
index 6a0d9bf3f6..85557e2626 100644
--- a/org.eclipse.jdt.junit.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.junit.core/META-INF/MANIFEST.MF
@@ -9,11 +9,11 @@ Bundle-ActivationPolicy: lazy
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Export-Package:
- org.eclipse.jdt.internal.junit;x-friends:="org.eclipse.jdt.junit",
+ org.eclipse.jdt.internal.junit;x-friends:="org.eclipse.jdt.junit,org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.internal.junit.buildpath;core="split"; mandatory:="core";x-friends:="org.eclipse.jdt.junit",
- org.eclipse.jdt.internal.junit.launcher;core="split"; mandatory:="core";x-friends:="org.eclipse.jdt.junit",
+ org.eclipse.jdt.internal.junit.launcher;core="split"; mandatory:="core";x-friends:="org.eclipse.jdt.junit,org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.internal.junit.model;x-friends:="org.eclipse.jdt.junit",
- org.eclipse.jdt.internal.junit.util;core="split"; mandatory:="core";x-friends:="org.eclipse.jdt.junit",
+ org.eclipse.jdt.internal.junit.util;core="split"; mandatory:="core";x-friends:="org.eclipse.jdt.junit,org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.junit,
org.eclipse.jdt.junit.launcher;core="split"; mandatory:="core",
org.eclipse.jdt.junit.model
diff --git a/org.eclipse.jdt.junit.runtime/META-INF/MANIFEST.MF b/org.eclipse.jdt.junit.runtime/META-INF/MANIFEST.MF
index 38abd2db9d..d8b690a3b7 100644
--- a/org.eclipse.jdt.junit.runtime/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.junit.runtime/META-INF/MANIFEST.MF
@@ -10,7 +10,8 @@ Export-Package: org.eclipse.jdt.internal.junit.runner;
x-friends:="org.eclipse.jdt.junit.core,
org.eclipse.jdt.junit4.runtime,
org.eclipse.pde.junit.runtime,
- org.eclipse.jdt.junit5.runtime",
+ org.eclipse.jdt.junit5.runtime,
+ org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.internal.junit.runner.junit3;x-friends:="org.eclipse.jdt.junit4.runtime,org.eclipse.jdt.junit5.runtime"
Require-Bundle: org.junit;bundle-version="3.8.2"
Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/org.eclipse.jdt.junit/META-INF/MANIFEST.MF b/org.eclipse.jdt.junit/META-INF/MANIFEST.MF
index ffef4ed0e9..c9ccf784cc 100644
--- a/org.eclipse.jdt.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.junit/META-INF/MANIFEST.MF
@@ -9,10 +9,10 @@ Bundle-ActivationPolicy: lazy
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Export-Package: org.eclipse.jdt.internal.junit.buildpath;x-internal:=true,
- org.eclipse.jdt.internal.junit.launcher;x-internal:=true,
+ org.eclipse.jdt.internal.junit.launcher;x-friends:="org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.internal.junit.refactoring;x-internal:=true,
org.eclipse.jdt.internal.junit.ui;x-internal:=true,
- org.eclipse.jdt.internal.junit.util;x-internal:=true,
+ org.eclipse.jdt.internal.junit.util;x-friends:="org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.internal.junit.wizards;x-internal:=true,
org.eclipse.jdt.junit.launcher,
org.eclipse.jdt.junit.wizards
diff --git a/org.eclipse.jdt.ui.unittest.junit.feature/.project b/org.eclipse.jdt.ui.unittest.junit.feature/.project
new file mode 100644
index 0000000000..be4e235083
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.jdt.ui.unittest.junit.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.jdt.ui.unittest.junit.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jdt.ui.unittest.junit.feature/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..99f26c0203
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit.feature/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jdt.ui.unittest.junit.feature/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jdt.ui.unittest.junit.feature/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..5a0ad22d2a
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit.feature/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jdt.ui.unittest.junit.feature/build.properties b/org.eclipse.jdt.ui.unittest.junit.feature/build.properties
new file mode 100644
index 0000000000..307acb99e9
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit.feature/build.properties
@@ -0,0 +1,17 @@
+###############################################################################
+# 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
+#
+# Contributors:
+# Red Hat Inc. - initial API and implementation
+###############################################################################
+bin.includes = feature.xml,\
+ feature.properties
+src.includes = feature.xml,\
+ feature.properties
diff --git a/org.eclipse.jdt.ui.unittest.junit.feature/feature.properties b/org.eclipse.jdt.ui.unittest.junit.feature/feature.properties
new file mode 100644
index 0000000000..2d32a1d182
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit.feature/feature.properties
@@ -0,0 +1,22 @@
+#################################################################################
+# 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
+#
+# Contributors:
+# Red Hat Inc. - initial API and implementation
+#################################################################################
+featureName=JUnit Test runner client for UnitTest View
+description=JUnit test runner client for UnitTest View
+provider=Eclipse.org
+copyright=\
+Copyright (c) 2020 Red Hat, Inc. and others.\n\
+This program and the accompanying materials\n\
+are made available under the terms of the Eclipse Public License 2.0\n\
+which accompanies this distribution, and is available at\n\
+https://www.eclipse.org/legal/epl-2.0/
diff --git a/org.eclipse.jdt.ui.unittest.junit.feature/feature.xml b/org.eclipse.jdt.ui.unittest.junit.feature/feature.xml
new file mode 100644
index 0000000000..6df5b8bc3b
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit.feature/feature.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="org.eclipse.jdt.ui.unittest.junit.feature"
+ label="%featureName"
+ version="1.0.0.qualifier"
+ provider-name="%provider"
+ license-feature="org.eclipse.license"
+ license-feature-version="0.0.0">
+
+ <description>
+ %description
+ </description>
+
+ <copyright>
+ %copyright
+ </copyright>
+
+ <license url="%licenseURL">
+ %license
+ </license>
+
+ <url>
+ <update label="JDT UI Tools" url="http://www.eclipse.org/jdt/ui/update-site"/>
+ </url>
+
+ <requires>
+ <import plugin="org.eclipse.unittest.ui"/>
+ <import plugin="org.eclipse.jface.text"/>
+ <import plugin="org.eclipse.ui.workbench.texteditor"/>
+ <import plugin="org.eclipse.ui"/>
+ <import plugin="org.eclipse.debug.ui"/>
+ <import plugin="org.eclipse.jdt.core"/>
+ <import plugin="org.eclipse.jdt.ui"/>
+ <import plugin="org.eclipse.core.runtime"/>
+ <import plugin="org.eclipse.core.variables"/>
+ <import plugin="org.eclipse.jdt.debug.ui"/>
+ <import plugin="org.eclipse.jdt.launching"/>
+ <import plugin="org.eclipse.jdt.junit"/>
+ <import plugin="org.eclipse.jdt.junit.core"/>
+ <import plugin="org.eclipse.jdt.junit.runtime"/>
+ <import plugin="org.eclipse.jdt.core.manipulation"/>
+ </requires>
+
+ <plugin
+ id="org.eclipse.jdt.ui.unittest.junit"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/org.eclipse.jdt.ui.unittest.junit.feature/pom.xml b/org.eclipse.jdt.ui.unittest.junit.feature/pom.xml
new file mode 100644
index 0000000000..20672090a3
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit.feature/pom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2017, 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 is available at https://www.eclipse.org/legal/epl-2.0/
+
+ SPDX-License-Identifier: EPL-2.0
+
+ Contributors:
+ Red Hat Incorporated - initial implementation
+-->
+<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.jdt.ui</artifactId>
+ <groupId>eclipse.jdt.ui</groupId>
+ <version>4.18.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.eclipse.jdt.feature</groupId>
+ <artifactId>org.eclipse.jdt.ui.unittest.junit.feature</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>eclipse-feature</packaging>
+</project>
diff --git a/org.eclipse.jdt.ui.unittest.junit/.classpath b/org.eclipse.jdt.ui.unittest.junit/.classpath
new file mode 100644
index 0000000000..4a00becd81
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/.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.jdt.ui.unittest.junit/.project b/org.eclipse.jdt.ui.unittest.junit/.project
new file mode 100644
index 0000000000..b0f3874478
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.jdt.ui.unittest.junit</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.jdt.ui.unittest.junit/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..99f26c0203
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..5a0ad22d2a
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..5ab320324e
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/.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.jdt.ui.unittest.junit/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..724c757e57
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,86 @@
+cleanup_settings_version=2
+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
+org.eclipse.jdt.ui.text.custom_code_templates=
+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=false
+sp_cleanup.remove_unused_imports=false
+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.jdt.ui.unittest.junit/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000000..9d6e2dc796
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/.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.jdt.ui.unittest.junit/.settings/org.eclipse.pde.prefs b/org.eclipse.jdt.ui.unittest.junit/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000000..0eb6933c5c
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/.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=2
+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.jdt.ui.unittest.junit/META-INF/MANIFEST.MF b/org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..1e95d7e0c2
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF
@@ -0,0 +1,27 @@
+Manifest-Version: 1.0
+Automatic-Module-Name: org.eclipse.jdt.ui.unittest.junit
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.jdt.ui.unittest.junit;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin
+Bundle-ActivationPolicy: lazy
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Require-Bundle:
+ org.eclipse.unittest.ui,
+ org.eclipse.jface.text;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui.workbench.texteditor;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.jdt.core;bundle-version="[3.18.0,4.0.0)",
+ org.eclipse.jdt.ui;bundle-version="[3.17.100,4.0.0)",
+ org.eclipse.core.runtime;bundle-version="[3.11.0,4.0.0)",
+ org.eclipse.jdt.launching;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.jdt.debug.ui;bundle-version="[3.3.0,4.0.0)",
+ org.eclipse.jdt.junit.core;bundle-version="[3.10.800,4.0.0]",
+ org.eclipse.jdt.junit.runtime;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.variables;bundle-version="[3.2.200,4.0.0)",
+ org.eclipse.jdt.core.manipulation;bundle-version="1.9.0",
+ org.eclipse.jdt.junit;bundle-version="3.11.0"
+Bundle-RequiredExecutionEnvironment: JavaSE-11
diff --git a/org.eclipse.jdt.ui.unittest.junit/about.html b/org.eclipse.jdt.ui.unittest.junit/about.html
new file mode 100644
index 0000000000..164f781a8f
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/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.jdt.ui.unittest.junit/build.properties b/org.eclipse.jdt.ui.unittest.junit/build.properties
new file mode 100644
index 0000000000..bd95f3e73b
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/build.properties
@@ -0,0 +1,25 @@
+###############################################################################
+# 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
+###############################################################################
+bin.includes = plugin.xml,\
+ about.html,\
+ icons/,\
+ plugin.properties,\
+ .,\
+ META-INF/
+
+source.. = src/
+
+src.includes = about.html
+
+javacWarnings..=-unavoidableGenericProblems
diff --git a/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/julaunch.png b/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/julaunch.png
new file mode 100644
index 0000000000..ede46fbac3
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/julaunch.png
Binary files differ
diff --git a/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/julaunch@2x.png b/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/julaunch@2x.png
new file mode 100644
index 0000000000..e9e9b1d691
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/julaunch@2x.png
Binary files differ
diff --git a/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/test.png b/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/test.png
new file mode 100644
index 0000000000..c95f6b58bc
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/test.png
Binary files differ
diff --git a/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/test@2x.png b/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/test@2x.png
new file mode 100644
index 0000000000..c404f65077
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/icons/full/obj16/test@2x.png
Binary files differ
diff --git a/org.eclipse.jdt.ui.unittest.junit/plugin.properties b/org.eclipse.jdt.ui.unittest.junit/plugin.properties
new file mode 100644
index 0000000000..56732b2c6a
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/plugin.properties
@@ -0,0 +1,28 @@
+#################################################################################
+# 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
+#
+# Contributors:
+# Red Hat Inc. - initial API and implementation
+#################################################################################
+pluginName=Java Development Tools Unit Test support
+providerName=Eclipse.org
+
+JUnitLaunchDelegate.name=Eclipse JUnit Unit Test Launcher
+JUnitLaunchDelegate.description=The Eclipse JUnit Unit Test Launcher supports running and debugging Unit tests and test suites.
+
+RunJUnitLaunchShortcut.description=Runs a set of Unit tests
+DebugJUnitLaunchShortcut.description=Debugs a set of Unit tests
+
+Launch.label= JUnit Test (generic Test view)
+
+UnitTestShortcut.label= JUnit Test (generic Test view)
+
+JUnitTabGroupDescription.debug=Create a configuration that will launch a Unit test in debug mode.
+JUnitTabGroupDescription.run=Create a configuration that will launch a Unit test.
diff --git a/org.eclipse.jdt.ui.unittest.junit/plugin.xml b/org.eclipse.jdt.ui.unittest.junit/plugin.xml
new file mode 100644
index 0000000000..0d53b7c63e
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/plugin.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTypeImages">
+ <launchConfigurationTypeImage
+ icon="$nl$/icons/full/obj16/julaunch.png"
+ configTypeID="org.eclipse.jdt.ui.unittest.junit.launchConfiguration"
+ id="org.eclipse.unittest.launchimage">
+ </launchConfigurationTypeImage>
+ </extension>
+
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTabGroups">
+ <launchConfigurationTabGroup
+ type="org.eclipse.jdt.ui.unittest.junit.launchConfiguration"
+ helpContextId="org.eclipse.jdt.ui.unittest.junit_tab_group"
+ class="org.eclipse.jdt.ui.unittest.junit.internal.launcher.JUnitTabGroup"
+ id="org.eclipse.jdt.junit.launchConfigurationTabGroup.junit">
+ <launchMode
+ perspective="org.eclipse.debug.ui.DebugPerspective"
+ description="%JUnitTabGroupDescription.debug"
+ mode="debug">
+ </launchMode>
+ <launchMode
+ description="%JUnitTabGroupDescription.run"
+ mode="run">
+ </launchMode>
+ </launchConfigurationTabGroup>
+ </extension>
+
+ <extension
+ point="org.eclipse.debug.core.launchConfigurationTypes">
+ <launchConfigurationType
+ name="%Launch.label"
+ delegateDescription="%JUnitLaunchDelegate.description"
+ delegateName="%JUnitLaunchDelegate.name"
+ allowCommandLine="true"
+ delegate="org.eclipse.jdt.ui.unittest.junit.launcher.AdvancedJUnitLaunchConfigurationDelegate"
+ modes="run, debug"
+ id="org.eclipse.jdt.ui.unittest.junit.launchConfiguration"
+ sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"
+ sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer"
+ migrationDelegate="org.eclipse.jdt.internal.junit.launcher.JUnitMigrationDelegate">
+ </launchConfigurationType>
+ </extension>
+
+ <extension
+ point="org.eclipse.debug.ui.launchShortcuts">
+ <shortcut
+ label="%UnitTestShortcut.label"
+ icon="$nl$/icons/full/obj16/julaunch.png"
+ helpContextId="org.eclipse.jdt.junit.launch_shortcut"
+ class="org.eclipse.jdt.ui.unittest.junit.launcher.JUnitLaunchShortcut"
+ modes="run, debug"
+ id="org.eclipse.jdt.ui.unittest.junit.junitShortcut">
+ <contextualLaunch>
+ <enablement>
+ <with variable="selection">
+ <count value="1"/>
+ <iterate>
+ <adapt type="org.eclipse.jdt.core.IJavaElement">
+ <and>
+ <test property="org.eclipse.jdt.core.isInJavaProject"/>
+ <or>
+ <test property="org.eclipse.jdt.core.hasTypeOnClasspath" value="junit.framework.Test"/>
+ <test property="org.eclipse.jdt.core.hasTypeOnClasspath" value="org.junit.platform.commons.annotation.Testable"/>
+ </or>
+ <test property="org.eclipse.jdt.junit.canLaunchAsJUnit" forcePluginActivation="true"/>
+ </and>
+ </adapt>
+ </iterate>
+ </with>
+ </enablement>
+ </contextualLaunch>
+ <configurationType
+ id="org.eclipse.jdt.ui.unittest.junit.launchConfiguration">
+ </configurationType>
+ <description
+ description="%DebugJUnitLaunchShortcut.description"
+ mode="debug">
+ </description>
+ <description
+ description="%RunJUnitLaunchShortcut.description"
+ mode="run">
+ </description>
+ </shortcut>
+ </extension>
+
+ <extension
+ point="org.eclipse.unittest.ui.unittestViewSupport">
+ <viewSupport
+ class="org.eclipse.jdt.ui.unittest.junit.ui.JUnitTestViewSupport"
+ id="org.eclipse.jdt.ui.unittest.junit">
+ </viewSupport>
+ </extension>
+</plugin>
diff --git a/org.eclipse.jdt.ui.unittest.junit/pom.xml b/org.eclipse.jdt.ui.unittest.junit/pom.xml
new file mode 100644
index 0000000000..293dd06da7
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/pom.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2012, 2020 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
+-->
+<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.jdt.ui</artifactId>
+ <groupId>eclipse.jdt.ui</groupId>
+ <version>4.18.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.eclipse.jdt</groupId>
+ <artifactId>org.eclipse.jdt.ui.unittest.junit</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>eclipse-plugin</packaging>
+ <properties>
+ <skipAPIAnalysis>true</skipAPIAnalysis>
+ </properties>
+</project>
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/JUnitTestPlugin.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/JUnitTestPlugin.java
new file mode 100644
index 0000000000..7d7cf50988
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/JUnitTestPlugin.java
@@ -0,0 +1,274 @@
+/*******************************************************************************
+ * 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.jdt.ui.unittest.junit;
+
+import java.util.Arrays;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import org.eclipse.jdt.core.IAnnotation;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMemberValuePair;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+
+import org.eclipse.jdt.internal.junit.launcher.ITestKind;
+import org.eclipse.jdt.internal.junit.util.CoreTestSearchEngine;
+
+/**
+ * The plug-in runtime class for the JUnit core plug-in.
+ */
+@SuppressWarnings("deprecation")
+public class JUnitTestPlugin extends AbstractUIPlugin {
+
+ /**
+ * The single instance of this plug-in runtime class.
+ */
+ private static JUnitTestPlugin fgPlugin = null;
+
+ public static final String PLUGIN_ID = "org.eclipse.jdt.ui.unittest.junit"; //$NON-NLS-1$
+
+ public static final String UNIT_TEST_VIEW_SUPPORT_ID = "org.eclipse.jdt.ui.unittest.junit"; //$NON-NLS-1$
+
+ public enum JUnitVersion {
+ JUNIT3("org.eclipse.jdt.junit.loader.junit3"), //$NON-NLS-1$
+ JUNIT4("org.eclipse.jdt.junit.loader.junit4"), //$NON-NLS-1$
+ JUNIT5("org.eclipse.jdt.junit.loader.junit5"); //$NON-NLS-1$
+
+ public final String junitTestKindId;
+
+ private JUnitVersion(String junitTestKindId) {
+ this.junitTestKindId = junitTestKindId;
+ }
+
+ public static JUnitVersion fromJUnitTestKindId(String junitTestKindId) {
+ return Arrays.stream(values()).filter(version -> version.junitTestKindId.equals(junitTestKindId)).findAny()
+ .orElse(null);
+ }
+
+ public ITestKind getJUnitTestKind() {
+ return org.eclipse.jdt.internal.junit.launcher.TestKindRegistry.getDefault().getKind(junitTestKindId);
+ }
+ }
+
+ private BundleContext fBundleContext;
+
+ public JUnitTestPlugin() {
+ fgPlugin = this;
+ }
+
+ public static JUnitTestPlugin getDefault() {
+ return fgPlugin;
+ }
+
+ public static String getPluginId() {
+ return PLUGIN_ID;
+ }
+
+ public static void log(Throwable e) {
+ log(new Status(IStatus.ERROR, getPluginId(), IStatus.ERROR, "Error", e)); //$NON-NLS-1$
+ }
+
+ public static void log(IStatus status) {
+ getDefault().getLog().log(status);
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ fBundleContext = context;
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ super.stop(context);
+ fBundleContext = null;
+ }
+
+ /**
+ * Returns a service with the specified name or <code>null</code> if none.
+ *
+ * @param serviceName name of service
+ * @return service object or <code>null</code> if none
+ */
+ public Object getService(String serviceName) {
+ ServiceReference<?> reference = fBundleContext.getServiceReference(serviceName);
+ if (reference == null)
+ return null;
+ return fBundleContext.getService(reference);
+ }
+
+ public static JUnitVersion getJUnitVersion(IJavaElement element) {
+ if (element != null) {
+ IJavaProject project = element.getJavaProject();
+ if (CoreTestSearchEngine.is50OrHigher(project)) {
+ if (CoreTestSearchEngine.is18OrHigher(project)) {
+ if (isRunWithJUnitPlatform(element)) {
+ return JUnitVersion.JUNIT4;
+ }
+ if (CoreTestSearchEngine.hasJUnit5TestAnnotation(project)) {
+ return JUnitVersion.JUNIT5;
+ }
+ }
+ if (CoreTestSearchEngine.hasJUnit4TestAnnotation(project)) {
+ return JUnitVersion.JUNIT4;
+ }
+ }
+ }
+ return JUnitVersion.JUNIT3;
+ }
+
+ /**
+ * @param element the element
+ * @return <code>true</code> if the element is a test class annotated with
+ * <code>@RunWith(JUnitPlatform.class)</code>
+ */
+ public static boolean isRunWithJUnitPlatform(IJavaElement element) {
+ if (element instanceof ICompilationUnit) {
+ element = ((ICompilationUnit) element).findPrimaryType();
+ }
+ if (element instanceof IType) {
+ IType type = (IType) element;
+ try {
+ IAnnotation runWithAnnotation = type.getAnnotation("RunWith"); //$NON-NLS-1$
+ if (!runWithAnnotation.exists()) {
+ runWithAnnotation = type.getAnnotation("org.junit.runner.RunWith"); //$NON-NLS-1$
+ }
+ if (runWithAnnotation.exists()) {
+ IMemberValuePair[] memberValuePairs = runWithAnnotation.getMemberValuePairs();
+ for (IMemberValuePair memberValuePair : memberValuePairs) {
+ if (memberValuePair.getMemberName().equals("value") //$NON-NLS-1$
+ && memberValuePair.getValue().equals("JUnitPlatform")) { //$NON-NLS-1$
+ return true;
+ }
+ }
+ }
+ } catch (JavaModelException e) {
+ // ignore
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the bundle for a given bundle name, regardless whether the bundle is
+ * resolved or not.
+ *
+ * @param bundleName the bundle name
+ * @return the bundle
+ */
+ public Bundle getBundle(String bundleName) {
+ Bundle[] bundles = getBundles(bundleName, null);
+ if (bundles != null && bundles.length > 0)
+ return bundles[0];
+ return null;
+ }
+
+ /**
+ * Returns the bundles for a given bundle name,
+ *
+ * @param bundleName the bundle name
+ * @param version the version of the bundle
+ * @return the bundles of the given name
+ */
+ public Bundle[] getBundles(String bundleName, String version) {
+ Bundle[] bundles = Platform.getBundles(bundleName, version);
+ if (bundles != null)
+ return bundles;
+
+ // Accessing unresolved bundle
+ ServiceReference<PackageAdmin> serviceRef = fBundleContext.getServiceReference(PackageAdmin.class);
+ PackageAdmin admin = fBundleContext.getService(serviceRef);
+ bundles = admin.getBundles(bundleName, version);
+ if (bundles != null && bundles.length > 0)
+ return bundles;
+ return null;
+ }
+
+ /**
+ * Returns this workbench window's shell.
+ *
+ * @return the shell containing this window's controls or <code>null</code> if
+ * the shell has not been created yet or if the window has been closed
+ */
+ public static Shell getActiveWorkbenchShell() {
+ IWorkbenchWindow workBenchWindow = getActiveWorkbenchWindow();
+ if (workBenchWindow == null)
+ return null;
+ return workBenchWindow.getShell();
+ }
+
+ /**
+ * Returns the active workbench window
+ *
+ * @return the active workbench window, or <code>null</code> if there is no
+ * active workbench window or if called from a non-UI thread
+ */
+ public static IWorkbenchWindow getActiveWorkbenchWindow() {
+ if (fgPlugin == null)
+ return null;
+ IWorkbench workBench = PlatformUI.getWorkbench();
+ if (workBench == null)
+ return null;
+ return workBench.getActiveWorkbenchWindow();
+ }
+
+ /**
+ * Returns the currently active page for this workbench window.
+ *
+ * @return the active page, or <code>null</code> if none
+ */
+ public static IWorkbenchPage getActivePage() {
+ IWorkbenchWindow activeWorkbenchWindow = getActiveWorkbenchWindow();
+ if (activeWorkbenchWindow == null)
+ return null;
+ return activeWorkbenchWindow.getActivePage();
+ }
+
+ /**
+ * Activates UnitTestBundle. Eclipse uses lazy bundle loading by default, which
+ * means a bundle will not be loaded in many cases until some of its class is
+ * used. This method allows the clients to instantiate the Unit Test bundle in
+ * order to make it setup its launch listeners that are used to create and
+ * activate Unit Test View. The Unit Test client bundles must call this method
+ * before a Unit Test launch is created (preferably right before creation of the
+ * launch in order to not make Eclipse to load the Unit Test bundle when it is
+ * not really required), To load the Unit Test bundle the clients, for example,
+ * might call this method inside their
+ * 'ILaunchConfigurationDelegate2.getLaunch(ILaunchConfiguration, String)'
+ * method of their launch configuration implementation.
+ */
+ public static void activateUnitTestCoreBundle() {
+ Assert.isNotNull(Platform.getBundle("org.eclipse.unittest.ui")); //$NON-NLS-1$
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/internal/launcher/JUnitTabGroup.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/internal/launcher/JUnitTabGroup.java
new file mode 100644
index 0000000000..d1bd6ea9a3
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/internal/launcher/JUnitTabGroup.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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
+ * David Saff (saff@mit.edu) - bug 102632: [JUnit] Support for JUnit 4.
+ *******************************************************************************/
+package org.eclipse.jdt.ui.unittest.junit.internal.launcher;
+
+import org.eclipse.jdt.debug.ui.launchConfigurations.JavaArgumentsTab;
+import org.eclipse.jdt.debug.ui.launchConfigurations.JavaClasspathTab;
+import org.eclipse.jdt.debug.ui.launchConfigurations.JavaDependenciesTab;
+import org.eclipse.jdt.debug.ui.launchConfigurations.JavaJRETab;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.CommonTab;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.EnvironmentTab;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+import org.eclipse.debug.ui.sourcelookup.SourceLookupTab;
+
+import org.eclipse.jdt.internal.junit.launcher.AssertionVMArg;
+
+import org.eclipse.jdt.launching.JavaRuntime;
+
+import org.eclipse.jdt.ui.unittest.junit.launcher.JUnitLaunchConfigurationTab;
+
+public class JUnitTabGroup extends AbstractLaunchConfigurationTabGroup {
+
+ @Override
+ public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
+ ILaunchConfiguration configuration = DebugUITools.getLaunchConfiguration(dialog);
+ boolean isModularConfiguration = configuration != null && JavaRuntime.isModularConfiguration(configuration);
+ ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] { new JUnitLaunchConfigurationTab(),
+ new JavaArgumentsTab(), new JavaJRETab(true),
+ isModularConfiguration ? new JavaDependenciesTab() : new JavaClasspathTab(), new SourceLookupTab(),
+ new EnvironmentTab(), new CommonTab() };
+ setTabs(tabs);
+ }
+
+ @Override
+ public void setDefaults(ILaunchConfigurationWorkingCopy config) {
+ super.setDefaults(config);
+ AssertionVMArg.setArgDefault(config);
+ }
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/internal/launcher/RemoteTestRunnerClient.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/internal/launcher/RemoteTestRunnerClient.java
new file mode 100644
index 0000000000..ac13ffe200
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/internal/launcher/RemoteTestRunnerClient.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * 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.jdt.ui.unittest.junit.internal.launcher;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.PushbackReader;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.unittest.launcher.ITestRunnerClient;
+import org.eclipse.unittest.model.ITestRunSession;
+
+import org.eclipse.core.runtime.ISafeRunnable;
+
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin;
+
+/**
+ * The client side of the RemoteTestRunner. Handles the marshaling of the
+ * different messages.
+ */
+public abstract class RemoteTestRunnerClient implements ITestRunnerClient {
+
+ public abstract class ListenerSafeRunnable implements ISafeRunnable {
+ @Override
+ public void handleException(Throwable exception) {
+ JUnitTestPlugin.log(exception);
+ }
+ }
+
+ /**
+ * A simple state machine to process requests from the RemoteTestRunner
+ */
+ abstract class ProcessingState {
+ abstract ProcessingState readMessage(String message);
+ }
+
+ private int fPort = -1;
+ protected String fLastLineDelimiter;
+ protected InputStream fInputStream;
+ protected PrintWriter fWriter;
+ protected PushbackReader fPushbackReader;
+
+ /**
+ * The protocol version
+ */
+ protected String fVersion;
+
+ protected boolean fDebug = false;
+ protected final ITestRunSession fTestRunSession;
+
+ /**
+ * Reads the message stream from the RemoteTestRunner
+ */
+ private class ServerConnection extends Thread {
+ int fServerPort;
+
+ public ServerConnection(int port) {
+ super("ServerConnection"); //$NON-NLS-1$
+ fServerPort = port;
+ }
+
+ @Override
+ public void run() {
+ try {
+ if (fDebug)
+ System.out.println("Creating server socket " + fServerPort); //$NON-NLS-1$
+ fServerSocket = new ServerSocket(fServerPort);
+ fSocket = fServerSocket.accept();
+ fPushbackReader = new PushbackReader(
+ new BufferedReader(new InputStreamReader(fSocket.getInputStream(), StandardCharsets.UTF_8)));
+ fWriter = new PrintWriter(new OutputStreamWriter(fSocket.getOutputStream(), StandardCharsets.UTF_8),
+ true);
+ String message;
+ while (fPushbackReader != null && (message = readMessage(fPushbackReader)) != null)
+ receiveMessage(message);
+ } catch (SocketException e) {
+ fTestRunSession.notifyTestSessionAborted(null, e);
+ } catch (IOException e) {
+ JUnitTestPlugin.log(e);
+ // fall through
+ }
+ shutDown();
+ }
+ }
+
+ protected RemoteTestRunnerClient(int port, ITestRunSession testRunSession) {
+ this.fPort = port;
+ fTestRunSession = testRunSession;
+ }
+
+ @Override
+ public void startMonitoring() {
+ ServerConnection connection = new ServerConnection(fPort);
+ connection.start();
+ }
+
+ public abstract void receiveMessage(String message);
+
+ public synchronized void shutDown() {
+ if (fDebug)
+ System.out.println("shutdown " + fPort); //$NON-NLS-1$
+
+ if (fWriter != null) {
+ fWriter.close();
+ fWriter = null;
+ }
+ try {
+ if (fPushbackReader != null) {
+ fPushbackReader.close();
+ fPushbackReader = null;
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ if (fDebug)
+ System.out.println("shutdown"); //$NON-NLS-1$
+
+ try {
+ if (fSocket != null) {
+ fSocket.close();
+ fSocket = null;
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ try {
+ if (fServerSocket != null) {
+ fServerSocket.close();
+ fServerSocket = null;
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ private String readMessage(PushbackReader in) throws IOException {
+ StringBuilder buf = new StringBuilder(128);
+ int ch;
+ while ((ch = in.read()) != -1) {
+ switch (ch) {
+ case '\n':
+ fLastLineDelimiter = "\n"; //$NON-NLS-1$
+ return buf.toString();
+ case '\r':
+ ch = in.read();
+ if (ch == '\n') {
+ fLastLineDelimiter = "\r\n"; //$NON-NLS-1$
+ } else {
+ in.unread(ch);
+ fLastLineDelimiter = "\r"; //$NON-NLS-1$
+ }
+ return buf.toString();
+ default:
+ buf.append((char) ch);
+ break;
+ }
+ }
+ fLastLineDelimiter = null;
+ if (buf.length() == 0)
+ return null;
+ return buf.toString();
+ }
+
+ /**
+ * The server socket
+ */
+ protected ServerSocket fServerSocket;
+ protected Socket fSocket;
+
+ @Override
+ public synchronized void stopMonitoring() {
+ if (fServerSocket != null && !fServerSocket.isClosed() && fSocket == null) {
+ shutDown(); // will throw a SocketException in Threads that wait in ServerSocket#accept()
+ }
+ }
+
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/AdvancedJUnitLaunchConfigurationDelegate.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/AdvancedJUnitLaunchConfigurationDelegate.java
new file mode 100644
index 0000000000..38d7f0244c
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/AdvancedJUnitLaunchConfigurationDelegate.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2017, 2020 Igor Fedorenko.
+ *
+ * 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:
+ * Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.ui.unittest.junit.launcher;
+
+/**
+ * Launch configuration delegate for a JUnit test as a Java application with
+ * advanced source lookup support.
+ *
+ * @provisional This is part of work in progress and can be changed, moved or
+ * removed without notice
+ */
+public class AdvancedJUnitLaunchConfigurationDelegate extends JUnitLaunchConfigurationDelegate {
+
+ public AdvancedJUnitLaunchConfigurationDelegate() {
+ super();
+ allowAdvancedSourcelookup();
+ }
+
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/IUnitTestHelpContextIds.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/IUnitTestHelpContextIds.java
new file mode 100644
index 0000000000..12afc185ba
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/IUnitTestHelpContextIds.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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.jdt.ui.unittest.junit.launcher;
+
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin;
+
+/**
+ * Help context ids for the JUnit UI.
+ */
+public interface IUnitTestHelpContextIds {
+ String PREFIX = JUnitTestPlugin.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$
+
+ // Wizard pages
+ String NEW_TESTCASE_WIZARD_PAGE = PREFIX + "new_testcase_wizard_page_context"; //$NON-NLS-1$
+ String NEW_TESTCASE_WIZARD_PAGE2 = PREFIX + "new_testcase_wizard_page2_context"; //$NON-NLS-1$
+ String NEW_TESTSUITE_WIZARD_PAGE = PREFIX + "new_testsuite_wizard_page2_context"; //$NON-NLS-1$
+ String LAUNCH_CONFIGURATION_DIALOG_JUNIT_MAIN_TAB = PREFIX + "launch_configuration_dialog_junit_main_tab"; //$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.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationDelegate.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationDelegate.java
new file mode 100644
index 0000000000..ea343f266a
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationDelegate.java
@@ -0,0 +1,815 @@
+/*******************************************************************************
+ * 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
+ * David Saff (saff@mit.edu) - bug 102632: [JUnit] Support for JUnit 4.
+ * Robert Konigsberg <konigsberg@google.com> - [JUnit] Leverage AbstractJavaLaunchConfigurationDelegate.getMainTypeName in JUnitLaunchConfigurationDelegate - https://bugs.eclipse.org/bugs/show_bug.cgi?id=280114
+ * Achim Demelt <a.demelt@exxcellent.de> - [junit] Separate UI from non-UI code - https://bugs.eclipse.org/bugs/show_bug.cgi?id=278844
+ *******************************************************************************/
+package org.eclipse.jdt.ui.unittest.junit.launcher;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+import org.eclipse.core.variables.VariablesPlugin;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.URIUtil;
+
+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.jdt.core.IClasspathAttribute;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMember;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+
+import org.eclipse.jdt.internal.junit.JUnitCorePlugin;
+import org.eclipse.jdt.internal.junit.launcher.ITestKind;
+import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
+import org.eclipse.jdt.internal.junit.launcher.JUnitRuntimeClasspathEntry;
+import org.eclipse.jdt.internal.junit.util.CoreTestSearchEngine;
+import org.eclipse.jdt.internal.junit.util.IJUnitStatusConstants;
+
+import org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate;
+import org.eclipse.jdt.launching.ExecutionArguments;
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+import org.eclipse.jdt.launching.IVMRunner;
+import org.eclipse.jdt.launching.JavaRuntime;
+import org.eclipse.jdt.launching.SocketUtil;
+import org.eclipse.jdt.launching.VMRunnerConfiguration;
+
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin;
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin.JUnitVersion;
+
+/**
+ * Launch configuration delegate for a JUnit test as a Java application.
+ *
+ * <p>
+ * Clients can instantiate and extend this class.
+ * </p>
+ */
+public class JUnitLaunchConfigurationDelegate extends AbstractJavaLaunchConfigurationDelegate {
+
+ // This needs to be differnet from JunitLaunchConfigurationConstants.ATTR_PORT
+ // or the "legacy" view handles it first
+ public static final String ATTR_PORT = JUnitTestPlugin.PLUGIN_ID + ".PORT"; //$NON-NLS-1$
+
+ private boolean fKeepAlive = false;
+ private int fPort;
+ private IJavaElement[] fTestElements;
+
+ private static final String DEFAULT = "<default>"; //$NON-NLS-1$
+
+ @Override
+ public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {
+ JUnitTestPlugin.activateUnitTestCoreBundle();
+ return super.getLaunch(configuration, mode);
+ }
+
+ @Override
+ public String showCommandLine(ILaunchConfiguration configuration, String mode, ILaunch launch,
+ IProgressMonitor monitor) throws CoreException {
+ if (monitor == null) {
+ monitor = new NullProgressMonitor();
+ }
+ try {
+ VMRunnerConfiguration runConfig = getVMRunnerConfiguration(configuration, launch, mode, monitor);
+ if (runConfig == null) {
+ return ""; //$NON-NLS-1$
+ }
+ IVMRunner runner = getVMRunner(configuration, mode);
+ String cmdLine = runner.showCommandLine(runConfig, launch, monitor);
+
+ // check for cancellation
+ if (monitor.isCanceled()) {
+ return ""; //$NON-NLS-1$
+ }
+ return cmdLine;
+ } finally {
+ monitor.done();
+ }
+ }
+
+ private VMRunnerConfiguration getVMRunnerConfiguration(ILaunchConfiguration configuration, ILaunch launch,
+ String mode, IProgressMonitor monitor) throws CoreException {
+ VMRunnerConfiguration runConfig = null;
+ monitor.beginTask(MessageFormat.format("{0}...", configuration.getName()), 5); //$NON-NLS-1$
+ // check for cancellation
+ if (monitor.isCanceled()) {
+ return null;
+ }
+
+ try {
+ if (mode.equals(JUnitLaunchConfigurationConstants.MODE_RUN_QUIETLY_MODE)) {
+ launch.setAttribute(JUnitLaunchConfigurationConstants.ATTR_NO_DISPLAY, "true"); //$NON-NLS-1$
+ mode = ILaunchManager.RUN_MODE;
+ }
+
+ monitor.subTask(Messages.JUnitLaunchConfigurationDelegate_verifying_attriburtes_description);
+
+ try {
+ preLaunchCheck(configuration, launch, SubMonitor.convert(monitor, 2));
+ } catch (CoreException e) {
+ if (e.getStatus().getSeverity() == IStatus.CANCEL) {
+ monitor.setCanceled(true);
+ return null;
+ }
+ throw e;
+ }
+ // check for cancellation
+ if (monitor.isCanceled()) {
+ return null;
+ }
+
+ fKeepAlive = mode.equals(ILaunchManager.DEBUG_MODE)
+ && configuration.getAttribute(JUnitLaunchConfigurationConstants.ATTR_KEEPRUNNING, false);
+ fPort = evaluatePort();
+ launch.setAttribute(ATTR_PORT, String.valueOf(fPort));
+
+ JUnitVersion junitVersion = getJUnitVersion(configuration);
+ IJavaProject javaProject = getJavaProject(configuration);
+ if (junitVersion == JUnitVersion.JUNIT3 || junitVersion == JUnitVersion.JUNIT4) {
+ fTestElements = evaluateTests(configuration, SubMonitor.convert(monitor, 1));
+ } else {
+ IJavaElement testTarget = getTestTarget(configuration, javaProject);
+ if (testTarget instanceof IPackageFragment || testTarget instanceof IPackageFragmentRoot
+ || testTarget instanceof IJavaProject) {
+ fTestElements = new IJavaElement[] { testTarget };
+ } else {
+ fTestElements = evaluateTests(configuration, SubMonitor.convert(monitor, 1));
+ }
+ }
+
+ String mainTypeName = verifyMainTypeName(configuration);
+
+ File workingDir = verifyWorkingDirectory(configuration);
+ String workingDirName = null;
+ if (workingDir != null) {
+ workingDirName = workingDir.getAbsolutePath();
+ }
+
+ // Environment variables
+ String[] envp = getEnvironment(configuration);
+
+ ArrayList<String> vmArguments = new ArrayList<>();
+ ArrayList<String> programArguments = new ArrayList<>();
+ collectExecutionArguments(configuration, vmArguments, programArguments);
+ vmArguments.addAll(Arrays.asList(DebugPlugin.parseArguments(getVMArguments(configuration, mode))));
+ if (JavaRuntime.isModularProject(javaProject)) {
+ vmArguments.add("--add-modules=ALL-MODULE-PATH"); //$NON-NLS-1$
+ }
+
+ // VM-specific attributes
+
+ Map<String, Object> vmAttributesMap = getVMSpecificAttributesMap(configuration);
+
+ // Classpath and modulepath
+ String[][] classpathAndModulepath = getClasspathAndModulepath(configuration);
+ String[] classpath = classpathAndModulepath[0];
+ String[] modulepath = classpathAndModulepath[1];
+
+ if (junitVersion == JUnitVersion.JUNIT5) {
+ if (!configuration.getAttribute(
+ JUnitLaunchConfigurationConstants.ATTR_DONT_ADD_MISSING_JUNIT5_DEPENDENCY, false)) {
+ if (!Arrays.stream(classpath).anyMatch(
+ s -> s.contains("junit-platform-launcher") || s.contains("org.junit.platform.launcher"))) { //$NON-NLS-1$ //$NON-NLS-2$
+ try {
+ JUnitRuntimeClasspathEntry x = new JUnitRuntimeClasspathEntry("org.junit.platform.launcher", //$NON-NLS-1$
+ null);
+ String entryString = new ClasspathLocalizer(Platform.inDevelopmentMode()).entryString(x);
+ int length = classpath.length;
+ System.arraycopy(classpath, 0, classpath = new String[length + 1], 0, length);
+ classpath[length] = entryString;
+ } catch (IOException | URISyntaxException e) {
+ throw new CoreException(
+ new Status(IStatus.ERROR, JUnitTestPlugin.PLUGIN_ID, IStatus.ERROR, "", e)); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ // Create VM config
+ runConfig = new VMRunnerConfiguration(mainTypeName, classpath);
+ runConfig.setVMArguments(vmArguments.toArray(new String[vmArguments.size()]));
+ runConfig.setProgramArguments(programArguments.toArray(new String[programArguments.size()]));
+ runConfig.setEnvironment(envp);
+ runConfig.setWorkingDirectory(workingDirName);
+ runConfig.setVMSpecificAttributesMap(vmAttributesMap);
+ runConfig.setPreviewEnabled(supportsPreviewFeatures(configuration));
+
+ if (!JavaRuntime.isModularConfiguration(configuration)) {
+ // Bootpath
+ runConfig.setBootClassPath(getBootpath(configuration));
+ } else {
+ // module path
+ runConfig.setModulepath(modulepath);
+ if (!configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_MODULE_CLI_OPTIONS,
+ true)) {
+ runConfig.setOverrideDependencies(
+ configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_MODULE_CLI_OPTIONS, "")); //$NON-NLS-1$
+ } else {
+ runConfig.setOverrideDependencies(getModuleCLIOptions(configuration));
+ }
+ }
+
+ // check for cancellation
+ if (monitor.isCanceled()) {
+ return null;
+ }
+ } finally {
+ // done the verification phase
+ monitor.worked(1);
+ }
+ return runConfig;
+ }
+
+ static JUnitVersion getJUnitVersion(ILaunchConfiguration configuration) {
+ try {
+ String junitTestKindId = configuration.getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND,
+ ""); //$NON-NLS-1$
+ if (!junitTestKindId.isEmpty()) {
+ return JUnitVersion.fromJUnitTestKindId(junitTestKindId);
+ }
+ } catch (Exception ex) {
+ JUnitTestPlugin.log(ex);
+ }
+ IJavaProject javaProject = JUnitLaunchConfigurationConstants.getJavaProject(configuration);
+ if (javaProject != null) {
+ return JUnitTestPlugin.getJUnitVersion(javaProject);
+ }
+ return JUnitVersion.JUNIT3;
+ }
+
+ @Override
+ public synchronized void launch(ILaunchConfiguration configuration, String mode, ILaunch launch,
+ IProgressMonitor monitor) throws CoreException {
+ if (monitor == null) {
+ monitor = new NullProgressMonitor();
+ }
+
+ try {
+
+ VMRunnerConfiguration runConfig = getVMRunnerConfiguration(configuration, launch, mode, monitor);
+ if (monitor.isCanceled() || runConfig == null) {
+ return;
+ }
+ IVMRunner runner = getVMRunner(configuration, mode);
+ monitor.subTask(Messages.JUnitLaunchConfigurationDelegate_create_source_locator_description);
+ // set the default source locator if required
+ setDefaultSourceLocator(launch, configuration);
+ monitor.worked(1);
+
+ // Launch the configuration - 1 unit of work
+ runner.run(runConfig, launch, monitor);
+ } finally {
+ fTestElements = null;
+ monitor.done();
+ }
+ }
+
+ private int evaluatePort() throws CoreException {
+ int port = SocketUtil.findFreePort();
+ if (port == -1) {
+ abort(Messages.JUnitLaunchConfigurationDelegate_error_no_socket, null,
+ IJavaLaunchConfigurationConstants.ERR_NO_SOCKET_AVAILABLE);
+ }
+ return port;
+ }
+
+ /**
+ * Performs a check on the launch configuration's attributes. If an attribute
+ * contains an invalid value, a {@link CoreException} with the error is thrown.
+ *
+ * @param configuration the launch configuration to verify
+ * @param launch the launch to verify
+ * @param monitor the progress monitor to use
+ * @throws CoreException an exception is thrown when the verification fails
+ */
+ protected void preLaunchCheck(ILaunchConfiguration configuration, ILaunch launch, IProgressMonitor monitor)
+ throws CoreException {
+ try {
+ IJavaProject javaProject = getJavaProject(configuration);
+ if ((javaProject == null) || !javaProject.exists()) {
+ abort(Messages.JUnitLaunchConfigurationDelegate_error_invalidproject, null,
+ IJavaLaunchConfigurationConstants.ERR_NOT_A_JAVA_PROJECT);
+ }
+ JUnitVersion junitVersion = getJUnitVersion(configuration);
+ if (junitVersion != JUnitVersion.JUNIT5 && !CoreTestSearchEngine.hasTestCaseType(javaProject)) {
+ abort(Messages.JUnitLaunchConfigurationDelegate_error_junitnotonpath, null,
+ IJUnitStatusConstants.ERR_JUNIT_NOT_ON_PATH);
+ }
+ if (junitVersion == JUnitVersion.JUNIT4 && !CoreTestSearchEngine.hasJUnit4TestAnnotation(javaProject)) {
+ abort(Messages.JUnitLaunchConfigurationDelegate_error_junit4notonpath, null,
+ IJUnitStatusConstants.ERR_JUNIT_NOT_ON_PATH);
+ }
+ if (junitVersion == JUnitVersion.JUNIT4 && !CoreTestSearchEngine.hasJUnit5TestAnnotation(javaProject)) {
+ String msg = MessageFormat.format(Messages.JUnitLaunchConfigurationDelegate_error_junit5notonpath,
+ JUnitCorePlugin.JUNIT5_TESTABLE_ANNOTATION_NAME);
+ abort(msg, null, IJUnitStatusConstants.ERR_JUNIT_NOT_ON_PATH);
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ @Override
+ public String getJavaProjectName(ILaunchConfiguration configuration) throws CoreException {
+ return configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null);
+ }
+
+ @Override
+ public String getMainTypeName(ILaunchConfiguration configuration) throws CoreException {
+ String mainType = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME,
+ (String) null);
+ if (mainType == null) {
+ return null;
+ }
+ return VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(mainType);
+ }
+
+ @Override
+ public String verifyMainTypeName(ILaunchConfiguration configuration) throws CoreException {
+ return "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner"; //$NON-NLS-1$
+ }
+
+ /**
+ * Evaluates all test elements selected by the given launch configuration. The
+ * elements are of type {@link IType} or {@link IMethod}. At the moment it is
+ * only possible to run a single method or a set of types, but not mixed or more
+ * than one method at a time.
+ *
+ * @param configuration the launch configuration to inspect
+ * @param monitor the progress monitor
+ * @return returns all types or methods that should be ran
+ * @throws CoreException an exception is thrown when the search for tests failed
+ */
+ protected IMember[] evaluateTests(ILaunchConfiguration configuration, IProgressMonitor monitor)
+ throws CoreException {
+ IJavaProject javaProject = getJavaProject(configuration);
+
+ IJavaElement testTarget = getTestTarget(configuration, javaProject);
+ String testMethodName = configuration.getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_NAME, ""); //$NON-NLS-1$
+ if (testMethodName.length() > 0) {
+ if (testTarget instanceof IType) {
+ // If parameters exist, testMethodName is followed by a comma-separated list of
+ // fully qualified parameter type names in parentheses.
+ // The testMethodName is required in this format by #collectExecutionArguments,
+ // hence it will be used as it is with the handle-only method IType#getMethod
+ // here.
+ return new IMember[] { ((IType) testTarget).getMethod(testMethodName, new String[0]) };
+ }
+ }
+ HashSet<IType> result = new HashSet<>();
+ org.eclipse.jdt.internal.junit.launcher.ITestKind junitTestKind = getJUnitVersion(configuration)
+ .getJUnitTestKind();
+ junitTestKind.getFinder().findTestsInContainer(testTarget, result, monitor);
+ if (result.isEmpty()) {
+ String msg = MessageFormat.format(Messages.JUnitLaunchConfigurationDelegate_error_notests_kind,
+ junitTestKind.getDisplayName());
+ abort(msg, null, IJavaLaunchConfigurationConstants.ERR_UNSPECIFIED_MAIN_TYPE);
+ }
+ return result.toArray(new IMember[result.size()]);
+ }
+
+ /**
+ * Collects all VM and program arguments. Implementors can modify and add
+ * arguments.
+ *
+ * @param configuration the configuration to collect the arguments for
+ * @param vmArguments a {@link List} of {@link String} representing the
+ * resulting VM arguments
+ * @param programArguments a {@link List} of {@link String} representing the
+ * resulting program arguments
+ * @exception CoreException if unable to collect the execution arguments
+ */
+ protected void collectExecutionArguments(ILaunchConfiguration configuration, List<String> vmArguments,
+ List<String> programArguments) throws CoreException {
+
+ // add program & VM arguments provided by getProgramArguments and getVMArguments
+ String pgmArgs = getProgramArguments(configuration);
+ String vmArgs = getVMArguments(configuration);
+ ExecutionArguments execArgs = new ExecutionArguments(vmArgs, pgmArgs);
+ vmArguments.addAll(Arrays.asList(execArgs.getVMArgumentsArray()));
+ programArguments.addAll(Arrays.asList(execArgs.getProgramArgumentsArray()));
+
+ boolean isModularProject = JavaRuntime.isModularProject(getJavaProject(configuration));
+ String addOpensTargets;
+ if (isModularProject) {
+ if (getJUnitVersion(configuration) == JUnitVersion.JUNIT5) {
+ if (isOnModulePath(getJavaProject(configuration), "org.junit.jupiter.api.Test")) { //$NON-NLS-1$
+ addOpensTargets = "org.junit.platform.commons,ALL-UNNAMED"; //$NON-NLS-1$
+ } else {
+ addOpensTargets = "ALL-UNNAMED"; //$NON-NLS-1$
+ }
+ } else {
+ if (isOnModulePath(getJavaProject(configuration), "junit.framework.TestCase")) { //$NON-NLS-1$
+ addOpensTargets = "junit,ALL-UNNAMED"; //$NON-NLS-1$
+ } else {
+ addOpensTargets = "ALL-UNNAMED"; //$NON-NLS-1$
+ }
+ }
+ } else {
+ addOpensTargets = null;
+ }
+ List<String> addOpensVmArgs = new ArrayList<>();
+
+ /*
+ * The "-version" "3" arguments don't make sense and should eventually be
+ * removed. But we keep them for now, since users may want to run with older
+ * releases of org.eclipse.jdt.junit[4].runtime, where this is still read by
+ * org.eclipse.jdt.internal.junit.runner.RemoteTestRunner#defaultInit(String[])
+ * and used in
+ * org.eclipse.jdt.internal.junit.runner.DefaultClassifier#isComparisonFailure(
+ * Throwable). The JUnit4 equivalent of the latter method is already
+ * version-agnostic:
+ * org.eclipse.jdt.internal.junit4.runner.JUnit4TestListener#testFailure(
+ * Failure, boolean)
+ */
+ programArguments.add("-version"); //$NON-NLS-1$
+ programArguments.add("3"); //$NON-NLS-1$
+
+ programArguments.add("-port"); //$NON-NLS-1$
+ programArguments.add(String.valueOf(fPort));
+
+ if (fKeepAlive)
+ programArguments.add(0, "-keepalive"); //$NON-NLS-1$
+
+ ITestKind testRunnerKind = getJUnitVersion(configuration).getJUnitTestKind();
+
+ programArguments.add("-testLoaderClass"); //$NON-NLS-1$
+ programArguments.add(testRunnerKind.getLoaderClassName());
+ programArguments.add("-loaderpluginname"); //$NON-NLS-1$
+ programArguments.add(testRunnerKind.getLoaderPluginId());
+
+ // Enable Debugging mode:
+ // programArguments.add("-debugging"); //$NON-NLS-1$
+
+ IJavaElement[] testElements = fTestElements;
+
+ if (testElements.length == 1) { // a test name was specified just run the single test, or a test container was
+ // specified
+ IJavaElement testElement = testElements[0];
+ if (testElement instanceof IMethod) {
+ IMethod method = (IMethod) testElement;
+ programArguments.add("-test"); //$NON-NLS-1$
+ programArguments.add(method.getDeclaringType().getFullyQualifiedName() + ':' + method.getElementName());
+ collectAddOpensVmArgs(addOpensTargets, addOpensVmArgs, method, configuration);
+ } else if (testElement instanceof IType) {
+ IType type = (IType) testElement;
+ programArguments.add("-classNames"); //$NON-NLS-1$
+ programArguments.add(type.getFullyQualifiedName());
+ collectAddOpensVmArgs(addOpensTargets, addOpensVmArgs, type, configuration);
+ } else if (testElement instanceof IPackageFragment || testElement instanceof IPackageFragmentRoot
+ || testElement instanceof IJavaProject) {
+ Set<String> pkgNames = new HashSet<>();
+ String fileName = createPackageNamesFile(testElement, testRunnerKind, pkgNames);
+ programArguments.add("-packageNameFile"); //$NON-NLS-1$
+ programArguments.add(fileName);
+ for (String pkgName : pkgNames) {
+ if (!DEFAULT.equals(pkgName)) { // skip --add-opens for default package
+ collectAddOpensVmArgs(addOpensTargets, addOpensVmArgs, pkgName, configuration);
+ }
+ }
+ } else {
+ abort(Messages.JUnitLaunchConfigurationDelegate_error_wrong_input, null,
+ IJavaLaunchConfigurationConstants.ERR_UNSPECIFIED_MAIN_TYPE);
+ }
+ } else if (testElements.length > 1) {
+ String fileName = createTestNamesFile(testElements);
+ programArguments.add("-testNameFile"); //$NON-NLS-1$
+ programArguments.add(fileName);
+ for (IJavaElement testElement : testElements) {
+ collectAddOpensVmArgs(addOpensTargets, addOpensVmArgs, testElement, configuration);
+ }
+ }
+
+ String testFailureNames = configuration.getAttribute(JUnitLaunchConfigurationConstants.ATTR_FAILURES_NAMES, ""); //$NON-NLS-1$
+ if (testFailureNames.length() > 0) {
+ programArguments.add("-testfailures"); //$NON-NLS-1$
+ programArguments.add(testFailureNames);
+ }
+
+ String uniqueId = configuration.getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_UNIQUE_ID, ""); //$NON-NLS-1$
+ if (!uniqueId.trim().isEmpty()) {
+ programArguments.add("-uniqueId"); //$NON-NLS-1$
+ programArguments.add(uniqueId);
+ }
+
+ boolean hasIncludeTags = configuration
+ .getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_HAS_INCLUDE_TAGS, false);
+ if (hasIncludeTags) {
+ String includeTags = configuration.getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_INCLUDE_TAGS,
+ ""); //$NON-NLS-1$
+ if (includeTags != null && !includeTags.trim().isEmpty()) {
+ String[] tags = includeTags.split(","); //$NON-NLS-1$
+ for (String tag : tags) {
+ programArguments.add("--include-tag"); //$NON-NLS-1$
+ programArguments.add(tag.trim());
+ }
+ }
+ }
+
+ boolean hasExcludeTags = configuration
+ .getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_HAS_EXCLUDE_TAGS, false);
+ if (hasExcludeTags) {
+ String excludeTags = configuration.getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_EXCLUDE_TAGS,
+ ""); //$NON-NLS-1$
+ if (excludeTags != null && !excludeTags.trim().isEmpty()) {
+ String[] tags = excludeTags.split(","); //$NON-NLS-1$
+ for (String tag : tags) {
+ programArguments.add("--exclude-tag"); //$NON-NLS-1$
+ programArguments.add(tag.trim());
+ }
+ }
+ }
+
+ if (addOpensTargets != null) {
+ vmArguments.addAll(addOpensVmArgs);
+ }
+ }
+
+ private static boolean isOnModulePath(IJavaProject javaProject, String typeToCheck) {
+ try {
+ IType type = javaProject.findType(typeToCheck);
+ if (type == null)
+ return false;
+ IPackageFragmentRoot packageFragmentRoot = (IPackageFragmentRoot) type.getPackageFragment().getParent();
+ IClasspathEntry resolvedClasspathEntry = packageFragmentRoot.getResolvedClasspathEntry();
+ return Arrays.stream(resolvedClasspathEntry.getExtraAttributes())
+ .anyMatch(p -> p.getName().equals(IClasspathAttribute.MODULE) && p.getValue().equals("true")); //$NON-NLS-1$
+ } catch (JavaModelException e) {
+ // if anything goes wrong, assume true (in the worst case, user get a warning
+ // because of a redundant add-opens)
+ return true;
+ }
+ }
+
+ private void collectAddOpensVmArgs(String addOpensTargets, List<String> addOpensVmArgs, IJavaElement javaElem,
+ ILaunchConfiguration configuration) throws CoreException {
+ if (addOpensTargets != null) {
+ IPackageFragment pkg = getParentPackageFragment(javaElem);
+ if (pkg != null) {
+ String pkgName = pkg.getElementName();
+ collectAddOpensVmArgs(addOpensTargets, addOpensVmArgs, pkgName, configuration);
+ }
+ }
+ }
+
+ private void collectAddOpensVmArgs(String addOpensTargets, List<String> addOpensVmArgs, String pkgName,
+ ILaunchConfiguration configuration) throws CoreException {
+ if (addOpensTargets != null) {
+ IJavaProject javaProject = getJavaProject(configuration);
+ String sourceModuleName = javaProject.getModuleDescription().getElementName();
+ addOpensVmArgs.add("--add-opens"); //$NON-NLS-1$
+ addOpensVmArgs.add(sourceModuleName + "/" + pkgName + "=" + addOpensTargets); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ private IPackageFragment getParentPackageFragment(IJavaElement element) {
+ IJavaElement parent = element.getParent();
+ while (parent != null) {
+ if (parent instanceof IPackageFragment) {
+ return (IPackageFragment) parent;
+ }
+ parent = parent.getParent();
+ }
+ return null;
+ }
+
+ private String createPackageNamesFile(IJavaElement testContainer,
+ org.eclipse.jdt.internal.junit.launcher.ITestKind testRunnerKind, Set<String> pkgNames)
+ throws CoreException {
+ try {
+ File file = File.createTempFile("packageNames", ".txt"); //$NON-NLS-1$ //$NON-NLS-2$
+ file.deleteOnExit();
+
+ try (BufferedWriter bw = new BufferedWriter(
+ new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
+ if (testContainer instanceof IPackageFragment) {
+ pkgNames.add(getPackageName(testContainer.getElementName()));
+ } else if (testContainer instanceof IPackageFragmentRoot) {
+ addAllPackageFragments((IPackageFragmentRoot) testContainer, pkgNames);
+ } else if (testContainer instanceof IJavaProject) {
+ for (IPackageFragmentRoot pkgFragmentRoot : ((IJavaProject) testContainer)
+ .getPackageFragmentRoots()) {
+ if (!pkgFragmentRoot.isExternal() && !pkgFragmentRoot.isArchive()) {
+ addAllPackageFragments(pkgFragmentRoot, pkgNames);
+ }
+ }
+ } else {
+ abort(Messages.JUnitLaunchConfigurationDelegate_error_wrong_input, null,
+ IJavaLaunchConfigurationConstants.ERR_UNSPECIFIED_MAIN_TYPE);
+ }
+ if (pkgNames.isEmpty()) {
+ String msg = MessageFormat.format(Messages.JUnitLaunchConfigurationDelegate_error_notests_kind,
+ testRunnerKind.getDisplayName());
+ abort(msg, null, IJavaLaunchConfigurationConstants.ERR_UNSPECIFIED_MAIN_TYPE);
+ } else {
+ for (String pkgName : pkgNames) {
+ bw.write(pkgName);
+ bw.newLine();
+ }
+ }
+ }
+ return file.getAbsolutePath();
+ } catch (IOException | JavaModelException e) {
+ throw new CoreException(new Status(IStatus.ERROR, JUnitTestPlugin.PLUGIN_ID, IStatus.ERROR, "", e)); //$NON-NLS-1$
+ }
+ }
+
+ private Set<String> addAllPackageFragments(IPackageFragmentRoot pkgFragmentRoot, Set<String> pkgNames)
+ throws JavaModelException {
+ for (IJavaElement child : pkgFragmentRoot.getChildren()) {
+ if (child instanceof IPackageFragment && ((IPackageFragment) child).hasChildren()) {
+ pkgNames.add(getPackageName(child.getElementName()));
+ }
+ }
+ return pkgNames;
+ }
+
+ private String getPackageName(String elementName) {
+ if (elementName.isEmpty()) {
+ return DEFAULT;
+ }
+ return elementName;
+ }
+
+ private String createTestNamesFile(IJavaElement[] testElements) throws CoreException {
+ try {
+ File file = File.createTempFile("testNames", ".txt"); //$NON-NLS-1$ //$NON-NLS-2$
+ file.deleteOnExit();
+ try (BufferedWriter bw = new BufferedWriter(
+ new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8));) {
+ for (IJavaElement testElement : testElements) {
+ if (testElement instanceof IType) {
+ IType type = (IType) testElement;
+ String testName = type.getFullyQualifiedName();
+ bw.write(testName);
+ bw.newLine();
+ } else {
+ abort(Messages.JUnitLaunchConfigurationDelegate_error_wrong_input, null,
+ IJavaLaunchConfigurationConstants.ERR_UNSPECIFIED_MAIN_TYPE);
+ }
+ }
+ }
+ return file.getAbsolutePath();
+ } catch (IOException e) {
+ throw new CoreException(new Status(IStatus.ERROR, JUnitTestPlugin.PLUGIN_ID, IStatus.ERROR, "", e)); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public String[][] getClasspathAndModulepath(ILaunchConfiguration configuration) throws CoreException {
+ String[][] cpmp = super.getClasspathAndModulepath(configuration);
+ String[] cp = cpmp[0];
+
+ List<String> junitEntries = new ClasspathLocalizer(Platform.inDevelopmentMode())
+ .localizeClasspath(getJUnitVersion(configuration));
+
+ String[] classPath = new String[cp.length + junitEntries.size()];
+ Object[] jea = junitEntries.toArray();
+ System.arraycopy(cp, 0, classPath, 0, cp.length);
+ System.arraycopy(jea, 0, classPath, cp.length, jea.length);
+
+ cpmp[0] = classPath;
+
+ return cpmp;
+ }
+
+ private static class ClasspathLocalizer {
+
+ private boolean fInDevelopmentMode;
+
+ public ClasspathLocalizer(boolean inDevelopmentMode) {
+ fInDevelopmentMode = inDevelopmentMode;
+ }
+
+ public List<String> localizeClasspath(JUnitVersion junitVersion) {
+ JUnitRuntimeClasspathEntry[] entries = junitVersion.getJUnitTestKind().getClasspathEntries();
+ List<String> junitEntries = new ArrayList<>();
+
+ for (JUnitRuntimeClasspathEntry entrie : entries) {
+ try {
+ addEntry(junitEntries, entrie);
+ } catch (IOException | URISyntaxException e) {
+ Assert.isTrue(false, entrie.getPluginId() + " is available (required JAR)"); //$NON-NLS-1$
+ }
+ }
+ return junitEntries;
+ }
+
+ private void addEntry(List<String> junitEntries, final JUnitRuntimeClasspathEntry entry)
+ throws IOException, MalformedURLException, URISyntaxException {
+ String entryString = entryString(entry);
+ if (entryString != null)
+ junitEntries.add(entryString);
+ }
+
+ private String entryString(final JUnitRuntimeClasspathEntry entry)
+ throws IOException, MalformedURLException, URISyntaxException {
+ if (inDevelopmentMode()) {
+ try {
+ return localURL(entry.developmentModeEntry());
+ } catch (IOException e3) {
+ // fall through and try default
+ }
+ }
+ return localURL(entry);
+ }
+
+ private boolean inDevelopmentMode() {
+ return fInDevelopmentMode;
+ }
+
+ private String localURL(JUnitRuntimeClasspathEntry jar)
+ throws IOException, MalformedURLException, URISyntaxException {
+ Bundle bundle = JUnitTestPlugin.getDefault().getBundle(jar.getPluginId());
+ URL url;
+ if (jar.getPluginRelativePath() == null) {
+ String bundleClassPath = bundle.getHeaders().get(Constants.BUNDLE_CLASSPATH);
+ url = bundleClassPath != null ? bundle.getEntry(bundleClassPath) : null;
+ if (url == null) {
+ url = bundle.getEntry("/"); //$NON-NLS-1$
+ }
+ } else {
+ url = bundle.getEntry(jar.getPluginRelativePath());
+ }
+
+ if (url == null)
+ throw new IOException();
+ return URIUtil.toFile(URIUtil.toURI(FileLocator.toFileURL(url))).getAbsolutePath(); // See bug 503050
+ }
+ }
+
+ private final IJavaElement getTestTarget(ILaunchConfiguration configuration, IJavaProject javaProject)
+ throws CoreException {
+ String containerHandle = configuration.getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, ""); //$NON-NLS-1$
+ if (containerHandle.length() != 0) {
+ IJavaElement element = JavaCore.create(containerHandle);
+ if (element == null || !element.exists()) {
+ abort(Messages.JUnitLaunchConfigurationDelegate_error_input_element_deosn_not_exist, null,
+ IJavaLaunchConfigurationConstants.ERR_UNSPECIFIED_MAIN_TYPE);
+ }
+ return element;
+ }
+ String testTypeName = getMainTypeName(configuration);
+ if (testTypeName != null && testTypeName.length() != 0) {
+ IType type = javaProject.findType(testTypeName);
+ if (type != null && type.exists()) {
+ return type;
+ }
+ }
+ abort(Messages.JUnitLaunchConfigurationDelegate_input_type_does_not_exist, null,
+ IJavaLaunchConfigurationConstants.ERR_UNSPECIFIED_MAIN_TYPE);
+ return null; // not reachable
+ }
+
+ @Override
+ protected void abort(String message, Throwable exception, int code) throws CoreException {
+ throw new CoreException(new Status(IStatus.ERROR, JUnitTestPlugin.PLUGIN_ID, code, message, exception));
+ }
+
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationTab.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationTab.java
new file mode 100644
index 0000000000..0c16623926
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationTab.java
@@ -0,0 +1,1287 @@
+/*******************************************************************************
+ * 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
+ * Sebastian Davids: sdavids@gmx.de bug: 26293, 27889
+ * David Saff (saff@mit.edu) - bug 102632: [JUnit] Support for JUnit 4.
+ * Robert Konigsberg <konigsberg@google.com> - [JUnit] Improve discoverability of the ability to run a single method under JUnit Tests - https://bugs.eclipse.org/bugs/show_bug.cgi?id=285637
+ *******************************************************************************/
+package org.eclipse.jdt.ui.unittest.junit.launcher;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.unittest.ui.ConfigureViewerSupport;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.window.Window;
+
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
+import org.eclipse.ui.dialogs.SelectionDialog;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
+
+import org.eclipse.jdt.core.IAnnotation;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaModel;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.ISourceReference;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.search.IJavaSearchScope;
+import org.eclipse.jdt.core.search.SearchEngine;
+
+import org.eclipse.jdt.internal.junit.JUnitCorePlugin;
+import org.eclipse.jdt.internal.junit.launcher.ITestKind;
+import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
+import org.eclipse.jdt.internal.junit.launcher.JUnitMigrationDelegate;
+import org.eclipse.jdt.internal.junit.util.CoreTestSearchEngine;
+import org.eclipse.jdt.internal.junit.util.JUnitStubUtility;
+import org.eclipse.jdt.internal.junit.util.LayoutUtil;
+import org.eclipse.jdt.internal.junit.util.TestSearchEngine;
+
+import org.eclipse.jdt.launching.AbstractVMInstall;
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+import org.eclipse.jdt.launching.IVMInstall;
+import org.eclipse.jdt.launching.JavaRuntime;
+
+import org.eclipse.jdt.ui.IJavaElementSearchConstants;
+import org.eclipse.jdt.ui.JavaElementComparator;
+import org.eclipse.jdt.ui.JavaElementLabelProvider;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jdt.ui.StandardJavaElementContentProvider;
+import org.eclipse.jdt.ui.dialogs.ITypeInfoFilterExtension;
+import org.eclipse.jdt.ui.dialogs.TypeSelectionExtension;
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin;
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin.JUnitVersion;
+import org.eclipse.jdt.ui.unittest.junit.ui.BasicElementLabels;
+
+import org.eclipse.jdt.internal.ui.util.SWTUtil;
+import org.eclipse.jdt.internal.ui.wizards.TypedElementSelectionValidator;
+import org.eclipse.jdt.internal.ui.wizards.TypedViewerFilter;
+
+/**
+ * The launch configuration tab for JUnit.
+ * <p>
+ * This class may be instantiated but is not intended to be subclassed.
+ * </p>
+ *
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class JUnitLaunchConfigurationTab extends AbstractLaunchConfigurationTab {
+
+ // Project UI widgets
+ private Label fProjLabel;
+
+ private Text fProjText;
+
+ private Button fProjButton;
+
+ private Button fKeepRunning;
+
+ // Test class UI widgets
+ private Text fTestText;
+
+ private Button fSearchButton;
+
+ private final Image fTestIcon = createImage("obj16/test.png"); //$NON-NLS-1$
+
+ private String fOriginalTestMethodName;
+
+ private Label fTestMethodLabel;
+
+ private Text fTestMethodText;
+
+ private Button fTestMethodSearchButton;
+
+ private Text fContainerText;
+
+ private IJavaElement fContainerElement;
+
+ private final ILabelProvider fJavaElementLabelProvider = new JavaElementLabelProvider();
+
+ private Button fContainerSearchButton;
+
+ private Button fTestContainerRadioButton;
+
+ private Button fTestRadioButton;
+
+ private Label fTestLabel;
+
+ private Label fIncludeExcludeTagsLabel;
+
+ private Button fIncludeExcludeTagsButton;
+
+ private ComboViewer fTestLoaderViewer;
+
+ private ILaunchConfiguration fLaunchConfiguration;
+
+ private boolean fIsValid = true;
+
+ private Set<String> fMethodsCache;
+
+ private String fMethodsCacheKey;
+
+ /**
+ * Creates a JUnit launch configuration tab.
+ */
+ public JUnitLaunchConfigurationTab() {
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ Composite comp = new Composite(parent, SWT.NONE);
+ setControl(comp);
+
+ GridLayout topLayout = new GridLayout();
+ topLayout.numColumns = 3;
+ comp.setLayout(topLayout);
+
+ createSingleTestSection(comp);
+ createSpacer(comp);
+
+ createTestContainerSelectionGroup(comp);
+ createSpacer(comp);
+
+ createTagsGroup(comp);
+ createSpacer(comp);
+
+ createTestLoaderGroup(comp);
+ createSpacer(comp);
+
+ createKeepAliveGroup(comp);
+ Dialog.applyDialogFont(comp);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(),
+ IUnitTestHelpContextIds.LAUNCH_CONFIGURATION_DIALOG_JUNIT_MAIN_TAB);
+ validatePage();
+ }
+
+ private void createTagsGroup(Composite comp) {
+ GridData gd;
+
+ fIncludeExcludeTagsLabel = new Label(comp, SWT.NONE);
+ fIncludeExcludeTagsLabel.setText(Messages.JUnitLaunchConfigurationTab_addtag_text);
+ gd = new GridData();
+ gd.horizontalSpan = 1;
+ fIncludeExcludeTagsLabel.setLayoutData(gd);
+ fIncludeExcludeTagsButton = new Button(comp, SWT.PUSH);
+ fIncludeExcludeTagsButton.setText(Messages.JUnitLaunchConfigurationTab_addtag_label);
+ fIncludeExcludeTagsButton.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ configureIncludeExcludeTags();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+ });
+ gd = new GridData();
+ gd.horizontalSpan = 1;
+ fIncludeExcludeTagsButton.setLayoutData(gd);
+ }
+
+ private void createTestLoaderGroup(Composite comp) {
+ Label loaderLabel = new Label(comp, SWT.NONE);
+ loaderLabel.setText(Messages.JUnitLaunchConfigurationTab_Test_Loader);
+ GridData gd = new GridData();
+ gd.horizontalIndent = 0;
+ loaderLabel.setLayoutData(gd);
+
+ fTestLoaderViewer = new ComboViewer(comp, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fTestLoaderViewer.getCombo().setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ fTestLoaderViewer.setContentProvider(ArrayContentProvider.getInstance());
+ fTestLoaderViewer.setLabelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ return ((JUnitVersion) element).toString();
+ }
+ });
+ fTestLoaderViewer.setInput(JUnitVersion.values());
+ fTestLoaderViewer.addSelectionChangedListener(event -> {
+ setEnableTagsGroup(event);
+ validatePage();
+ updateLaunchConfigurationDialog();
+ });
+ }
+
+ private void setEnableTagsGroup(SelectionChangedEvent event) {
+ ISelection selection = event.getSelection();
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss = (IStructuredSelection) selection;
+ if (ss.size() == 1) {
+ Object first = ss.getFirstElement();
+ if (first instanceof JUnitVersion) {
+ fIncludeExcludeTagsButton.setEnabled(((JUnitVersion) first) == JUnitVersion.JUNIT5);
+ }
+ }
+ }
+ }
+
+ private void createSpacer(Composite comp) {
+ Label label = new Label(comp, SWT.NONE);
+ GridData gd = new GridData();
+ gd.horizontalSpan = 3;
+ label.setLayoutData(gd);
+ }
+
+ private void createSingleTestSection(Composite comp) {
+ fTestRadioButton = new Button(comp, SWT.RADIO);
+ fTestRadioButton.setText(Messages.JUnitLaunchConfigurationTab_label_oneTest);
+ GridData gd = new GridData();
+ gd.horizontalSpan = 3;
+ fTestRadioButton.setLayoutData(gd);
+ fTestRadioButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (fTestRadioButton.getSelection())
+ testModeChanged();
+ }
+ });
+
+ fProjLabel = new Label(comp, SWT.NONE);
+ fProjLabel.setText(Messages.JUnitLaunchConfigurationTab_label_project);
+ gd = new GridData();
+ gd.horizontalIndent = 25;
+ fProjLabel.setLayoutData(gd);
+
+ fProjText = new Text(comp, SWT.SINGLE | SWT.BORDER);
+ fProjText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ fProjText.addModifyListener(evt -> {
+ validatePage();
+ updateLaunchConfigurationDialog();
+ fSearchButton.setEnabled(fTestRadioButton.getSelection() && fProjText.getText().length() > 0);
+ });
+
+ fProjButton = new Button(comp, SWT.PUSH);
+ fProjButton.setText(Messages.JUnitLaunchConfigurationTab_label_browse);
+ fProjButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent evt) {
+ handleProjectButtonSelected();
+ }
+ });
+ setButtonGridData(fProjButton);
+
+ fTestLabel = new Label(comp, SWT.NONE);
+ gd = new GridData();
+ gd.horizontalIndent = 25;
+ fTestLabel.setLayoutData(gd);
+ fTestLabel.setText(Messages.JUnitLaunchConfigurationTab_label_test);
+
+ fTestText = new Text(comp, SWT.SINGLE | SWT.BORDER);
+ fTestText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ fTestText.addModifyListener(evt -> {
+ fTestMethodSearchButton.setEnabled(fTestText.getText().length() > 0);
+ validatePage();
+ updateLaunchConfigurationDialog();
+ });
+
+ fSearchButton = new Button(comp, SWT.PUSH);
+ fSearchButton.setEnabled(fProjText.getText().length() > 0);
+ fSearchButton.setText(Messages.JUnitLaunchConfigurationTab_label_search);
+ fSearchButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent evt) {
+ handleSearchButtonSelected();
+ }
+ });
+ setButtonGridData(fSearchButton);
+
+ fTestMethodLabel = new Label(comp, SWT.NONE);
+ gd = new GridData();
+ gd.horizontalIndent = 25;
+ fTestMethodLabel.setLayoutData(gd);
+ fTestMethodLabel.setText(Messages.JUnitLaunchConfigurationTab_label_method);
+
+ fTestMethodText = new Text(comp, SWT.SINGLE | SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ fTestMethodText.setLayoutData(gd);
+
+ fTestMethodText.addModifyListener(evt -> {
+ validatePage();
+ updateLaunchConfigurationDialog();
+ });
+ fTestMethodText.setMessage(Messages.JUnitLaunchConfigurationTab_all_methods_text);
+
+ fTestMethodSearchButton = new Button(comp, SWT.PUSH);
+ fTestMethodSearchButton.setEnabled(fTestText.getText().length() > 0);
+ fTestMethodSearchButton.setText(Messages.JUnitLaunchConfigurationTab_label_search_method);
+ fTestMethodSearchButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent evt) {
+ handleTestMethodSearchButtonSelected();
+ }
+ });
+
+ setButtonGridData(fTestMethodSearchButton);
+ }
+
+ private void createTestContainerSelectionGroup(Composite comp) {
+ fTestContainerRadioButton = new Button(comp, SWT.RADIO);
+ fTestContainerRadioButton.setText(Messages.JUnitLaunchConfigurationTab_label_containerTest);
+ GridData gd = new GridData();
+ gd.horizontalSpan = 3;
+ fTestContainerRadioButton.setLayoutData(gd);
+ fTestContainerRadioButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
+ if (fTestContainerRadioButton.getSelection())
+ testModeChanged();
+ }));
+
+ fContainerText = new Text(comp, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY);
+ SWTUtil.fixReadonlyTextBackground(fContainerText);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalIndent = 25;
+ gd.horizontalSpan = 2;
+ fContainerText.setLayoutData(gd);
+ fContainerText.addModifyListener(evt -> updateLaunchConfigurationDialog());
+
+ fContainerSearchButton = new Button(comp, SWT.PUSH);
+ fContainerSearchButton.setText(Messages.JUnitLaunchConfigurationTab_label_search);
+ fContainerSearchButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent evt) {
+ handleContainerSearchButtonSelected();
+ }
+ });
+ setButtonGridData(fContainerSearchButton);
+ }
+
+ private void handleContainerSearchButtonSelected() {
+ IJavaElement javaElement = chooseContainer(fContainerElement);
+ if (javaElement != null)
+ setContainerElement(javaElement);
+ }
+
+ private void setContainerElement(IJavaElement javaElement) {
+ fContainerElement = javaElement;
+ fContainerText.setText(getPresentationName(javaElement));
+ validatePage();
+ updateLaunchConfigurationDialog();
+ }
+
+ private void createKeepAliveGroup(Composite comp) {
+ GridData gd;
+ fKeepRunning = new Button(comp, SWT.CHECK);
+ fKeepRunning.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
+ updateLaunchConfigurationDialog();
+ }));
+ fKeepRunning.setText(Messages.JUnitLaunchConfigurationTab_label_keeprunning);
+ gd = new GridData();
+ gd.horizontalAlignment = GridData.FILL;
+ gd.horizontalSpan = 2;
+ fKeepRunning.setLayoutData(gd);
+ }
+
+ private static Image createImage(String path) {
+ URL url = FileLocator.find(JUnitTestPlugin.getDefault().getBundle(), new Path("icons/full/" + path)); //$NON-NLS-1$
+ if (url != null) {
+ return ImageDescriptor.createFromURL(url).createImage();
+ }
+ return null;
+ }
+
+ @Override
+ public void initializeFrom(ILaunchConfiguration config) {
+ fLaunchConfiguration = config;
+
+ updateProjectFromConfig(config);
+ String containerHandle = ""; //$NON-NLS-1$
+ try {
+ containerHandle = config.getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, ""); //$NON-NLS-1$
+ } catch (CoreException ce) {
+ }
+
+ if (containerHandle.length() > 0)
+ updateTestContainerFromConfig(config);
+ else
+ updateTestTypeFromConfig(config);
+ updateKeepRunning(config);
+ updateTestLoaderFromConfig(config);
+
+ validatePage();
+ }
+
+ private void updateTestLoaderFromConfig(ILaunchConfiguration config) {
+ JUnitVersion junitVersion = JUnitLaunchConfigurationDelegate.getJUnitVersion(config);
+ if (junitVersion == null) {
+ if (fContainerElement != null) {
+ junitVersion = JUnitTestPlugin.getJUnitVersion(fContainerElement);
+ }
+ if (junitVersion == null) {
+ junitVersion = JUnitVersion.JUNIT3;
+ }
+ }
+ fTestLoaderViewer.setSelection(new StructuredSelection(junitVersion));
+ }
+
+ private JUnitVersion getSelectedJUnitVersion() {
+ IStructuredSelection selection = (IStructuredSelection) fTestLoaderViewer.getSelection();
+ return (JUnitVersion) selection.getFirstElement();
+ }
+
+ private void updateKeepRunning(ILaunchConfiguration config) {
+ boolean running = false;
+ try {
+ running = config.getAttribute(JUnitLaunchConfigurationConstants.ATTR_KEEPRUNNING, false);
+ } catch (CoreException ce) {
+ }
+ fKeepRunning.setSelection(running);
+ }
+
+ private void updateProjectFromConfig(ILaunchConfiguration config) {
+ String projectName = ""; //$NON-NLS-1$
+ try {
+ projectName = config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, ""); //$NON-NLS-1$
+ } catch (CoreException ce) {
+ }
+ fProjText.setText(projectName);
+ }
+
+ private void updateTestTypeFromConfig(ILaunchConfiguration config) {
+ String testTypeName = ""; //$NON-NLS-1$
+ fOriginalTestMethodName = ""; //$NON-NLS-1$
+ try {
+ testTypeName = config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, ""); //$NON-NLS-1$
+ fOriginalTestMethodName = config.getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_NAME, ""); //$NON-NLS-1$
+ } catch (CoreException ce) {
+ }
+ fTestRadioButton.setSelection(true);
+ setEnableSingleTestGroup(true);
+ setEnableContainerTestGroup(false);
+ fTestContainerRadioButton.setSelection(false);
+ fTestText.setText(testTypeName);
+ fContainerText.setText(""); //$NON-NLS-1$
+ fTestMethodText.setText(fOriginalTestMethodName);
+ }
+
+ private void updateTestContainerFromConfig(ILaunchConfiguration config) {
+ String containerHandle = ""; //$NON-NLS-1$
+ IJavaElement containerElement = null;
+ try {
+ containerHandle = config.getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, ""); //$NON-NLS-1$
+ if (containerHandle.length() > 0) {
+ containerElement = JavaCore.create(containerHandle);
+ }
+ } catch (CoreException ce) {
+ }
+ if (containerElement != null)
+ fContainerElement = containerElement;
+ fTestContainerRadioButton.setSelection(true);
+ setEnableSingleTestGroup(false);
+ setEnableContainerTestGroup(true);
+ fTestRadioButton.setSelection(false);
+ if (fContainerElement != null)
+ fContainerText.setText(getPresentationName(fContainerElement));
+ fTestText.setText(""); //$NON-NLS-1$
+ }
+
+ @Override
+ public void performApply(ILaunchConfigurationWorkingCopy config) {
+ if (fTestContainerRadioButton.getSelection() && fContainerElement != null) {
+ config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
+ fContainerElement.getJavaProject().getElementName());
+ config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER,
+ fContainerElement.getHandleIdentifier());
+ config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, ""); //$NON-NLS-1$
+ // workaround for bug 65399
+ config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_NAME, ""); //$NON-NLS-1$
+ } else {
+ config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, fProjText.getText());
+ config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, fTestText.getText());
+ config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, ""); //$NON-NLS-1$
+ config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_NAME, fTestMethodText.getText());
+ }
+
+ new ConfigureViewerSupport(JUnitTestPlugin.UNIT_TEST_VIEW_SUPPORT_ID).apply(config);
+ config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_KEEPRUNNING, fKeepRunning.getSelection());
+ try {
+ mapResources(config);
+ } catch (CoreException e) {
+ JUnitTestPlugin.log(e.getStatus());
+ }
+ IStructuredSelection junitVersionSelection = (IStructuredSelection) fTestLoaderViewer.getSelection();
+ if (!junitVersionSelection.isEmpty()) {
+ JUnitVersion junitVersion = (JUnitVersion) junitVersionSelection.getFirstElement();
+ config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND,
+ junitVersion.getJUnitTestKind().getId());
+ }
+ }
+
+ private void mapResources(ILaunchConfigurationWorkingCopy config) throws CoreException {
+ JUnitMigrationDelegate.mapResources(config);
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ fTestIcon.dispose();
+ fJavaElementLabelProvider.dispose();
+ }
+
+ @Override
+ public Image getImage() {
+ return fTestIcon;
+ }
+
+ /*
+ * Show a dialog that lists all main types
+ */
+ private void handleSearchButtonSelected() {
+ Shell shell = getShell();
+
+ IJavaProject javaProject = getJavaProject();
+
+ IType[] types = new IType[0];
+ boolean[] radioSetting = new boolean[2];
+ try {
+ // fix for 66922 Wrong radio behaviour when switching
+ // remember the selected radio button
+ radioSetting[0] = fTestRadioButton.getSelection();
+ radioSetting[1] = fTestContainerRadioButton.getSelection();
+
+ types = TestSearchEngine.findTests(getLaunchConfigurationDialog(), javaProject,
+ getSelectedJUnitVersion().getJUnitTestKind());
+ } catch (InterruptedException e) {
+ setErrorMessage(e.getMessage());
+ return;
+ } catch (InvocationTargetException e) {
+ JUnitTestPlugin.log(e.getTargetException());
+ return;
+ } finally {
+ fTestRadioButton.setSelection(radioSetting[0]);
+ fTestContainerRadioButton.setSelection(radioSetting[1]);
+ }
+
+ final HashSet<String> typeLookup = new HashSet<>();
+ for (IType type : types) {
+ typeLookup.add(type.getPackageFragment().getElementName() + '/' + type.getTypeQualifiedName('.'));
+ }
+ SelectionDialog dialog = null;
+ try {
+ dialog = JavaUI.createTypeDialog(shell, getLaunchConfigurationDialog(),
+ SearchEngine.createJavaSearchScope(new IJavaElement[] { javaProject }, IJavaSearchScope.SOURCES),
+ IJavaElementSearchConstants.CONSIDER_ALL_TYPES, false, "**", //$NON-NLS-1$
+ new TypeSelectionExtension() {
+ @Override
+ public ITypeInfoFilterExtension getFilterExtension() {
+ return requestor -> {
+ StringBuilder buf = new StringBuilder();
+ buf.append(requestor.getPackageName()).append('/');
+ String enclosingName = requestor.getEnclosingName();
+ if (enclosingName.length() > 0)
+ buf.append(enclosingName).append('.');
+ buf.append(requestor.getTypeName());
+ return typeLookup.contains(buf.toString());
+ };
+ }
+ });
+ } catch (JavaModelException e) {
+ JUnitTestPlugin.log(e);
+ return;
+ }
+
+ dialog.setTitle(Messages.JUnitLaunchConfigurationTab_testdialog_title);
+ dialog.setMessage(Messages.JUnitLaunchConfigurationTab_testdialog_message);
+ if (dialog.open() == Window.CANCEL) {
+ return;
+ }
+
+ Object[] results = dialog.getResult();
+ if ((results == null) || (results.length < 1)) {
+ return;
+ }
+ IType type = (IType) results[0];
+
+ if (type != null) {
+ fTestText.setText(type.getFullyQualifiedName('.'));
+ javaProject = type.getJavaProject();
+ fProjText.setText(javaProject.getElementName());
+ }
+ }
+
+ /*
+ * Show a dialog that lets the user select a project. This in turn provides
+ * context for the main type, allowing the user to key a main type name, or
+ * constraining the search for main types to the specified project.
+ */
+ private void handleProjectButtonSelected() {
+ IJavaProject project = chooseJavaProject();
+ if (project == null) {
+ return;
+ }
+
+ String projectName = project.getElementName();
+ fProjText.setText(projectName);
+ }
+
+ private void handleTestMethodSearchButtonSelected() {
+ try {
+ IJavaProject javaProject = getJavaProject();
+ IType testType = javaProject.findType(fTestText.getText());
+ Set<String> methodNames = getMethodsForType(javaProject, testType, getSelectedJUnitVersion());
+ String methodName = chooseMethodName(methodNames);
+
+ if (methodName != null) {
+ fTestMethodText.setText(methodName);
+ validatePage();
+ updateLaunchConfigurationDialog();
+ }
+ } catch (JavaModelException e) {
+ JUnitTestPlugin.log(e.getStatus());
+ }
+ }
+
+ private Set<String> getMethodsForType(IJavaProject javaProject, IType type, JUnitVersion junitVersion)
+ throws JavaModelException {
+ ITestKind testKind = junitVersion.getJUnitTestKind();
+ if (javaProject == null || type == null || testKind == null)
+ return Collections.emptySet();
+ String methodsCacheKey = javaProject.getElementName() + '\n' + type.getFullyQualifiedName() + '\n'
+ + testKind.getId();
+ if (methodsCacheKey.equals(fMethodsCacheKey))
+ return fMethodsCache;
+
+ Set<String> methodNames = new HashSet<>();
+ fMethodsCache = methodNames;
+ fMethodsCacheKey = methodsCacheKey;
+
+ collectMethodNames(type, javaProject, junitVersion, methodNames);
+
+ return methodNames;
+ }
+
+ private void collectMethodNames(IType type, IJavaProject javaProject, JUnitVersion junitVersion,
+ Set<String> methodNames) throws JavaModelException {
+ if (type == null) {
+ return;
+ }
+ collectDeclaredMethodNames(type, javaProject, junitVersion, methodNames);
+
+ String superclassName = type.getSuperclassName();
+ IType superType = getResolvedType(superclassName, type, javaProject);
+ collectMethodNames(superType, javaProject, junitVersion, methodNames);
+
+ String[] superInterfaceNames = type.getSuperInterfaceNames();
+ for (String interfaceName : superInterfaceNames) {
+ superType = getResolvedType(interfaceName, type, javaProject);
+ collectMethodNames(superType, javaProject, junitVersion, methodNames);
+ }
+ }
+
+ private IType getResolvedType(String typeName, IType type, IJavaProject javaProject) throws JavaModelException {
+ IType resolvedType = null;
+ if (typeName != null) {
+ int pos = typeName.indexOf('<');
+ if (pos != -1) {
+ typeName = typeName.substring(0, pos);
+ }
+ String[][] resolvedTypeNames = type.resolveType(typeName);
+ if (resolvedTypeNames != null && resolvedTypeNames.length > 0) {
+ String[] resolvedTypeName = resolvedTypeNames[0];
+ resolvedType = javaProject.findType(resolvedTypeName[0], resolvedTypeName[1]); // secondary types not
+ // found by this API
+ }
+ }
+ return resolvedType;
+ }
+
+ private void collectDeclaredMethodNames(IType type, IJavaProject javaProject, JUnitVersion jUnitVersion,
+ Set<String> methodNames) throws JavaModelException {
+ IMethod[] methods = type.getMethods();
+ for (IMethod method : methods) {
+ String methodName = method.getElementName();
+ int flags = method.getFlags();
+ // Only include public, non-static, no-arg methods that return void and start
+ // with "test":
+ if (Modifier.isPublic(flags) && !Modifier.isStatic(flags) && method.getNumberOfParameters() == 0
+ && Signature.SIG_VOID.equals(method.getReturnType()) && methodName.startsWith("test")) { //$NON-NLS-1$
+ methodNames.add(methodName);
+ }
+ if (jUnitVersion != JUnitVersion.JUNIT3 && !Modifier.isPrivate(flags) && !Modifier.isStatic(flags)) {
+ IAnnotation annotation = method.getAnnotation("Test"); //$NON-NLS-1$
+ if (annotation.exists()) {
+ methodNames.add(methodName + JUnitStubUtility.getParameterTypes(method, false));
+ } else if (jUnitVersion == JUnitVersion.JUNIT5) {
+ boolean hasAnyTestAnnotation = method.getAnnotation("TestFactory").exists() //$NON-NLS-1$
+ || method.getAnnotation("Testable").exists() //$NON-NLS-1$
+ || method.getAnnotation("TestTemplate").exists() //$NON-NLS-1$
+ || method.getAnnotation("ParameterizedTest").exists() //$NON-NLS-1$
+ || method.getAnnotation("RepeatedTest").exists(); //$NON-NLS-1$
+ if (hasAnyTestAnnotation || isAnnotatedWithTestable(method, type, javaProject)) {
+ methodNames.add(methodName + JUnitStubUtility.getParameterTypes(method, false));
+ }
+ }
+ }
+ }
+ }
+
+ // See JUnit5TestFinder.Annotation#annotates also.
+ private boolean isAnnotatedWithTestable(IMethod method, IType declaringType, IJavaProject javaProject)
+ throws JavaModelException {
+ for (IAnnotation annotation : method.getAnnotations()) {
+ IType annotationType = getResolvedType(annotation.getElementName(), declaringType, javaProject);
+ if (annotationType != null) {
+ if (matchesTestable(annotationType)) {
+ return true;
+ }
+ Set<IType> hierarchy = new HashSet<>();
+ if (matchesTestableInAnnotationHierarchy(annotationType, javaProject, hierarchy)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean matchesTestable(IType annotationType) {
+ return annotationType != null
+ && JUnitCorePlugin.JUNIT5_TESTABLE_ANNOTATION_NAME.equals(annotationType.getFullyQualifiedName());
+ }
+
+ private boolean matchesTestableInAnnotationHierarchy(IType annotationType, IJavaProject javaProject,
+ Set<IType> hierarchy) throws JavaModelException {
+ if (annotationType != null) {
+ for (IAnnotation annotation : annotationType.getAnnotations()) {
+ IType annType = getResolvedType(annotation.getElementName(), annotationType, javaProject);
+ if (annType != null && hierarchy.add(annType)) {
+ if (matchesTestable(annType)
+ || matchesTestableInAnnotationHierarchy(annType, javaProject, hierarchy)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private String chooseMethodName(Set<String> methodNames) {
+ Shell shell = getShell();
+
+ ElementListSelectionDialog dialog = new ElementListSelectionDialog(shell, new LabelProvider());
+ dialog.setMessage(
+ MessageFormat.format(Messages.JUnitLaunchConfigurationTab_select_method_header, fTestText.getText()));
+ dialog.setTitle(Messages.JUnitLaunchConfigurationTab_select_method_title);
+
+ int methodCount = methodNames.size();
+ String[] elements = new String[methodCount + 1];
+ methodNames.toArray(elements);
+ elements[methodCount] = Messages.JUnitLaunchConfigurationTab_all_methods_text;
+
+ dialog.setElements(elements);
+
+ String methodName = fTestMethodText.getText();
+
+ if (methodNames.contains(methodName)) {
+ dialog.setInitialSelections(methodName);
+ }
+
+ dialog.setAllowDuplicates(false);
+ dialog.setMultipleSelection(false);
+ if (dialog.open() == Window.OK) {
+ String result = (String) dialog.getFirstResult();
+ return (result == null || result.equals(Messages.JUnitLaunchConfigurationTab_all_methods_text)) ? "" //$NON-NLS-1$
+ : result;
+ }
+ return null;
+ }
+
+ /*
+ * Realize a Java Project selection dialog and return the first selected
+ * project, or null if there was none.
+ */
+ private IJavaProject chooseJavaProject() {
+ IJavaProject[] projects;
+ try {
+ projects = JavaCore.create(getWorkspaceRoot()).getJavaProjects();
+ } catch (JavaModelException e) {
+ JUnitTestPlugin.log(e.getStatus());
+ projects = new IJavaProject[0];
+ }
+
+ ILabelProvider labelProvider = new JavaElementLabelProvider(JavaElementLabelProvider.SHOW_DEFAULT);
+ ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), labelProvider);
+ dialog.setTitle(Messages.JUnitLaunchConfigurationTab_projectdialog_title);
+ dialog.setMessage(Messages.JUnitLaunchConfigurationTab_projectdialog_message);
+ dialog.setElements(projects);
+
+ IJavaProject javaProject = getJavaProject();
+ if (javaProject != null) {
+ dialog.setInitialSelections(javaProject);
+ }
+ if (dialog.open() == Window.OK) {
+ return (IJavaProject) dialog.getFirstResult();
+ }
+ return null;
+ }
+
+ /*
+ * Return the IJavaProject corresponding to the project name in the project name
+ * text field, or null if the text does not match a project name.
+ */
+ private IJavaProject getJavaProject() {
+ String projectName = fProjText.getText().trim();
+ if (projectName.length() < 1) {
+ return null;
+ }
+ return getJavaModel().getJavaProject(projectName);
+ }
+
+ /*
+ * Convenience method to get the workspace root.
+ */
+ private IWorkspaceRoot getWorkspaceRoot() {
+ return ResourcesPlugin.getWorkspace().getRoot();
+ }
+
+ /*
+ * Convenience method to get access to the java model.
+ */
+ private IJavaModel getJavaModel() {
+ return JavaCore.create(getWorkspaceRoot());
+ }
+
+ @Override
+ public boolean isValid(ILaunchConfiguration config) {
+ validatePage();
+ return fIsValid;
+ }
+
+ private void testModeChanged() {
+ boolean isSingleTestMode = fTestRadioButton.getSelection();
+ setEnableSingleTestGroup(isSingleTestMode);
+ setEnableContainerTestGroup(!isSingleTestMode);
+ if (!isSingleTestMode && fContainerText.getText().length() == 0) {
+ String projText = fProjText.getText();
+ if (Path.EMPTY.isValidSegment(projText)) {
+ IJavaProject javaProject = getJavaModel().getJavaProject(projText);
+ if (javaProject != null && javaProject.exists())
+ setContainerElement(javaProject);
+ }
+ }
+ validatePage();
+ updateLaunchConfigurationDialog();
+ }
+
+ @Override
+ protected void setErrorMessage(String errorMessage) {
+ fIsValid = errorMessage == null;
+ super.setErrorMessage(errorMessage);
+ }
+
+ private void validatePage() {
+
+ setErrorMessage(null);
+ setMessage(null);
+
+ if (fTestContainerRadioButton.getSelection()) {
+ if (fContainerElement == null) {
+ setErrorMessage(Messages.JUnitLaunchConfigurationTab_error_noContainer);
+ return;
+ }
+ validateJavaProject(fContainerElement.getJavaProject());
+
+ } else {
+ String projectName = fProjText.getText().trim();
+ if (projectName.length() == 0) {
+ setErrorMessage(Messages.JUnitLaunchConfigurationTab_error_projectnotdefined);
+ return;
+ }
+
+ IStatus status = ResourcesPlugin.getWorkspace().validatePath(IPath.SEPARATOR + projectName,
+ IResource.PROJECT);
+ if (!status.isOK() || !Path.ROOT.isValidSegment(projectName)) {
+ setErrorMessage(MessageFormat.format(Messages.JUnitLaunchConfigurationTab_error_invalidProjectName,
+ BasicElementLabels.getResourceName(projectName)));
+ return;
+ }
+
+ IProject project = getWorkspaceRoot().getProject(projectName);
+ if (!project.exists()) {
+ setErrorMessage(Messages.JUnitLaunchConfigurationTab_error_projectnotexists);
+ return;
+ }
+ IJavaProject javaProject = JavaCore.create(project);
+ validateJavaProject(javaProject);
+
+ try {
+ if (!project.hasNature(JavaCore.NATURE_ID)) {
+ setErrorMessage(Messages.JUnitLaunchConfigurationTab_error_notJavaProject);
+ return;
+ }
+ String className = fTestText.getText().trim();
+ if (className.length() == 0) {
+ setErrorMessage(Messages.JUnitLaunchConfigurationTab_error_testnotdefined);
+ return;
+ }
+ IType type = javaProject.findType(className);
+ if (type == null) {
+ setErrorMessage(MessageFormat.format(
+ Messages.JUnitLaunchConfigurationTab_error_test_class_not_found, className, projectName));
+ return;
+ }
+ String methodName = fTestMethodText.getText();
+ if (methodName.length() > 0) {
+ Set<String> methodsForType = getMethodsForType(javaProject, type, getSelectedJUnitVersion());
+ if (!methodsForType.contains(methodName)) {
+ super.setErrorMessage(
+ MessageFormat.format(Messages.JUnitLaunchConfigurationTab_error_test_method_not_found,
+ className, methodName, projectName));
+ return;
+ }
+ }
+ } catch (CoreException e) {
+ JUnitTestPlugin.log(e);
+ }
+ }
+
+ validateTestLoaderJVM();
+ }
+
+ private void validateJavaProject(IJavaProject javaProject) {
+ JUnitVersion jUnitVersion = getSelectedJUnitVersion();
+ if (jUnitVersion != null) {
+ if (jUnitVersion != JUnitVersion.JUNIT5 && !CoreTestSearchEngine.hasTestCaseType(javaProject)) {
+ setErrorMessage(Messages.JUnitLaunchConfigurationTab_error_testcasenotonpath);
+ return;
+ }
+
+ String msg = Messages.JUnitLaunchConfigurationTab_error_testannotationnotonpath;
+ if (jUnitVersion == JUnitVersion.JUNIT4 && !CoreTestSearchEngine.hasJUnit4TestAnnotation(javaProject)) {
+ setErrorMessage(MessageFormat.format(msg, JUnitCorePlugin.JUNIT4_ANNOTATION_NAME));
+ return;
+ }
+ if (jUnitVersion == JUnitVersion.JUNIT5 && !CoreTestSearchEngine.hasJUnit5TestAnnotation(javaProject)) {
+ setErrorMessage(MessageFormat.format(msg, JUnitCorePlugin.JUNIT5_TESTABLE_ANNOTATION_NAME));
+ return;
+ }
+ }
+
+ }
+
+ private void validateTestLoaderJVM() {
+ if (fLaunchConfiguration == null)
+ return;
+
+ JUnitVersion junitVersion = getSelectedJUnitVersion();
+ if (junitVersion == null || junitVersion == JUnitVersion.JUNIT3)
+ return;
+ try {
+ String path = fLaunchConfiguration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH,
+ (String) null);
+ if (path != null) {
+ IVMInstall vm = JavaRuntime.getVMInstall(Path.fromPortableString(path));
+ if (vm instanceof AbstractVMInstall) {
+ String compliance = ((AbstractVMInstall) vm).getJavaVersion();
+ if (compliance != null) {
+ if (junitVersion == JUnitVersion.JUNIT4 && !JUnitStubUtility.is50OrHigher(compliance)) {
+ setErrorMessage(Messages.JUnitLaunchConfigurationTab_error_JDK15_required);
+ } else if (junitVersion == JUnitVersion.JUNIT5 && !JUnitStubUtility.is18OrHigher(compliance)) {
+ setErrorMessage(Messages.JUnitLaunchConfigurationTab_error_JDK18_required);
+ }
+ }
+ }
+ }
+ } catch (CoreException e) {
+ }
+ }
+
+ private void setEnableContainerTestGroup(boolean enabled) {
+ fContainerSearchButton.setEnabled(enabled);
+ fContainerText.setEnabled(enabled);
+ }
+
+ private void setEnableSingleTestGroup(boolean enabled) {
+ fProjLabel.setEnabled(enabled);
+ fProjText.setEnabled(enabled);
+ fProjButton.setEnabled(enabled);
+ fTestLabel.setEnabled(enabled);
+ fTestText.setEnabled(enabled);
+ boolean projectTextHasContents = fProjText.getText().length() > 0;
+ fSearchButton.setEnabled(enabled && projectTextHasContents);
+ fTestMethodLabel.setEnabled(enabled);
+ fTestMethodText.setEnabled(enabled);
+ fTestMethodSearchButton.setEnabled(enabled && projectTextHasContents && fTestText.getText().length() > 0);
+ }
+
+ @Override
+ public void setDefaults(ILaunchConfigurationWorkingCopy config) {
+ IJavaElement javaElement = getContext();
+ if (javaElement != null) {
+ initializeJavaProject(javaElement, config);
+ } else {
+ // We set empty attributes for project & main type so that when one config is
+ // compared to another, the existence of empty attributes doesn't cause an
+ // incorrect result (the performApply() method can result in empty values
+ // for these attributes being set on a config if there is nothing in the
+ // corresponding text boxes)
+ config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, ""); //$NON-NLS-1$
+ config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, ""); //$NON-NLS-1$
+ }
+ initializeTestAttributes(javaElement, config);
+ }
+
+ private void initializeTestAttributes(IJavaElement javaElement, ILaunchConfigurationWorkingCopy config) {
+ if (javaElement != null && javaElement.getElementType() < IJavaElement.COMPILATION_UNIT)
+ initializeTestContainer(javaElement, config);
+ else
+ initializeTestType(javaElement, config);
+ }
+
+ private void initializeTestContainer(IJavaElement javaElement, ILaunchConfigurationWorkingCopy config) {
+ config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, javaElement.getHandleIdentifier());
+ initializeName(config, javaElement.getElementName());
+ }
+
+ private void initializeName(ILaunchConfigurationWorkingCopy config, String name) {
+ if (name == null) {
+ name = ""; //$NON-NLS-1$
+ }
+ if (name.length() > 0) {
+ int index = name.lastIndexOf('.');
+ if (index > 0) {
+ name = name.substring(index + 1);
+ }
+ name = getLaunchConfigurationDialog().generateName(name);
+ config.rename(name);
+ }
+ }
+
+ /*
+ * Set the main type & name attributes on the working copy based on the
+ * IJavaElement
+ */
+ private void initializeTestType(IJavaElement javaElement, ILaunchConfigurationWorkingCopy config) {
+ String name = ""; //$NON-NLS-1$
+ String testKindId = null;
+ try {
+ // we only do a search for compilation units or class files or
+ // or source references
+ if (javaElement instanceof ISourceReference) {
+ ITestKind testKind = JUnitTestPlugin.getJUnitVersion(javaElement).getJUnitTestKind();
+ testKindId = testKind.getId();
+
+ IType[] types = TestSearchEngine.findTests(getLaunchConfigurationDialog(), javaElement, testKind);
+ if ((types == null) || (types.length < 1)) {
+ return;
+ }
+ // Simply grab the first main type found in the searched element
+ name = types[0].getFullyQualifiedName('.');
+
+ }
+ } catch (InterruptedException | InvocationTargetException ie) {
+ }
+ config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, name);
+ new ConfigureViewerSupport(testKindId).apply(config);
+ initializeName(config, name);
+ boolean isRunWithJUnitPlatform = JUnitTestPlugin.isRunWithJUnitPlatform(javaElement);
+ if (isRunWithJUnitPlatform) {
+ config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_RUN_WITH_JUNIT_PLATFORM_ANNOTATION, true);
+ }
+ }
+
+ @Override
+ public String getName() {
+ return Messages.JUnitLaunchConfigurationTab_tab_label;
+ }
+
+ private IJavaElement chooseContainer(IJavaElement initElement) {
+ Class<?>[] acceptedClasses = new Class[] { IPackageFragmentRoot.class, IJavaProject.class,
+ IPackageFragment.class };
+ TypedElementSelectionValidator validator = new TypedElementSelectionValidator(acceptedClasses, false) {
+ @Override
+ public boolean isSelectedValid(Object element) {
+ return true;
+ }
+ };
+
+ acceptedClasses = new Class[] { IJavaModel.class, IPackageFragmentRoot.class, IJavaProject.class,
+ IPackageFragment.class };
+ ViewerFilter filter = new TypedViewerFilter(acceptedClasses) {
+ @Override
+ public boolean select(Viewer viewer, Object parent, Object element) {
+ if (element instanceof IPackageFragmentRoot && ((IPackageFragmentRoot) element).isArchive())
+ return false;
+ try {
+ if (element instanceof IPackageFragment && !((IPackageFragment) element).hasChildren()) {
+ return false;
+ }
+ } catch (JavaModelException e) {
+ return false;
+ }
+ return super.select(viewer, parent, element);
+ }
+ };
+
+ StandardJavaElementContentProvider provider = new StandardJavaElementContentProvider();
+ ILabelProvider labelProvider = new JavaElementLabelProvider(JavaElementLabelProvider.SHOW_DEFAULT);
+ ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(getShell(), labelProvider, provider);
+ dialog.setValidator(validator);
+ dialog.setComparator(new JavaElementComparator());
+ dialog.setTitle(Messages.JUnitLaunchConfigurationTab_folderdialog_title);
+ dialog.setMessage(Messages.JUnitLaunchConfigurationTab_folderdialog_message);
+ dialog.addFilter(filter);
+ dialog.setInput(JavaCore.create(getWorkspaceRoot()));
+ dialog.setInitialSelection(initElement);
+ dialog.setAllowMultiple(false);
+
+ if (dialog.open() == Window.OK) {
+ Object element = dialog.getFirstResult();
+ return (IJavaElement) element;
+ }
+ return null;
+ }
+
+ private String getPresentationName(IJavaElement element) {
+ return fJavaElementLabelProvider.getText(element);
+ }
+
+ /*
+ * Returns the current Java element context from which to initialize default
+ * settings, or <code>null</code> if none.
+ *
+ * @return Java element context.
+ */
+ private IJavaElement getContext() {
+ IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (activeWorkbenchWindow == null) {
+ return null;
+ }
+ IWorkbenchPage page = activeWorkbenchWindow.getActivePage();
+ if (page != null) {
+ ISelection selection = page.getSelection();
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss = (IStructuredSelection) selection;
+ if (!ss.isEmpty()) {
+ Object obj = ss.getFirstElement();
+ if (obj instanceof IJavaElement) {
+ return (IJavaElement) obj;
+ }
+ if (obj instanceof IResource) {
+ IJavaElement je = JavaCore.create((IResource) obj);
+ if (je == null) {
+ IProject pro = ((IResource) obj).getProject();
+ je = JavaCore.create(pro);
+ }
+ if (je != null) {
+ return je;
+ }
+ }
+ }
+ }
+ IEditorPart part = page.getActiveEditor();
+ if (part != null) {
+ IEditorInput input = part.getEditorInput();
+ return input.getAdapter(IJavaElement.class);
+ }
+ }
+ return null;
+ }
+
+ private void initializeJavaProject(IJavaElement javaElement, ILaunchConfigurationWorkingCopy config) {
+ IJavaProject javaProject = javaElement.getJavaProject();
+ String name = null;
+ if (javaProject != null && javaProject.exists()) {
+ name = javaProject.getElementName();
+ }
+ config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, name);
+ }
+
+ private void setButtonGridData(Button button) {
+ GridData gridData = new GridData();
+ button.setLayoutData(gridData);
+ LayoutUtil.setButtonDimensionHint(button);
+ }
+
+ @Override
+ public String getId() {
+ return "org.eclipse.jdt.ui.unittest.junit.JUnitLaunchConfigurationTab"; //$NON-NLS-1$
+ }
+
+ private void configureIncludeExcludeTags() {
+ JUnitLaunchIncludeExcludeTagsDialog dialog = new JUnitLaunchIncludeExcludeTagsDialog(getShell(),
+ fLaunchConfiguration);
+
+ if (dialog.open() == Window.OK) {
+ try {
+ ILaunchConfigurationWorkingCopy workingCopy = fLaunchConfiguration.getWorkingCopy();
+ workingCopy.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_HAS_INCLUDE_TAGS,
+ dialog.hasIncludeTags());
+ workingCopy.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_HAS_EXCLUDE_TAGS,
+ dialog.hasExcludeTags());
+ workingCopy.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_INCLUDE_TAGS,
+ dialog.getIncludeTags());
+ workingCopy.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_EXCLUDE_TAGS,
+ dialog.getExcludeTags());
+ workingCopy.doSave();
+ validatePage();
+ updateLaunchConfigurationDialog();
+ } catch (CoreException e) {
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchIncludeExcludeTagsDialog.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchIncludeExcludeTagsDialog.java
new file mode 100644
index 0000000000..5fd32c5993
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchIncludeExcludeTagsDialog.java
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Copyright (c) 2017, 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.jdt.ui.unittest.junit.launcher;
+
+import org.eclipse.swt.SWT;
+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.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.jface.dialogs.StatusDialog;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+
+import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
+import org.eclipse.jdt.internal.junit.util.LayoutUtil;
+
+import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
+import org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField;
+import org.eclipse.jdt.internal.ui.wizards.dialogfields.IDialogFieldListener;
+import org.eclipse.jdt.internal.ui.wizards.dialogfields.SelectionButtonDialogField;
+import org.eclipse.jdt.internal.ui.wizards.dialogfields.TextBoxDialogField;
+
+public class JUnitLaunchIncludeExcludeTagsDialog extends StatusDialog {
+ private SelectionButtonDialogField fHasIncludeTags;
+
+ private SelectionButtonDialogField fHasExcludeTags;
+
+ private TextBoxDialogField fIncludeTags;
+
+ private TextBoxDialogField fExcludeTags;
+
+ private ILaunchConfiguration fLaunchConfiguration;
+
+ private static final String EMPTY_STRING = ""; //$NON-NLS-1$
+
+ IDialogFieldListener fListener = this::doDialogFieldChanged;
+
+ public JUnitLaunchIncludeExcludeTagsDialog(Shell parent, ILaunchConfiguration config) {
+ super(parent);
+ fLaunchConfiguration = config;
+ setTitle(Messages.JUnitLaunchConfigurationTab_addincludeexcludetagdialog_title);
+ createIncludeTagGroup();
+ createExcludeTagGroup();
+ setHelpAvailable(false);
+ setShellStyle(getShellStyle() | SWT.RESIZE);
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite composite = (Composite) super.createDialogArea(parent);
+
+ Composite inner = new Composite(composite, SWT.NONE);
+ inner.setFont(composite.getFont());
+
+ GridLayout layout = new GridLayout();
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ layout.numColumns = 1;
+ inner.setLayout(layout);
+ inner.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ fHasIncludeTags.doFillIntoGrid(inner, 1);
+ fIncludeTags.doFillIntoGrid(inner, 2);
+ LayoutUtil.setHorizontalIndent(fIncludeTags.getLabelControl(null));
+ Text includeTagControl = fIncludeTags.getTextControl(null);
+ LayoutUtil.setHorizontalIndent(includeTagControl);
+ LayoutUtil.setHorizontalGrabbing(includeTagControl);
+ LayoutUtil.setWidthHint(fIncludeTags.getLabelControl(null), convertWidthInCharsToPixels(50));
+ LayoutUtil.setVerticalGrabbing(includeTagControl);
+ LayoutUtil.setHeightHint(includeTagControl, convertHeightInCharsToPixels(3));
+
+ fHasExcludeTags.doFillIntoGrid(inner, 1);
+ fExcludeTags.doFillIntoGrid(inner, 2);
+ LayoutUtil.setHorizontalIndent(fExcludeTags.getLabelControl(null));
+ Text excludeTagsControl = fExcludeTags.getTextControl(null);
+ LayoutUtil.setHorizontalIndent(excludeTagsControl);
+ LayoutUtil.setHorizontalGrabbing(excludeTagsControl);
+ LayoutUtil.setWidthHint(fExcludeTags.getLabelControl(null), convertWidthInCharsToPixels(50));
+ LayoutUtil.setVerticalGrabbing(excludeTagsControl);
+ LayoutUtil.setHeightHint(excludeTagsControl, convertHeightInCharsToPixels(3));
+
+ applyDialogFont(composite);
+ return composite;
+ }
+
+ private String getCommaSeperatedText(String input) {
+ if (input.isEmpty())
+ return EMPTY_STRING;
+ StringBuilder buf = new StringBuilder();
+ String[] strings = input.split(System.lineSeparator());
+ for (int i = 0; i < strings.length; i++) {
+ if (i > 0)
+ buf.append(',');
+ buf.append(strings[i]);
+ }
+ return buf.toString();
+ }
+
+ private String getLineSeperatedText(String input) {
+ if (input.isEmpty())
+ return EMPTY_STRING;
+ StringBuilder buf = new StringBuilder();
+ String[] strings = input.split(","); //$NON-NLS-1$
+ for (int i = 0; i < strings.length; i++) {
+ if (i > 0)
+ buf.append(System.lineSeparator());
+ buf.append(strings[i]);
+ }
+ return buf.toString();
+ }
+
+ public String getIncludeTags() {
+ return getCommaSeperatedText(fIncludeTags.getText());
+ }
+
+ public String getExcludeTags() {
+ return getCommaSeperatedText(fExcludeTags.getText());
+ }
+
+ public boolean hasIncludeTags() {
+ return fHasIncludeTags.isSelected();
+ }
+
+ public boolean hasExcludeTags() {
+ return fHasExcludeTags.isSelected();
+ }
+
+ private void createIncludeTagGroup() {
+ fHasIncludeTags = new SelectionButtonDialogField(SWT.CHECK);
+ fHasIncludeTags.setDialogFieldListener(fListener);
+ fHasIncludeTags.setLabelText(Messages.JUnitLaunchConfigurationTab_includetag_checkbox_label);
+
+ fIncludeTags = new TextBoxDialogField();
+ fIncludeTags.setDialogFieldListener(fListener);
+ fIncludeTags.setLabelText(Messages.JUnitLaunchConfigurationTab_includetags_description);
+
+ try {
+ fHasIncludeTags.setSelection(fLaunchConfiguration
+ .getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_HAS_INCLUDE_TAGS, false));
+ } catch (CoreException e) {
+ // ignore
+ }
+ fIncludeTags.setEnabled(fHasIncludeTags.isSelected());
+ try {
+ fIncludeTags.setText(getLineSeperatedText(fLaunchConfiguration
+ .getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_INCLUDE_TAGS, EMPTY_STRING)));
+ } catch (CoreException e) {
+ // ignore
+ }
+ }
+
+ private void createExcludeTagGroup() {
+ fHasExcludeTags = new SelectionButtonDialogField(SWT.CHECK);
+ fHasExcludeTags.setDialogFieldListener(fListener);
+ fHasExcludeTags.setLabelText(Messages.JUnitLaunchConfigurationTab_excludetag_checkbox_label);
+
+ fExcludeTags = new TextBoxDialogField();
+ fExcludeTags.setDialogFieldListener(fListener);
+ fExcludeTags.setLabelText(Messages.JUnitLaunchConfigurationTab_excludetags_description);
+
+ try {
+ fHasExcludeTags.setSelection(fLaunchConfiguration
+ .getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_HAS_EXCLUDE_TAGS, false));
+ } catch (CoreException e) {
+ // ignore
+ }
+ fExcludeTags.setEnabled(fHasExcludeTags.isSelected());
+ try {
+ fExcludeTags.setText(getLineSeperatedText(fLaunchConfiguration
+ .getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_EXCLUDE_TAGS, EMPTY_STRING)));
+ } catch (CoreException e) {
+ // ignore
+ }
+ }
+
+ private IStatus getValidationStatus() {
+ if (fHasIncludeTags != null && fHasIncludeTags.isSelected()
+ && fIncludeTags.getText().trim().equals(EMPTY_STRING)) {
+ return new StatusInfo(IStatus.ERROR, Messages.JUnitLaunchConfigurationTab_includetag_empty_error);
+ }
+
+ if (fHasExcludeTags != null && fHasExcludeTags.isSelected()
+ && fExcludeTags.getText().trim().equals(EMPTY_STRING))
+ return new StatusInfo(IStatus.ERROR, Messages.JUnitLaunchConfigurationTab_excludetag_empty_error);
+
+ return new StatusInfo();
+ }
+
+ private void doDialogFieldChanged(DialogField field) {
+ if (field == fHasIncludeTags) {
+ fIncludeTags.setEnabled(fHasIncludeTags.isSelected());
+ }
+ if (field == fHasExcludeTags) {
+ fExcludeTags.setEnabled(fHasExcludeTags.isSelected());
+ }
+ updateStatus(getValidationStatus());
+ }
+
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchShortcut.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchShortcut.java
new file mode 100644
index 0000000000..be10319010
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchShortcut.java
@@ -0,0 +1,643 @@
+/*******************************************************************************
+ * 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
+ * David Saff (saff@mit.edu) - bug 102632: [JUnit] Support for JUnit 4.
+ *******************************************************************************/
+package org.eclipse.jdt.ui.unittest.junit.launcher;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.unittest.ui.ConfigureViewerSupport;
+
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IPath;
+
+import org.eclipse.core.resources.IResource;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+
+import org.eclipse.jface.text.ITextSelection;
+
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.IDebugModelPresentation;
+import org.eclipse.debug.ui.ILaunchShortcut2;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMember;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IOrdinaryClassFile;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaModelException;
+
+import org.eclipse.jdt.internal.junit.launcher.AssertionVMArg;
+import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
+import org.eclipse.jdt.internal.junit.launcher.JUnitMigrationDelegate;
+import org.eclipse.jdt.internal.junit.util.ExceptionHandler;
+import org.eclipse.jdt.internal.junit.util.JUnitStubUtility;
+import org.eclipse.jdt.internal.junit.util.TestSearchEngine;
+
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+
+import org.eclipse.jdt.ui.JavaElementLabelProvider;
+import org.eclipse.jdt.ui.JavaElementLabels;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin;
+
+import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
+
+/**
+ * The launch shortcut to launch JUnit tests.
+ *
+ * <p>
+ * This class may be instantiated and subclassed.
+ * </p>
+ */
+public class JUnitLaunchShortcut implements ILaunchShortcut2 {
+
+ private static final String EMPTY_STRING = ""; //$NON-NLS-1$
+
+ // see org.junit.runner.Description.METHOD_AND_CLASS_NAME_PATTERN
+ private static final Pattern METHOD_AND_CLASS_NAME_PATTERN = Pattern.compile("(.*)\\((.*)\\)"); //$NON-NLS-1$
+
+ /**
+ * Default constructor.
+ */
+ public JUnitLaunchShortcut() {
+ }
+
+ @Override
+ public void launch(IEditorPart editor, String mode) {
+ ITypeRoot element = JavaUI.getEditorInputTypeRoot(editor.getEditorInput());
+ if (element != null) {
+ IMember selectedMember = resolveSelectedMemberName(editor, element);
+ if (selectedMember != null) {
+ launch(new Object[] { selectedMember }, mode);
+ } else {
+ launch(new Object[] { element }, mode);
+ }
+ } else {
+ showNoTestsFoundDialog();
+ }
+ }
+
+ private IMember resolveSelectedMemberName(IEditorPart editor, ITypeRoot element) {
+ try {
+ ISelectionProvider selectionProvider = editor.getSite().getSelectionProvider();
+ if (selectionProvider == null)
+ return null;
+
+ ISelection selection = selectionProvider.getSelection();
+ if (!(selection instanceof ITextSelection))
+ return null;
+
+ ITextSelection textSelection = (ITextSelection) selection;
+
+ IJavaElement elementAtOffset = SelectionConverter.getElementAtOffset(element, textSelection);
+ if (!(elementAtOffset instanceof IMethod) && !(elementAtOffset instanceof IType))
+ return null;
+
+ IMember member = (IMember) elementAtOffset;
+
+ ISourceRange nameRange = member.getNameRange();
+ if (nameRange.getOffset() <= textSelection.getOffset() && textSelection.getOffset()
+ + textSelection.getLength() <= nameRange.getOffset() + nameRange.getLength())
+ return member;
+ } catch (JavaModelException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ @Override
+ public void launch(ISelection selection, String mode) {
+ if (selection instanceof IStructuredSelection) {
+ launch(((IStructuredSelection) selection).toArray(), mode);
+ } else {
+ showNoTestsFoundDialog();
+ }
+ }
+
+ private void launch(Object[] elements, String mode) {
+ try {
+ IJavaElement elementToLaunch = null;
+
+ if (elements.length == 1) {
+ Object selected = elements[0];
+ if (!(selected instanceof IJavaElement) && selected instanceof IAdaptable) {
+ selected = ((IAdaptable) selected).getAdapter(IJavaElement.class);
+ }
+ if (selected instanceof IJavaElement) {
+ IJavaElement element = (IJavaElement) selected;
+ switch (element.getElementType()) {
+ case IJavaElement.JAVA_PROJECT:
+ case IJavaElement.PACKAGE_FRAGMENT_ROOT:
+ case IJavaElement.PACKAGE_FRAGMENT:
+ case IJavaElement.TYPE:
+ case IJavaElement.METHOD:
+ elementToLaunch = element;
+ break;
+ case IJavaElement.CLASS_FILE:
+ if (element instanceof IOrdinaryClassFile)
+ elementToLaunch = ((IOrdinaryClassFile) element).getType();
+ break;
+ case IJavaElement.COMPILATION_UNIT:
+ elementToLaunch = findTypeToLaunch((ICompilationUnit) element, mode);
+ break;
+ }
+ }
+ }
+ if (elementToLaunch == null) {
+ showNoTestsFoundDialog();
+ return;
+ }
+ performLaunch(elementToLaunch, mode);
+ } catch (InterruptedException e) {
+ // OK, silently move on
+ } catch (CoreException e) {
+ ExceptionHandler.handle(e, getShell(), Messages.UnitTestLaunchShortcut_dialog_title,
+ Messages.UnitTestLaunchShortcut_message_launchfailed);
+ } catch (InvocationTargetException e) {
+ ExceptionHandler.handle(e, getShell(), Messages.UnitTestLaunchShortcut_dialog_title,
+ Messages.UnitTestLaunchShortcut_message_launchfailed);
+ }
+ }
+
+ private void showNoTestsFoundDialog() {
+ MessageDialog.openInformation(getShell(), Messages.UnitTestLaunchShortcut_dialog_title,
+ Messages.UnitTestLaunchShortcut_message_notests);
+ }
+
+ private IType findTypeToLaunch(ICompilationUnit cu, String mode)
+ throws InterruptedException, InvocationTargetException {
+ IType[] types = findTypesToLaunch(cu);
+ if (types.length == 0) {
+ return null;
+ } else if (types.length > 1) {
+ return chooseType(types, mode);
+ }
+ return types[0];
+ }
+
+ private IType[] findTypesToLaunch(ICompilationUnit cu) throws InterruptedException, InvocationTargetException {
+ return TestSearchEngine.findTests(PlatformUI.getWorkbench().getActiveWorkbenchWindow(), cu,
+ JUnitTestPlugin.getJUnitVersion(cu).getJUnitTestKind());
+ }
+
+ private void performLaunch(IJavaElement element, String mode) throws InterruptedException, CoreException {
+ ILaunchConfigurationWorkingCopy temparary = createLaunchConfiguration(element);
+ ILaunchConfiguration config = findExistingLaunchConfiguration(temparary, mode);
+ if (config == null) {
+ // no existing found: create a new one
+ config = temparary.doSave();
+ }
+ DebugUITools.launch(config, mode);
+ }
+
+ private IType chooseType(IType[] types, String mode) throws InterruptedException {
+ ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(),
+ new JavaElementLabelProvider(JavaElementLabelProvider.SHOW_POST_QUALIFIED));
+ dialog.setElements(types);
+ dialog.setTitle(Messages.UnitTestLaunchShortcut_dialog_title2);
+ if (mode.equals(ILaunchManager.DEBUG_MODE)) {
+ dialog.setMessage(Messages.UnitTestLaunchShortcut_message_selectTestToDebug);
+ } else {
+ dialog.setMessage(Messages.UnitTestLaunchShortcut_message_selectTestToRun);
+ }
+ dialog.setMultipleSelection(false);
+ if (dialog.open() == Window.OK) {
+ return (IType) dialog.getFirstResult();
+ }
+ throw new InterruptedException(); // cancelled by user
+ }
+
+ private Shell getShell() {
+ return JUnitTestPlugin.getActiveWorkbenchShell();
+ }
+
+ private ILaunchManager getLaunchManager() {
+ return DebugPlugin.getDefault().getLaunchManager();
+ }
+
+ /**
+ * Show a selection dialog that allows the user to choose one of the specified
+ * launch configurations. Return the chosen config, or <code>null</code> if the
+ * user cancelled the dialog.
+ *
+ * @param configList list of {@link ILaunchConfiguration}s
+ * @param mode launch mode
+ * @return ILaunchConfiguration
+ * @throws InterruptedException if cancelled by the user
+ */
+ private ILaunchConfiguration chooseConfiguration(List<ILaunchConfiguration> configList, String mode)
+ throws InterruptedException {
+ IDebugModelPresentation labelProvider = DebugUITools.newDebugModelPresentation();
+ ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), labelProvider);
+ dialog.setElements(configList.toArray());
+ dialog.setTitle(Messages.UnitTestLaunchShortcut_message_selectConfiguration);
+ if (mode.equals(ILaunchManager.DEBUG_MODE)) {
+ dialog.setMessage(Messages.UnitTestLaunchShortcut_message_selectDebugConfiguration);
+ } else {
+ dialog.setMessage(Messages.UnitTestLaunchShortcut_message_selectRunConfiguration);
+ }
+ dialog.setMultipleSelection(false);
+ int result = dialog.open();
+ if (result == Window.OK) {
+ return (ILaunchConfiguration) dialog.getFirstResult();
+ }
+ throw new InterruptedException(); // cancelled by user
+ }
+
+ /**
+ * Returns the launch configuration type id of the launch configuration this
+ * shortcut will create. Clients can override this method to return the id of
+ * their launch configuration.
+ *
+ * @return the launch configuration type id of the launch configuration this
+ * shortcut will create
+ */
+ protected String getLaunchConfigurationTypeId() {
+ // must match extension in plugin.xml
+ return JUnitTestPlugin.PLUGIN_ID + ".launchConfiguration"; //$NON-NLS-1$
+ }
+
+ /**
+ * Creates a launch configuration working copy for the given element. The launch
+ * configuration type created will be of the type returned by
+ * {@link #getLaunchConfigurationTypeId}. The element type can only be of type
+ * {@link IJavaProject}, {@link IPackageFragmentRoot}, {@link IPackageFragment},
+ * {@link IType} or {@link IMethod}.
+ *
+ * <p>
+ * Clients can extend this method (should call super) to configure additional
+ * attributes on the launch configuration working copy. Note that this method
+ * calls
+ * <code>{@link #createLaunchConfiguration(IJavaElement, String) createLaunchConfiguration}(element, null)</code>.
+ * Extenders are recommended to extend the two-args method instead of this
+ * method.
+ * </p>
+ *
+ * @param element element to launch
+ *
+ * @return a launch configuration working copy for the given element
+ * @throws CoreException if creation failed
+ */
+ protected ILaunchConfigurationWorkingCopy createLaunchConfiguration(IJavaElement element) throws CoreException {
+ return createLaunchConfiguration(element, null);
+ }
+
+ /**
+ * Creates a launch configuration working copy for the given element. The launch
+ * configuration type created will be of the type returned by
+ * {@link #getLaunchConfigurationTypeId}. The element type can only be of type
+ * {@link IJavaProject}, {@link IPackageFragmentRoot}, {@link IPackageFragment},
+ * {@link IType} or {@link IMethod}.
+ *
+ * <p>
+ * Clients can extend this method (should call super) to configure additional
+ * attributes on the launch configuration working copy.
+ * </p>
+ *
+ * @param element element to launch
+ * @param testName name of the test to launch, e.g. the method name or an
+ * artificial name created by a JUnit runner, or
+ * <code>null</code> if none. The testName is ignored if the
+ * element is an IMethod; the method name is used in that case.
+ *
+ * @return a launch configuration working copy for the given element
+ * @throws CoreException if creation failed
+ */
+ protected ILaunchConfigurationWorkingCopy createLaunchConfiguration(IJavaElement element, String testName)
+ throws CoreException {
+ final String mainTypeQualifiedName;
+ final String containerHandleId;
+
+ switch (element.getElementType()) {
+ case IJavaElement.JAVA_PROJECT:
+ case IJavaElement.PACKAGE_FRAGMENT_ROOT:
+ case IJavaElement.PACKAGE_FRAGMENT: {
+ containerHandleId = element.getHandleIdentifier();
+ mainTypeQualifiedName = EMPTY_STRING;
+ break;
+ }
+ case IJavaElement.TYPE: {
+ containerHandleId = EMPTY_STRING;
+ mainTypeQualifiedName = ((IType) element).getFullyQualifiedName('.'); // don't replace, fix for binary inner
+ // types
+ break;
+ }
+ case IJavaElement.METHOD: {
+ IMethod method = (IMethod) element;
+ testName = method.getElementName(); // Test-names can not be specified when launching a Java method.
+ testName += JUnitStubUtility.getParameterTypes(method, false);
+ containerHandleId = EMPTY_STRING;
+ IType declaringType = method.getDeclaringType();
+ mainTypeQualifiedName = declaringType.getFullyQualifiedName('.');
+ break;
+ }
+ default:
+ throw new IllegalArgumentException(
+ "Invalid element type to create a launch configuration: " + element.getClass().getName()); //$NON-NLS-1$
+ }
+
+ ILaunchConfigurationType configType = getLaunchManager()
+ .getLaunchConfigurationType(getLaunchConfigurationTypeId());
+ String configName = getLaunchManager()
+ .generateLaunchConfigurationName(suggestLaunchConfigurationName(element, testName));
+ ILaunchConfigurationWorkingCopy wc = configType.newInstance(null, configName);
+
+ wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, mainTypeQualifiedName);
+ wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, element.getJavaProject().getElementName());
+ /*
+ * As AbstractJavaLaunchConfigurationDelegate.getVMSpecificAttributesMap(
+ * ILaunchConfiguration) method works with Java Launch, it requires
+ * `IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME` to be set, while we
+ * operate only with `UnitTestLaunchConfigurationConstants.ATTR_PROJECT_NAME`.
+ * So, copy our project name property to `'JavaLaunch...`-one in order tp
+ * satisfy `getVMSpecificAttributesMap()`
+ */
+ wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, element.getJavaProject().getElementName());
+
+ wc.setAttribute(JUnitLaunchConfigurationConstants.ATTR_KEEPRUNNING, false);
+ wc.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, containerHandleId);
+ wc.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND,
+ JUnitTestPlugin.getJUnitVersion(element).getJUnitTestKind().getId());
+ new ConfigureViewerSupport(JUnitTestPlugin.UNIT_TEST_VIEW_SUPPORT_ID).apply(wc);
+ JUnitMigrationDelegate.mapResources(wc);
+ AssertionVMArg.setArgDefault(wc);
+ if (testName != null) {
+ wc.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_NAME, testName);
+ }
+ boolean isRunWithJUnitPlatform = JUnitTestPlugin.isRunWithJUnitPlatform(element);
+ if (isRunWithJUnitPlatform) {
+ wc.setAttribute(JUnitLaunchConfigurationConstants.ATTR_RUN_WITH_JUNIT_PLATFORM_ANNOTATION, true);
+ }
+ return wc;
+ }
+
+ /**
+ * Computes a human-readable name for a launch configuration. The name serves as
+ * a suggestion and it's the caller's responsibility to make it valid and
+ * unique.
+ *
+ * @param element The Java Element that will be executed.
+ * @param fullTestName The test name. See
+ * org.eclipse.jdt.internal.junit4.runner.DescriptionMatcher
+ * for supported formats.
+ * @return The suggested name for the launch configuration.
+ */
+ protected String suggestLaunchConfigurationName(IJavaElement element, String fullTestName) {
+ switch (element.getElementType()) {
+ case IJavaElement.JAVA_PROJECT:
+ case IJavaElement.PACKAGE_FRAGMENT_ROOT:
+ case IJavaElement.PACKAGE_FRAGMENT:
+ String name = JavaElementLabels.getTextLabel(element, JavaElementLabels.ALL_FULLY_QUALIFIED);
+ return name.substring(name.lastIndexOf(IPath.SEPARATOR) + 1);
+ case IJavaElement.TYPE:
+ if (fullTestName != null) {
+ Matcher matcher = METHOD_AND_CLASS_NAME_PATTERN.matcher(fullTestName);
+ if (matcher.matches()) {
+ String typeFQN = matcher.group(2);
+ String testName = matcher.group(1);
+ int typeFQNDot = typeFQN.lastIndexOf('.');
+ String typeName = typeFQNDot >= 0 ? typeFQN.substring(typeFQNDot + 1) : typeFQN;
+ return typeName + " " + testName; //$NON-NLS-1$
+ }
+ return element.getElementName() + " " + fullTestName;//$NON-NLS-1$
+ }
+ return element.getElementName();
+ case IJavaElement.METHOD:
+ IMethod method = (IMethod) element;
+ String methodName = method.getElementName();
+ methodName += JUnitStubUtility.getParameterTypes(method, true); // use simple names of parameter types
+ return method.getDeclaringType().getElementName() + '.' + methodName;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid element type to create a launch configuration: " + element.getClass().getName()); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Returns the attribute names of the attributes that are compared when looking
+ * for an existing similar launch configuration. Clients can override and
+ * replace to customize.
+ *
+ * @return the attribute names of the attributes that are compared
+ */
+ protected String[] getAttributeNamesToCompare() {
+ return new String[] { IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, //
+ JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, //
+ IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, //
+ JUnitLaunchConfigurationConstants.ATTR_TEST_NAME };
+ }
+
+ private static boolean hasSameAttributes(ILaunchConfiguration config1, ILaunchConfiguration config2,
+ String[] attributeToCompare) {
+ try {
+ for (String element : attributeToCompare) {
+ String val1 = config1.getAttribute(element, EMPTY_STRING);
+ String val2 = config2.getAttribute(element, EMPTY_STRING);
+ if (!val1.equals(val2)) {
+ return false;
+ }
+ }
+ return true;
+ } catch (CoreException e) {
+ // ignore access problems here, return false
+ }
+ return false;
+ }
+
+ private ILaunchConfiguration findExistingLaunchConfiguration(ILaunchConfigurationWorkingCopy temporary, String mode)
+ throws InterruptedException, CoreException {
+ List<ILaunchConfiguration> candidateConfigs = findExistingLaunchConfigurations(temporary);
+
+ // If there are no existing configs associated with the IType, create
+ // one.
+ // If there is exactly one config associated with the IType, return it.
+ // Otherwise, if there is more than one config associated with the
+ // IType, prompt the
+ // user to choose one.
+ int candidateCount = candidateConfigs.size();
+ switch (candidateCount) {
+ case 0:
+ return null;
+ case 1:
+ return candidateConfigs.get(0);
+ default:
+ // Prompt the user to choose a config. A null result means the user
+ // cancelled the dialog, in which case this method returns null,
+ // since cancelling the dialog should also cancel launching
+ // anything.
+ ILaunchConfiguration config = chooseConfiguration(candidateConfigs, mode);
+ if (config != null) {
+ return config;
+ }
+ break;
+ }
+ return null;
+ }
+
+ private List<ILaunchConfiguration> findExistingLaunchConfigurations(ILaunchConfigurationWorkingCopy temporary)
+ throws CoreException {
+ ILaunchConfigurationType configType = temporary.getType();
+
+ ILaunchConfiguration[] configs = getLaunchManager().getLaunchConfigurations(configType);
+ String[] attributeToCompare = getAttributeNamesToCompare();
+
+ ArrayList<ILaunchConfiguration> candidateConfigs = new ArrayList<>(configs.length);
+ for (ILaunchConfiguration config : configs) {
+ if (hasSameAttributes(config, temporary, attributeToCompare)) {
+ candidateConfigs.add(config);
+ }
+ }
+ return candidateConfigs;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ILaunchConfiguration[] getLaunchConfigurations(ISelection selection) {
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss = (IStructuredSelection) selection;
+ if (ss.size() == 1) {
+ return findExistingLaunchConfigurations(ss.getFirstElement());
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ILaunchConfiguration[] getLaunchConfigurations(final IEditorPart editor) {
+ final ITypeRoot element = JavaUI.getEditorInputTypeRoot(editor.getEditorInput());
+ if (element != null) {
+ IMember selectedMember = null;
+ if (Display.getCurrent() == null) {
+ final AtomicReference<IMember> temp = new AtomicReference<>();
+ Runnable runnable = () -> temp.set(resolveSelectedMemberName(editor, element));
+ Display.getDefault().syncExec(runnable);
+ selectedMember = temp.get();
+ } else {
+ selectedMember = resolveSelectedMemberName(editor, element);
+ }
+ Object candidate = element;
+ if (selectedMember != null) {
+ candidate = selectedMember;
+ }
+ return findExistingLaunchConfigurations(candidate);
+ }
+ return null;
+ }
+
+ private ILaunchConfiguration[] findExistingLaunchConfigurations(Object candidate) {
+ if (!(candidate instanceof IJavaElement) && candidate instanceof IAdaptable) {
+ candidate = ((IAdaptable) candidate).getAdapter(IJavaElement.class);
+ }
+ if (candidate instanceof IJavaElement) {
+ IJavaElement element = (IJavaElement) candidate;
+ IJavaElement elementToLaunch = null;
+ try {
+ switch (element.getElementType()) {
+ case IJavaElement.JAVA_PROJECT:
+ case IJavaElement.PACKAGE_FRAGMENT_ROOT:
+ case IJavaElement.PACKAGE_FRAGMENT:
+ case IJavaElement.TYPE:
+ case IJavaElement.METHOD:
+ elementToLaunch = element;
+ break;
+ case IJavaElement.CLASS_FILE:
+ if (element instanceof IOrdinaryClassFile)
+ elementToLaunch = ((IOrdinaryClassFile) element).getType();
+ break;
+ case IJavaElement.COMPILATION_UNIT:
+ elementToLaunch = ((ICompilationUnit) element).findPrimaryType();
+ break;
+ }
+ if (elementToLaunch == null) {
+ return null;
+ }
+ ILaunchConfigurationWorkingCopy workingCopy = createLaunchConfiguration(elementToLaunch);
+ List<ILaunchConfiguration> list = findExistingLaunchConfigurations(workingCopy);
+ return list.toArray(new ILaunchConfiguration[list.size()]);
+ } catch (CoreException e) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IResource getLaunchableResource(ISelection selection) {
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss = (IStructuredSelection) selection;
+ if (ss.size() == 1) {
+ Object selected = ss.getFirstElement();
+ if (!(selected instanceof IJavaElement) && selected instanceof IAdaptable) {
+ selected = ((IAdaptable) selected).getAdapter(IJavaElement.class);
+ }
+ if (selected instanceof IJavaElement) {
+ return ((IJavaElement) selected).getResource();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IResource getLaunchableResource(IEditorPart editor) {
+ ITypeRoot element = JavaUI.getEditorInputTypeRoot(editor.getEditorInput());
+ if (element != null) {
+ try {
+ return element.getCorrespondingResource();
+ } catch (JavaModelException e) {
+ }
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitRemoteTestRunnerClient.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitRemoteTestRunnerClient.java
new file mode 100644
index 0000000000..11d69d7c5c
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitRemoteTestRunnerClient.java
@@ -0,0 +1,543 @@
+/*******************************************************************************
+ * 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
+ * Julien Ruaux: jruaux@octo.com
+ * Vincent Massol: vmassol@octo.com
+ *******************************************************************************/
+package org.eclipse.jdt.ui.unittest.junit.launcher;
+
+import java.time.Duration;
+import java.util.Arrays;
+
+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.core.runtime.ISafeRunnable;
+
+import org.eclipse.debug.core.ILaunch;
+
+import org.eclipse.jdt.internal.junit.runner.MessageIds;
+import org.eclipse.jdt.internal.junit.runner.RemoteTestRunner;
+
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin;
+import org.eclipse.jdt.ui.unittest.junit.internal.launcher.RemoteTestRunnerClient;
+
+/**
+ * The client side of the RemoteTestRunner. Handles the marshaling of the
+ * different messages.
+ */
+public class JUnitRemoteTestRunnerClient extends RemoteTestRunnerClient {
+ public JUnitRemoteTestRunnerClient(int port, ITestRunSession session) {
+ super(port, session);
+ }
+
+ public abstract class ListenerSafeRunnable implements ISafeRunnable {
+ @Override
+ public void handleException(Throwable exception) {
+ JUnitTestPlugin.log(exception);
+ }
+ }
+
+ /**
+ * A simple state machine to process requests from the RemoteTestRunner
+ */
+ abstract class ProcessingState {
+ abstract ProcessingState readMessage(String message);
+ }
+
+ class DefaultProcessingState extends ProcessingState {
+ @Override
+ ProcessingState readMessage(String message) {
+ if (fDebug) {
+ System.out.println("JUnitRemoteTestRunnerClient.DefaultProcessingState.readMessage: " + message); //$NON-NLS-1$
+ }
+
+ if (message.startsWith(MessageIds.TRACE_START)) {
+ fFailedTrace.setLength(0);
+ return fTraceState;
+ }
+ if (message.startsWith(MessageIds.EXPECTED_START)) {
+ fExpectedResult.setLength(0);
+ return fExpectedState;
+ }
+ if (message.startsWith(MessageIds.ACTUAL_START)) {
+ fActualResult.setLength(0);
+ return fActualState;
+ }
+ if (message.startsWith(MessageIds.RTRACE_START)) {
+ fFailedRerunTrace.setLength(0);
+ return fRerunState;
+ }
+ String arg = message.substring(MessageIds.MSG_HEADER_LENGTH);
+ if (message.startsWith(MessageIds.TEST_RUN_START)) {
+ // version < 2 format: count
+ // version >= 2 format: count+" "+version
+ int count = 0;
+ int v = arg.indexOf(' ');
+ if (v == -1) {
+ fVersion = "v1"; //$NON-NLS-1$
+ count = Integer.parseInt(arg);
+ } else {
+ fVersion = arg.substring(v + 1);
+ String sc = arg.substring(0, v);
+ count = Integer.parseInt(sc);
+ }
+ fTestRunSession.notifyTestSessionStarted(count);
+ return this;
+ }
+ if (message.startsWith(MessageIds.TEST_START)) {
+ String s[] = extractTestId(arg);
+ ITestElement test = fTestRunSession.getTestElement(s[0]);
+ fTestRunSession.notifyTestStarted(test);
+ return this;
+ }
+ if (message.startsWith(MessageIds.TEST_END)) {
+ String s[] = extractTestId(arg);
+ boolean isIgnored = s[1].startsWith(MessageIds.IGNORED_TEST_PREFIX);
+ ITestElement testElement = fTestRunSession.getTestElement(s[0]);
+ fTestRunSession.notifyTestEnded(testElement, isIgnored);
+ return this;
+ }
+ if (message.startsWith(MessageIds.TEST_ERROR)) {
+ String s[] = extractTestId(arg);
+ ITestElement testElement = fTestRunSession.getTestElement(s[0]);
+ boolean isAssumptionFailed = s[1].startsWith(MessageIds.ASSUMPTION_FAILED_TEST_PREFIX);
+ extractFailure(testElement, Result.ERROR, isAssumptionFailed);
+ return this;
+ }
+ if (message.startsWith(MessageIds.TEST_FAILED)) {
+ String s[] = extractTestId(arg);
+ ITestElement testElement = fTestRunSession.getTestElement(s[0]);
+ boolean isAssumptionFailed = s[1].startsWith(MessageIds.ASSUMPTION_FAILED_TEST_PREFIX);
+ extractFailure(testElement, Result.FAILURE, isAssumptionFailed);
+ return this;
+ }
+ if (message.startsWith(MessageIds.TEST_RUN_END)) {
+ fTestRunSession.notifyTestSessionCompleted(Duration.ofMillis(Long.parseLong(arg)));
+ return this;
+ }
+ if (message.startsWith(MessageIds.TEST_STOPPED)) {
+ fTestRunSession.notifyTestSessionAborted(Duration.ofMillis(Long.parseLong(arg)), null);
+ shutDown();
+ return this;
+ }
+ if (message.startsWith(MessageIds.TEST_TREE)) {
+ notifyTestTreeEntry(arg);
+ return this;
+ }
+ if (message.startsWith(MessageIds.TEST_RERAN)) {
+ if (hasTestId())
+ scanReranMessage(arg);
+ else
+ scanOldReranMessage(arg);
+ return this;
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Base class for states in which messages are appended to an internal string
+ * buffer until an end message is read.
+ */
+ class AppendingProcessingState extends ProcessingState {
+ private final StringBuilder fBuffer;
+ private String fEndString;
+
+ AppendingProcessingState(StringBuilder buffer, String endString) {
+ this.fBuffer = buffer;
+ this.fEndString = endString;
+ }
+
+ @Override
+ ProcessingState readMessage(String message) {
+ if (message.startsWith(fEndString)) {
+ entireStringRead();
+ return fDefaultState;
+ }
+ fBuffer.append(message);
+ if (fLastLineDelimiter != null)
+ fBuffer.append(fLastLineDelimiter);
+ return this;
+ }
+
+ /**
+ * subclasses can override to do special things when end message is read
+ */
+ void entireStringRead() {
+ // Nothing to do
+ }
+ }
+
+ class TraceProcessingState extends AppendingProcessingState {
+ TraceProcessingState() {
+ super(fFailedTrace, MessageIds.TRACE_END);
+ }
+
+ @Override
+ void entireStringRead() {
+ fTestRunSession.notifyTestFailed(fFailedTest, fFailureKind, fFailedAssumption, new FailureTrace(
+ fFailedTrace.toString(), nullifyEmpty(fExpectedResult), nullifyEmpty(fActualResult)));
+ fExpectedResult.setLength(0);
+ fActualResult.setLength(0);
+ }
+
+ @Override
+ ProcessingState readMessage(String message) {
+ if (message.startsWith(MessageIds.TRACE_END)) {
+ fTestRunSession.notifyTestFailed(fFailedTest, fFailureKind, fFailedAssumption, new FailureTrace(
+ fFailedTrace.toString(), nullifyEmpty(fExpectedResult), nullifyEmpty(fActualResult)));
+ fFailedTrace.setLength(0);
+ fActualResult.setLength(0);
+ fExpectedResult.setLength(0);
+ return fDefaultState;
+ }
+ fFailedTrace.append(message);
+ if (fLastLineDelimiter != null)
+ fFailedTrace.append(fLastLineDelimiter);
+ return this;
+ }
+
+ /**
+ * Returns a comparison result from the given buffer. Removes the terminating
+ * line delimiter.
+ *
+ * @param buf the comparison result
+ * @return the result or <code>null</code> if empty
+ */
+ }
+
+ private ITestElement fFailedTest;
+ /**
+ * The kind of failure of the test that is currently reported as failed
+ */
+ private Result fFailureKind;
+ /**
+ * Is Assumption failed on failed test
+ */
+ private boolean fFailedAssumption;
+ /**
+ * The failed trace that is currently reported from the RemoteTestRunner
+ */
+ private final StringBuilder fFailedTrace = new StringBuilder();
+ /**
+ * The expected test result
+ */
+ private final StringBuilder fExpectedResult = new StringBuilder();
+ /**
+ * The actual test result
+ */
+ private final StringBuilder fActualResult = new StringBuilder();
+ /**
+ * The failed trace of a reran test
+ */
+ private final StringBuilder fFailedRerunTrace = new StringBuilder();
+ private ITestSuiteElement currentSuite;
+
+ ProcessingState fDefaultState = new DefaultProcessingState();
+ ProcessingState fTraceState = new TraceProcessingState();
+ ProcessingState fExpectedState = new AppendingProcessingState(fExpectedResult, MessageIds.EXPECTED_END);
+ ProcessingState fActualState = new AppendingProcessingState(fActualResult, MessageIds.ACTUAL_END);
+ ProcessingState fRerunState = new AppendingProcessingState(fFailedRerunTrace, MessageIds.RTRACE_END);
+ ProcessingState fCurrentState = fDefaultState;
+
+ /**
+ * An array of listeners that are informed about test events.
+ */
+// private ITestRunListener2[] fListeners;
+
+ /**
+ * The server socket
+ */
+ /*
+ * private ServerSocket fServerSocket; private Socket fSocket; private int
+ * fPort= -1; private PrintWriter fWriter; private PushbackReader
+ * fPushbackReader; private String fLastLineDelimiter;
+ */
+ /**
+ * The protocol version
+ */
+// private String fVersion;
+
+// private boolean fDebug= false;
+
+ /**
+ * Requests to stop the remote test run.
+ */
+ @Override
+ public synchronized void stopTest() {
+ fWriter.println(MessageIds.TEST_STOP);
+ fWriter.flush();
+ ILaunch launch = fTestRunSession.getLaunch();
+ try {
+ launch.terminate();
+ } catch (Exception ex) {
+ JUnitTestPlugin.log(ex);
+ }
+ }
+
+ @Override
+ public void receiveMessage(String message) {
+ fCurrentState = fCurrentState.readMessage(message);
+ }
+
+ private void scanOldReranMessage(String arg) {
+ // OLD V1 format
+ // format: className" "testName" "status
+ // status: FAILURE, ERROR, OK
+ int c = arg.indexOf(" "); //$NON-NLS-1$
+ int t = arg.indexOf(" ", c + 1); //$NON-NLS-1$
+ String className = arg.substring(0, c);
+ String testName = arg.substring(c + 1, t);
+ String status = arg.substring(t + 1);
+ String testId = className + testName;
+ notifyTestReran(testId, className, testName, status);
+ }
+
+ private void scanReranMessage(String arg) {
+ // format: testId" "className" "testName" "status
+ // status: FAILURE, ERROR, OK
+ int i = arg.indexOf(' ');
+ int c = arg.indexOf(' ', i + 1);
+ int t; // special treatment, since testName can contain spaces:
+ if (arg.endsWith(RemoteTestRunner.RERAN_ERROR)) {
+ t = arg.length() - RemoteTestRunner.RERAN_ERROR.length() - 1;
+ } else if (arg.endsWith(RemoteTestRunner.RERAN_FAILURE)) {
+ t = arg.length() - RemoteTestRunner.RERAN_FAILURE.length() - 1;
+ } else if (arg.endsWith(RemoteTestRunner.RERAN_OK)) {
+ t = arg.length() - RemoteTestRunner.RERAN_OK.length() - 1;
+ } else {
+ t = arg.indexOf(' ', c + 1);
+ }
+ String testId = arg.substring(0, i);
+ String className = arg.substring(i + 1, c);
+ String testName = arg.substring(c + 1, t);
+ String status = arg.substring(t + 1);
+ notifyTestReran(testId, className, testName, status);
+ }
+
+ /*
+ * private void notifyTestReran(String testId, String className, String
+ * testName, String status) { int statusCode= ITestRunListener2.STATUS_OK; if
+ * (status.equals("FAILURE")) //$NON-NLS-1$ statusCode=
+ * ITestRunListener2.STATUS_FAILURE; else if (status.equals("ERROR"))
+ * //$NON-NLS-1$ statusCode= ITestRunListener2.STATUS_ERROR;
+ *
+ * String trace= ""; //$NON-NLS-1$ if (statusCode !=
+ * ITestRunListener2.STATUS_OK) trace = fFailedRerunTrace.toString(); //
+ * assumption a rerun trace was sent before notifyTestReran(testId, className,
+ * testName, statusCode, trace); }
+ *
+ * @Override protected void extractFailure(String testId, String testName, int
+ * status) { fFailedTestId= testId; fFailedTest= testName; fFailureKind= status;
+ * }
+ */
+ /**
+ * @param arg test name
+ * @return an array with two elements. The first one is the testId, the second
+ * one the testName.
+ */
+ protected String[] extractTestId(String arg) {
+ String[] result = new String[2];
+ if (!hasTestId()) {
+ result[0] = arg; // use the test name as the test Id
+ result[1] = arg;
+ return result;
+ }
+ int i = arg.indexOf(',');
+ result[0] = arg.substring(0, i);
+ result[1] = arg.substring(i + 1, arg.length());
+ return result;
+ }
+
+ protected boolean hasTestId() {
+ if (fVersion == null) // TODO fix me
+ return true;
+ return fVersion.equals("v2"); //$NON-NLS-1$
+ }
+
+ /*
+ * private void notifyTestReran(final String testId, final String className,
+ * final String testName, final int statusCode, final String trace) { for
+ * (ITestRunListener2 listener : fListeners) { SafeRunner.run(new
+ * ListenerSafeRunnable() {
+ *
+ * @Override public void run() { listener.testReran(testId, className, testName,
+ * statusCode, trace, nullifyEmpty(fExpectedResult),
+ * nullifyEmpty(fActualResult)); } }); } }
+ */
+ private void notifyTestTreeEntry(final String treeEntry) {
+ // format:
+ // testId","testName","isSuite","testcount","isDynamicTest","parentId","displayName","parameterTypes","uniqueId
+ String fixedTreeEntry = hasTestId() ? treeEntry : fakeTestId(treeEntry);
+
+ int index0 = fixedTreeEntry.indexOf(',');
+ String id = fixedTreeEntry.substring(0, index0);
+
+ StringBuilder testNameBuffer = new StringBuilder(100);
+ int index1 = scanTestName(fixedTreeEntry, index0 + 1, testNameBuffer);
+ String testName = testNameBuffer.toString().trim();
+
+ int index2 = fixedTreeEntry.indexOf(',', index1 + 1);
+ boolean isSuite = fixedTreeEntry.substring(index1 + 1, index2).equals("true"); //$NON-NLS-1$
+
+ int testCount;
+// boolean isDynamicTest;
+ String parentId;
+ String displayName;
+ StringBuilder displayNameBuffer = new StringBuilder(100);
+ String[] parameterTypes;
+ StringBuilder parameterTypesBuffer = new StringBuilder(200);
+ String uniqueId;
+ StringBuilder uniqueIdBuffer = new StringBuilder(200);
+ int index3 = fixedTreeEntry.indexOf(',', index2 + 1);
+ if (index3 == -1) {
+ testCount = Integer.parseInt(fixedTreeEntry.substring(index2 + 1));
+// isDynamicTest = false;
+ parentId = null;
+ displayName = null;
+ parameterTypes = null;
+ uniqueId = null;
+ } else {
+ testCount = Integer.parseInt(fixedTreeEntry.substring(index2 + 1, index3));
+
+ int index4 = fixedTreeEntry.indexOf(',', index3 + 1);
+// isDynamicTest = Boolean.parseBoolean(fixedTreeEntry.substring(index3 + 1, index4));
+
+ int index5 = fixedTreeEntry.indexOf(',', index4 + 1);
+ parentId = fixedTreeEntry.substring(index4 + 1, index5);
+ if (parentId.equals("-1")) { //$NON-NLS-1$
+ parentId = null;
+ }
+
+ int index6 = scanTestName(fixedTreeEntry, index5 + 1, displayNameBuffer);
+ displayName = displayNameBuffer.toString().trim();
+ if (displayName.equals(testName)) {
+ displayName = null;
+ }
+
+ int index7 = scanTestName(fixedTreeEntry, index6 + 1, parameterTypesBuffer);
+ String parameterTypesString = parameterTypesBuffer.toString().trim();
+ if (parameterTypesString.isEmpty()) {
+ parameterTypes = null;
+ } else {
+ parameterTypes = parameterTypesString.split(","); //$NON-NLS-1$
+ Arrays.parallelSetAll(parameterTypes, i -> parameterTypes[i].trim());
+ }
+
+ scanTestName(fixedTreeEntry, index7 + 1, uniqueIdBuffer);
+ uniqueId = uniqueIdBuffer.toString().trim();
+ if (uniqueId.isEmpty()) {
+ uniqueId = null;
+ }
+ }
+
+ ITestSuiteElement parent = getTestSuite(parentId);
+ if (parent == null && currentSuite != null) {
+ parent = currentSuite;
+ }
+ if (isSuite) {
+ currentSuite = fTestRunSession.newTestSuite(id, testName, Integer.valueOf(testCount), parent, displayName,
+ uniqueId);
+ } else {
+ fTestRunSession.newTestCase(id, testName, parent, displayName, uniqueId);
+ }
+ }
+
+ private ITestSuiteElement getTestSuite(String parentId) {
+ ITestElement element = fTestRunSession.getTestElement(parentId);
+ return element instanceof ITestSuiteElement ? (ITestSuiteElement) element : null;
+ }
+
+ /**
+ * Append the test name from <code>s</code> to <code>testName</code>.
+ *
+ * @param s the string to scan
+ * @param start the offset of the first character in <code>s</code>
+ * @param testName the result
+ *
+ * @return the index of the next ','
+ */
+ private int scanTestName(String s, int start, StringBuilder testName) {
+ boolean inQuote = false;
+ int i = start;
+ for (; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c == '\\' && !inQuote) {
+ inQuote = true;
+ continue;
+ } else if (inQuote) {
+ inQuote = false;
+ testName.append(c);
+ } else if (c == ',')
+ break;
+ else
+ testName.append(c);
+ }
+ return i;
+ }
+
+ private String fakeTestId(String treeEntry) {
+ // extract the test name and add it as the testId
+ int index0 = treeEntry.indexOf(',');
+ String testName = treeEntry.substring(0, index0).trim();
+ return testName + "," + treeEntry; //$NON-NLS-1$
+ }
+
+ private void extractFailure(ITestElement failedTest, Result status, boolean isAssumptionFailed) {
+ fFailedTest = failedTest;
+ fFailureKind = status;
+ fFailedAssumption = isAssumptionFailed;
+ }
+
+ private void notifyTestReran(String testId, String className, String testName, String status) {
+ Result statusCode = Result.OK;
+ if (status.equals("FAILURE")) { //$NON-NLS-1$
+ statusCode = Result.FAILURE;
+ } else if (status.equals("ERROR")) { //$NON-NLS-1$
+ statusCode = Result.ERROR;
+ }
+
+ String trace = ""; //$NON-NLS-1$
+ if (statusCode != Result.OK)
+ trace = fFailedRerunTrace.toString();
+ // assumption a rerun trace was sent before
+
+ ITestCaseElement element = fTestRunSession.newTestCase(testId, testName, null, testName, className);
+ if (statusCode != Result.OK) {
+ fTestRunSession.notifyTestFailed(element, statusCode, false,
+ new FailureTrace(trace, nullifyEmpty(fExpectedResult), nullifyEmpty(fActualResult)));
+ }
+ fTestRunSession.notifyTestEnded(element, false);
+ }
+
+ private static String nullifyEmpty(StringBuilder buf) {
+ int length = buf.length();
+ if (length == 0)
+ return null;
+
+ char last = buf.charAt(length - 1);
+ if (last == '\n') {
+ if (length > 1 && buf.charAt(length - 2) == '\r')
+ return buf.substring(0, length - 2);
+ else
+ return buf.substring(0, length - 1);
+ } else if (last == '\r') {
+ return buf.substring(0, length - 1);
+ }
+ return buf.toString();
+ }
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitTestKindUtil.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitTestKindUtil.java
new file mode 100644
index 0000000000..dc2f223ebf
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitTestKindUtil.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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
+ * David Saff (saff@mit.edu) - initial API and implementation
+ * (bug 102632: [JUnit] Support for JUnit 4.)
+ *******************************************************************************/
+
+package org.eclipse.jdt.ui.unittest.junit.launcher;
+
+import org.eclipse.jdt.core.IAnnotation;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMemberValuePair;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+
+import org.eclipse.jdt.internal.junit.util.CoreTestSearchEngine;
+
+class JUnitTestKindUtil {
+ public static final String JUNIT3_TEST_KIND_ID = "org.eclipse.jdt.ui.unittest.junit.loader.junit3"; //$NON-NLS-1$
+ public static final String JUNIT4_TEST_KIND_ID = "org.eclipse.jdt.ui.unittest.junit.loader.junit4"; //$NON-NLS-1$
+ public static final String JUNIT5_TEST_KIND_ID = "org.eclipse.jdt.ui.unittest.junit.loader.junit5"; //$NON-NLS-1$
+
+ private JUnitTestKindUtil() {
+ }
+
+ public static String getContainerTestKindId(IJavaElement element) {
+ if (element != null) {
+ IJavaProject project = element.getJavaProject();
+ if (CoreTestSearchEngine.is50OrHigher(project)) {
+ if (CoreTestSearchEngine.is18OrHigher(project)) {
+ if (isRunWithJUnitPlatform(element)) {
+ return JUNIT4_TEST_KIND_ID;
+ }
+ if (CoreTestSearchEngine.hasJUnit5TestAnnotation(project)) {
+ return JUNIT5_TEST_KIND_ID;
+ }
+ }
+ if (CoreTestSearchEngine.hasJUnit4TestAnnotation(project)) {
+ return JUNIT4_TEST_KIND_ID;
+ }
+ }
+ }
+ return JUNIT3_TEST_KIND_ID;
+ }
+
+ /**
+ * @param element the element
+ * @return <code>true</code> if the element is a test class annotated with
+ * <code>@RunWith(JUnitPlatform.class)</code>
+ */
+ public static boolean isRunWithJUnitPlatform(IJavaElement element) {
+ if (element instanceof ICompilationUnit) {
+ element = ((ICompilationUnit) element).findPrimaryType();
+ }
+ if (element instanceof IType) {
+ IType type = (IType) element;
+ try {
+ IAnnotation runWithAnnotation = type.getAnnotation("RunWith"); //$NON-NLS-1$
+ if (!runWithAnnotation.exists()) {
+ runWithAnnotation = type.getAnnotation("org.junit.runner.RunWith"); //$NON-NLS-1$
+ }
+ if (runWithAnnotation.exists()) {
+ IMemberValuePair[] memberValuePairs = runWithAnnotation.getMemberValuePairs();
+ for (IMemberValuePair memberValuePair : memberValuePairs) {
+ if (memberValuePair.getMemberName().equals("value") //$NON-NLS-1$
+ && memberValuePair.getValue().equals("JUnitPlatform")) { //$NON-NLS-1$
+ return true;
+ }
+ }
+ }
+ } catch (JavaModelException e) {
+ // ignore
+ }
+ }
+ return false;
+ }
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/Messages.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/Messages.java
new file mode 100644
index 0000000000..b0c47be932
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/Messages.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * 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.jdt.ui.unittest.junit.launcher;
+
+import org.eclipse.osgi.util.NLS;
+
+public final class Messages extends NLS {
+
+ private static final String BUNDLE_NAME = "org.eclipse.unittest.ui.Messages";//$NON-NLS-1$
+
+ public static String JUnitLaunchConfigurationTab_error_invalidProjectName;
+
+ public static String JUnitLaunchConfigurationTab_error_JDK15_required;
+
+ public static String JUnitLaunchConfigurationTab_error_JDK18_required;
+
+ public static String JUnitLaunchConfigurationTab_error_noContainer;
+
+ public static String JUnitLaunchConfigurationTab_error_notJavaProject;
+
+ public static String JUnitLaunchConfigurationTab_error_projectnotdefined;
+
+ public static String JUnitLaunchConfigurationTab_error_projectnotexists;
+
+ public static String JUnitLaunchConfigurationTab_error_test_class_not_found;
+
+ public static String JUnitLaunchConfigurationTab_error_test_method_not_found;
+
+ public static String JUnitLaunchConfigurationTab_error_testannotationnotonpath;
+
+ public static String JUnitLaunchConfigurationTab_error_testcasenotonpath;
+
+ public static String JUnitLaunchConfigurationTab_error_testnotdefined;
+
+ public static String JUnitLaunchConfigurationTab_folderdialog_message;
+
+ public static String JUnitLaunchConfigurationTab_folderdialog_title;
+
+ public static String JUnitLaunchConfigurationTab_label_browse;
+
+ public static String JUnitLaunchConfigurationTab_label_containerTest;
+
+ public static String JUnitLaunchConfigurationTab_label_keeprunning;
+
+ public static String JUnitLaunchConfigurationTab_label_method;
+
+ public static String JUnitLaunchConfigurationTab_label_oneTest;
+
+ public static String JUnitLaunchConfigurationTab_label_project;
+
+ public static String JUnitLaunchConfigurationTab_label_search;
+
+ public static String JUnitLaunchConfigurationTab_label_search_method;
+
+ public static String JUnitLaunchConfigurationTab_method_text_decoration;
+
+ public static String JUnitLaunchConfigurationTab_select_method_header;
+ public static String JUnitLaunchConfigurationTab_select_method_title;
+
+ public static String JUnitLaunchConfigurationTab_all_methods_text;
+
+ public static String JUnitLaunchConfigurationTab_label_test;
+
+ public static String JUnitLaunchConfigurationTab_projectdialog_message;
+
+ public static String JUnitLaunchConfigurationTab_projectdialog_title;
+
+ public static String JUnitLaunchConfigurationTab_tab_label;
+
+ public static String JUnitLaunchConfigurationTab_Test_Loader;
+
+ public static String JUnitLaunchConfigurationTab_testdialog_message;
+
+ public static String JUnitLaunchConfigurationTab_testdialog_title;
+
+ public static String JUnitLaunchConfigurationTab_addtag_label;
+
+ public static String JUnitLaunchConfigurationTab_addtag_text;
+
+ public static String JUnitLaunchConfigurationTab_includetag_checkbox_label;
+
+ public static String JUnitLaunchConfigurationTab_excludetag_checkbox_label;
+
+ public static String JUnitLaunchConfigurationTab_includetags_description;
+
+ public static String JUnitLaunchConfigurationTab_excludetags_description;
+
+ public static String JUnitLaunchConfigurationTab_addincludeexcludetagdialog_title;
+
+ public static String JUnitLaunchConfigurationTab_includetag_empty_error;
+
+ public static String JUnitLaunchConfigurationTab_excludetag_empty_error;
+
+ public static String UnitTestLaunchShortcut_dialog_title;
+
+ public static String UnitTestLaunchShortcut_dialog_title2;
+
+ public static String UnitTestLaunchShortcut_message_notests;
+
+ public static String JUnitMainTab_label_defaultpackage;
+
+ public static String OpenEditorAction_action_label;
+ public static String OpenEditorAction_error_cannotopen_message;
+ public static String OpenEditorAction_error_cannotopen_title;
+ public static String OpenEditorAction_error_dialog_message;
+ public static String OpenEditorAction_error_dialog_title;
+
+ public static String OpenEditorAction_message_cannotopen;
+
+ public static String OpenTestAction_error_methodNoFound;
+ public static String OpenTestAction_dialog_title;
+ public static String OpenTestAction_select_element;
+
+ public static String UnitTestLaunchShortcut_message_selectConfiguration;
+ public static String UnitTestLaunchShortcut_message_selectDebugConfiguration;
+ public static String UnitTestLaunchShortcut_message_selectRunConfiguration;
+ public static String UnitTestLaunchShortcut_message_selectTestToDebug;
+ public static String UnitTestLaunchShortcut_message_selectTestToRun;
+ public static String UnitTestLaunchShortcut_message_launchfailed;
+
+ public static String JUnitLaunchConfigurationDelegate_create_source_locator_description;
+ public static String JUnitLaunchConfigurationDelegate_error_input_element_deosn_not_exist;
+ public static String JUnitLaunchConfigurationDelegate_error_invalidproject;
+ public static String JUnitLaunchConfigurationDelegate_error_junit4notonpath;
+ public static String JUnitLaunchConfigurationDelegate_error_junit5notonpath;
+ public static String JUnitLaunchConfigurationDelegate_error_junitnotonpath;
+ public static String JUnitLaunchConfigurationDelegate_error_no_socket;
+ public static String JUnitLaunchConfigurationDelegate_error_notests_kind;
+ public static String JUnitLaunchConfigurationDelegate_error_wrong_input;
+ public static String JUnitLaunchConfigurationDelegate_input_type_does_not_exist;
+ public static String JUnitLaunchConfigurationDelegate_verifying_attriburtes_description;
+
+ static {
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ // Do not instantiate
+ }
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/Messages.properties b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/Messages.properties
new file mode 100644
index 0000000000..768f6a03f6
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/Messages.properties
@@ -0,0 +1,103 @@
+#################################################################################
+# 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
+#
+# Contributors:
+# Red Hat Inc. - initial API and implementation
+#################################################################################
+OpenEditorAction_action_label=&Go to File
+OpenEditorAction_error_cannotopen_title=Cannot Open Editor
+OpenEditorAction_error_cannotopen_message=Test class not found in selected project
+OpenEditorAction_error_dialog_title=Error
+OpenEditorAction_error_dialog_message=Cannot open editor
+
+OpenTestAction_dialog_title=Go to Test
+OpenTestAction_error_methodNoFound=Method ''{0}'' not found. Opening the test class.
+OpenTestAction_select_element=&Select or enter the element to open:
+
+JUnitMainTab_label_defaultpackage=(default package)
+
+JUnitLaunchConfigurationTab_error_test_class_not_found=Can not find test class ''{0}'' in project ''{1}''
+JUnitLaunchConfigurationTab_error_test_method_not_found=Can not find test method {0}.{1} in project {2}
+JUnitLaunchConfigurationTab_error_testannotationnotonpath=Cannot find class ''{0}'' on project build path.
+
+JUnitLaunchConfigurationTab_label_oneTest=Run a s&ingle test
+JUnitLaunchConfigurationTab_label_project=&Project:
+JUnitLaunchConfigurationTab_label_browse=&Browse...
+JUnitLaunchConfigurationTab_label_test=T&est class:
+JUnitLaunchConfigurationTab_label_search=&Search...
+JUnitLaunchConfigurationTab_label_search_method=Searc&h...
+JUnitLaunchConfigurationTab_label_containerTest=Run &all tests in the selected project, package or source folder:
+JUnitLaunchConfigurationTab_label_keeprunning=&Keep JUnit running after a test run when debugging
+JUnitLaunchConfigurationTab_label_method=Test &method:
+
+JUnitLaunchConfigurationTab_method_text_decoration=Omit the method name to run all tests.
+
+JUnitLaunchConfigurationTab_select_method_header=Choose a test method for ''{0}'':
+JUnitLaunchConfigurationTab_select_method_title=Test Method Selection
+
+JUnitLaunchConfigurationTab_all_methods_text=(all methods)
+
+JUnitLaunchConfigurationTab_testdialog_title=Test Selection
+JUnitLaunchConfigurationTab_testdialog_message=Choose a test case or test suite:
+
+JUnitLaunchConfigurationTab_projectdialog_title=Project Selection
+JUnitLaunchConfigurationTab_projectdialog_message=Choose a project to constrain the search for test classes:
+
+JUnitLaunchConfigurationTab_tab_label=Test
+
+JUnitLaunchConfigurationTab_Test_Loader=&Test runner:
+
+JUnitLaunchConfigurationTab_folderdialog_title=Folder Selection
+JUnitLaunchConfigurationTab_folderdialog_message=Choose a Project, Source Folder or Package:
+
+JUnitLaunchConfigurationTab_error_projectnotdefined=Project not specified
+JUnitLaunchConfigurationTab_error_projectnotexists=Project does not exist
+JUnitLaunchConfigurationTab_error_notJavaProject=Specified project is not a Java project
+JUnitLaunchConfigurationTab_error_testnotdefined=Test not specified
+JUnitLaunchConfigurationTab_error_testcasenotonpath=Cannot find class 'junit.framework.TestCase' on project build path.
+
+JUnitLaunchConfigurationTab_addtag_label=Con&figure...
+JUnitLaunchConfigurationTab_addtag_text=Include and exclude tags:
+
+JUnitLaunchConfigurationTab_includetag_checkbox_label=&Include Tags
+JUnitLaunchConfigurationTab_excludetag_checkbox_label=&Exclude Tags
+JUnitLaunchConfigurationTab_includetags_description=Newline separated tags to be i&ncluded in the test run:
+JUnitLaunchConfigurationTab_excludetags_description=Newline separated tags to be e&xcluded from the test run:
+JUnitLaunchConfigurationTab_addincludeexcludetagdialog_title=Configure Tags
+JUnitLaunchConfigurationTab_includetag_empty_error=Enter a tag to include
+JUnitLaunchConfigurationTab_excludetag_empty_error=Enter a tag to exclude
+JUnitLaunchConfigurationTab_error_invalidProjectName=''{0}'' is not a valid project name
+JUnitLaunchConfigurationTab_error_JDK15_required=JUnit 4 requires a JDK version of 1.5 or higher
+JUnitLaunchConfigurationTab_error_JDK18_required=JUnit 5 requires a JDK version of 1.8 or higher
+JUnitLaunchConfigurationTab_error_noContainer=No project, source folder or package is specified
+
+UnitTestLaunchShortcut_dialog_title=JUnit Launch
+UnitTestLaunchShortcut_message_notests=No JUnit tests found.
+UnitTestLaunchShortcut_dialog_title2=Test Selection
+
+UnitTestLaunchShortcut_message_selectConfiguration=Select a Test Configuration
+UnitTestLaunchShortcut_message_selectDebugConfiguration=Select JUnit configuration to debug
+UnitTestLaunchShortcut_message_selectRunConfiguration=Select JUnit configuration to run
+UnitTestLaunchShortcut_message_selectTestToRun=Select Test to run
+UnitTestLaunchShortcut_message_selectTestToDebug=Select Test to debug
+UnitTestLaunchShortcut_message_launchfailed=Launching of JUnit tests unexpectedly failed. Check log for details.
+
+JUnitLaunchConfigurationDelegate_verifying_attriburtes_description=Verifying launch attributes...
+JUnitLaunchConfigurationDelegate_create_source_locator_description=Creating source locator...
+JUnitLaunchConfigurationDelegate_error_no_socket=No socket available
+JUnitLaunchConfigurationDelegate_error_invalidproject=Invalid project specified.
+JUnitLaunchConfigurationDelegate_error_junitnotonpath=Cannot find 'junit.framework.TestCase' on project build path. JUnit 3 tests can only be run if JUnit is on the build path.
+JUnitLaunchConfigurationDelegate_error_junit4notonpath=Cannot find 'org.junit.Test' on project build path. JUnit 4 tests can only be run if JUnit 4 is on the build path.
+JUnitLaunchConfigurationDelegate_error_junit5notonpath=Cannot find ''{0}'' on project build path. JUnit 5 tests can only be run if JUnit 5 is on the build path.
+JUnitLaunchConfigurationDelegate_error_notests_kind=No tests found with test runner ''{0}''.
+JUnitLaunchConfigurationDelegate_error_wrong_input=Can only run types or single method
+JUnitLaunchConfigurationDelegate_error_input_element_deosn_not_exist=The input element of the launch configuration does not exist
+JUnitLaunchConfigurationDelegate_input_type_does_not_exist=The input type of the launch configuration does not exist
+
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/BasicElementLabels.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/BasicElementLabels.java
new file mode 100644
index 0000000000..f6e00fd0cd
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/BasicElementLabels.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 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.jdt.ui.unittest.junit.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.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/IJUnitHelpContextIds.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/IJUnitHelpContextIds.java
new file mode 100644
index 0000000000..a23734ca18
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/IJUnitHelpContextIds.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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.jdt.ui.unittest.junit.ui;
+
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin;
+
+/**
+ * Help context ids for the JUnit UI.
+ */
+public interface IJUnitHelpContextIds {
+ String PREFIX = JUnitTestPlugin.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$
+
+ // Preference/Property pages
+ String JUNIT_PREFERENCE_PAGE = PREFIX + "junit_preference_page_context"; //$NON-NLS-1$
+
+ // Wizard pages
+ String NEW_TESTCASE_WIZARD_PAGE = PREFIX + "new_testcase_wizard_page_context"; //$NON-NLS-1$
+ String NEW_TESTCASE_WIZARD_PAGE2 = PREFIX + "new_testcase_wizard_page2_context"; //$NON-NLS-1$
+ String NEW_TESTSUITE_WIZARD_PAGE = PREFIX + "new_testsuite_wizard_page2_context"; //$NON-NLS-1$
+ String LAUNCH_CONFIGURATION_DIALOG_JUNIT_MAIN_TAB = PREFIX + "launch_configuration_dialog_junit_main_tab"; //$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.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitMessages.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitMessages.java
new file mode 100644
index 0000000000..066cacde1c
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitMessages.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.jdt.ui.unittest.junit.ui;
+
+import org.eclipse.osgi.util.NLS;
+
+public final class JUnitMessages extends NLS {
+
+ private static final String BUNDLE_NAME = "org.eclipse.jdt.ui.unittest.junit.ui.JUnitMessages";//$NON-NLS-1$
+
+ public static String JUnitCantRunMultipleTests;
+
+ public static String OpenEditorAction_action_label;
+ public static String OpenEditorAction_error_cannotopen_message;
+ public static String OpenEditorAction_error_cannotopen_title;
+ public static String OpenEditorAction_error_dialog_message;
+ public static String OpenEditorAction_error_dialog_title;
+
+ public static String OpenTestAction_error_methodNoFound;
+ public static String OpenTestAction_dialog_title;
+ public static String OpenTestAction_select_element;
+
+ static {
+ NLS.initializeMessages(BUNDLE_NAME, JUnitMessages.class);
+ }
+
+ private JUnitMessages() {
+ // Do not instantiate
+ }
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitMessages.properties b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitMessages.properties
new file mode 100644
index 0000000000..b2b1208751
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitMessages.properties
@@ -0,0 +1,24 @@
+#################################################################################
+# 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
+#
+# Contributors:
+# Red Hat Inc. - initial API and implementation
+#################################################################################
+JUnitCantRunMultipleTests=JUnit can currently run only single test case at once
+
+OpenEditorAction_action_label=&Go to File
+OpenEditorAction_error_cannotopen_title=Cannot Open Editor
+OpenEditorAction_error_cannotopen_message=Test class not found in selected project
+OpenEditorAction_error_dialog_title=Error
+OpenEditorAction_error_dialog_message=Cannot open editor
+
+OpenTestAction_dialog_title=Go to Test
+OpenTestAction_error_methodNoFound=Method ''{0}'' not found. Opening the test class.
+OpenTestAction_select_element=&Select or enter the element to open:
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitTestViewSupport.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitTestViewSupport.java
new file mode 100644
index 0000000000..746b49779f
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/JUnitTestViewSupport.java
@@ -0,0 +1,326 @@
+/*******************************************************************************
+ * 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.jdt.ui.unittest.junit.ui;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+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.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.text.StringMatcher;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.Platform;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.MessageDialog;
+
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
+
+import org.eclipse.jdt.internal.junit.JUnitCorePlugin;
+import org.eclipse.jdt.internal.junit.JUnitPreferencesConstants;
+import org.eclipse.jdt.internal.junit.launcher.ITestFinder;
+import org.eclipse.jdt.internal.junit.launcher.ITestKind;
+import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
+
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin;
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin.JUnitVersion;
+import org.eclipse.jdt.ui.unittest.junit.launcher.JUnitLaunchConfigurationDelegate;
+import org.eclipse.jdt.ui.unittest.junit.launcher.JUnitRemoteTestRunnerClient;
+
+public class JUnitTestViewSupport implements ITestViewSupport {
+
+ public static final String FRAME_LINE_PREFIX = "at "; //$NON-NLS-1$
+
+ @Override
+ public Collection<StringMatcher> getTraceExclusionFilterPatterns() {
+ return Arrays
+ .stream(JUnitPreferencesConstants.parseList(Platform.getPreferencesService().getString(
+ JUnitCorePlugin.CORE_PLUGIN_ID, JUnitPreferencesConstants.PREF_ACTIVE_FILTERS_LIST, "", null))) //$NON-NLS-1$
+ .filter(Predicate.not(String::isBlank)) //
+ .map(pattern -> new StringMatcher(pattern, true, false)) //
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public IAction getOpenTestAction(Shell shell, ITestCaseElement testCase) {
+ return new OpenTestAction(shell, testCase, getParameterTypes(testCase));
+ }
+
+ @Override
+ public IAction getOpenTestAction(Shell shell, ITestSuiteElement testSuite) {
+ String testName = testSuite.getTestName();
+ List<? extends ITestElement> children = testSuite.getChildren();
+ if (testName.startsWith("[") && testName.endsWith("]") && !children.isEmpty() //$NON-NLS-1$ //$NON-NLS-2$
+ && children.get(0) instanceof ITestCaseElement) {
+ // a group of parameterized tests
+ return new OpenTestAction(shell, (ITestCaseElement) children.get(0), null);
+ }
+
+ int index = testName.indexOf('(');
+ // test factory method
+ if (index > 0) {
+ return new OpenTestAction(shell, testSuite.getTestName(), testName.substring(0, index),
+ getParameterTypes(testSuite), true, testSuite.getTestRunSession());
+ }
+
+ // regular test class
+ return new OpenTestAction(shell, testName, testSuite.getTestRunSession());
+
+ }
+
+ @Override
+ public IAction createOpenEditorAction(Shell shell, ITestElement failure, String traceLine) {
+ try {
+ String testName = traceLine;
+ int indexOfFramePrefix = testName.indexOf(FRAME_LINE_PREFIX);
+ if (indexOfFramePrefix == -1) {
+ return null;
+ }
+ testName = testName.substring(indexOfFramePrefix);
+ testName = testName.substring(FRAME_LINE_PREFIX.length(), testName.lastIndexOf('(')).trim();
+ int indexOfModuleSeparator = testName.lastIndexOf('/');
+ if (indexOfModuleSeparator != -1) {
+ testName = testName.substring(indexOfModuleSeparator + 1);
+ }
+ testName = testName.substring(0, testName.lastIndexOf('.'));
+ int innerSeparatorIndex = testName.indexOf('$');
+ if (innerSeparatorIndex != -1)
+ testName = testName.substring(0, innerSeparatorIndex);
+
+ String lineNumber = traceLine;
+ lineNumber = lineNumber.substring(lineNumber.indexOf(':') + 1, lineNumber.lastIndexOf(')'));
+ int line = Integer.parseInt(lineNumber);
+ return new OpenEditorAtLineAction(shell, testName, line, failure.getTestRunSession());
+ } catch (NumberFormatException | IndexOutOfBoundsException e) {
+ JUnitTestPlugin.log(e);
+ }
+ return null;
+ }
+
+ @Override
+ public Runnable createShowStackTraceInConsoleViewActionDelegate(ITestElement failedTest) {
+ return new ShowStackTraceInConsoleViewActionDelegate(failedTest);
+ }
+
+ @Override
+ public ILaunchConfiguration getRerunLaunchConfiguration(List<ITestElement> tests) {
+ if (tests.size() > 1) {
+ MessageDialog.openInformation(Display.getDefault().getActiveShell(),
+ JUnitMessages.JUnitCantRunMultipleTests, JUnitMessages.JUnitCantRunMultipleTests);
+ return null;
+ }
+ ITestElement testSuite = tests.get(0);
+ String testMethodName = null; // test method name is null when re-running a regular test class
+ String testName = testSuite.getTestName();
+
+ ILaunchConfiguration launchConfiguration = testSuite.getTestRunSession().getLaunch().getLaunchConfiguration();
+ ITestKind junitKind;
+ try {
+ junitKind = JUnitVersion
+ .fromJUnitTestKindId(launchConfiguration
+ .getAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND, "")) //$NON-NLS-1$
+ .getJUnitTestKind();
+ } catch (CoreException e) {
+ JUnitTestPlugin.log(e);
+ return null;
+ }
+
+ IJavaProject project = JUnitLaunchConfigurationConstants
+ .getJavaProject(testSuite.getTestRunSession().getLaunch().getLaunchConfiguration());
+ if (project == null) {
+ return null;
+ }
+
+ String qualifiedName = null;
+ IType testType = findTestClass(testSuite, junitKind.getFinder(), project, true);
+ if (testType != null) {
+ qualifiedName = testType.getFullyQualifiedName();
+
+ if (!qualifiedName.equals(testName)) {
+ int index = testName.indexOf('(');
+ if (index > 0) { // test factory method
+ testMethodName = testName.substring(0, index);
+ }
+ }
+ String[] parameterTypes = getParameterTypes(testSuite);
+ if (testMethodName != null && parameterTypes != null) {
+ String paramTypesStr = Arrays.stream(parameterTypes).collect(Collectors.joining(",")); //$NON-NLS-1$
+ testMethodName = testMethodName + "(" + paramTypesStr + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ } else {
+ // see bug 443498
+ testType = findTestClass(testSuite.getParent(), junitKind.getFinder(), project, false);
+ if (testType != null && testSuite instanceof ITestSuiteElement) {
+ qualifiedName = testType.getFullyQualifiedName();
+
+ String className = getClassName(testSuite);
+ if (!qualifiedName.equals(className)) {
+ testMethodName = testName;
+ }
+ }
+ }
+
+ ILaunchConfigurationWorkingCopy res;
+ try {
+ res = launchConfiguration.copy(launchConfiguration.getName() + " - rerun"); //$NON-NLS-1$
+ res.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME, testMethodName);
+ return res;
+ } catch (CoreException e) {
+ JUnitTestPlugin.log(e);
+ return null;
+ }
+
+ }
+
+ /*
+ * Returns the element's test class or the next container's test class, which
+ * exists, and for which ITestFinder.isTest() is true.
+ */
+ private IType findTestClass(ITestElement element, ITestFinder finder, IJavaProject project,
+ boolean checkOnlyCurrentElement) {
+ ITestElement current = element;
+ while (current != null) {
+ try {
+ String className = null;
+ if (current instanceof ITestRunSession) {
+ ILaunch launch = element.getTestRunSession().getLaunch();
+ if (launch != null) {
+ ILaunchConfiguration configuration = launch.getLaunchConfiguration();
+ if (configuration != null) {
+ className = configuration
+ .getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, (String) null);
+ }
+ }
+ } else {
+ className = getClassName(current);
+ }
+
+ if (className != null) {
+ IType type = project.findType(className);
+ if (type != null && finder.isTest(type)) {
+ return type;
+ } else if (checkOnlyCurrentElement) {
+ return null;
+ }
+ }
+ } catch (CoreException e) {
+ JUnitTestPlugin.log(e);
+ }
+ current = current.getParent();
+ }
+ return null;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "JUnit"; //$NON-NLS-1$
+ }
+
+ @Override
+ public ITestRunnerClient newTestRunnerClient(ITestRunSession session) {
+ String portAsString = session.getLaunch().getAttribute(JUnitLaunchConfigurationDelegate.ATTR_PORT);
+ return new JUnitRemoteTestRunnerClient(portAsString != null ? Integer.parseInt(portAsString) : -1, session);
+ }
+
+ /**
+ * Returns the parameter types specified for this test element
+ *
+ * @param test test
+ * @return a parameter type array
+ */
+ private String[] getParameterTypes(ITestElement test) {
+ String testName = test.getDisplayName();
+ if (testName != null) {
+ int index = testName.lastIndexOf("method:"); //$NON-NLS-1$
+ if (index != -1) {
+ index = testName.indexOf('(', index);
+ if (index > 0) {
+ int closeIndex = testName.indexOf(')', index);
+ if (closeIndex > 0) {
+ String params = testName.substring(index + 1, closeIndex);
+ return params.split(","); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the type/class of the test element
+ *
+ * @param test test
+ * @return return the type/class name
+ */
+ public static String getClassName(ITestElement test) {
+ return extractClassName(test.getTestName());
+ }
+
+ private static String extractClassName(String testNameString) {
+ testNameString = extractRawClassName(testNameString);
+ testNameString = testNameString.replace('$', '.'); // see bug 178503
+ return testNameString;
+ }
+
+ /**
+ * Extracts and returns a raw class name from a test element name
+ *
+ * @param testNameString a test element name
+ *
+ * @return an extracted raw class name
+ */
+ public static String extractRawClassName(String testNameString) {
+ if (testNameString.startsWith("[") && testNameString.endsWith("]")) { //$NON-NLS-1$ //$NON-NLS-2$
+ // a group of parameterized tests, see
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=102512
+ return testNameString;
+ }
+ int index = testNameString.lastIndexOf('(');
+ if (index < 0)
+ return testNameString;
+ int end = testNameString.lastIndexOf(')');
+ return testNameString.substring(index + 1, end > index ? end : testNameString.length());
+ }
+
+ public static String getTestMethodName(ITestElement test) {
+ String testName = test.getTestName();
+ int index = testName.lastIndexOf('(');
+ if (index > 0)
+ return testName.substring(0, index);
+ index = testName.indexOf('@');
+ if (index > 0)
+ return testName.substring(0, index);
+ return testName;
+ }
+
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenEditorAction.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenEditorAction.java
new file mode 100644
index 0000000000..2cf2ff709e
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenEditorAction.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * 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.jdt.ui.unittest.junit.ui;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.unittest.model.ITestRunSession;
+
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaModel;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.search.IJavaSearchConstants;
+import org.eclipse.jdt.core.search.SearchEngine;
+import org.eclipse.jdt.core.search.SearchPattern;
+import org.eclipse.jdt.core.search.TypeNameMatch;
+import org.eclipse.jdt.core.search.TypeNameMatchRequestor;
+
+import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
+
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin;
+
+/**
+ * Abstract Action for opening a Java editor.
+ */
+public abstract class OpenEditorAction extends Action {
+ protected final Shell shell;
+ protected final ITestRunSession testSession;
+ protected String fClassName;
+ private final boolean fActivate;
+
+ protected OpenEditorAction(Shell shell, String testClassName, ITestRunSession session) {
+ this(shell, testClassName, true, session);
+ }
+
+ public OpenEditorAction(Shell shell, String className, boolean activate, ITestRunSession session) {
+ super(JUnitMessages.OpenEditorAction_action_label);
+ this.fClassName = className;
+ this.shell = shell;
+ this.fActivate = activate;
+ this.testSession = session;
+ }
+
+ @Override
+ public void run() {
+ IEditorPart editor = null;
+ try {
+ IJavaElement element = findElement(
+ JUnitLaunchConfigurationConstants.getJavaProject(testSession.getLaunch().getLaunchConfiguration()),
+ fClassName);
+ if (element == null) {
+ MessageDialog.openError(getShell(), JUnitMessages.OpenEditorAction_error_cannotopen_title,
+ JUnitMessages.OpenEditorAction_error_cannotopen_message);
+ return;
+ }
+ editor = JavaUI.openInEditor(element, fActivate, false);
+ } catch (CoreException e) {
+ ErrorDialog.openError(getShell(), JUnitMessages.OpenEditorAction_error_dialog_title,
+ JUnitMessages.OpenEditorAction_error_dialog_message, e.getStatus());
+ return;
+ }
+ if (!(editor instanceof ITextEditor)) {
+ MessageDialog.openError(getShell(), JUnitMessages.OpenEditorAction_error_dialog_title,
+ JUnitMessages.OpenEditorAction_error_dialog_message);
+ return;
+ }
+ reveal((ITextEditor) editor);
+ }
+
+ protected Shell getShell() {
+ return shell;
+ }
+
+ protected String getClassName() {
+ return fClassName;
+ }
+
+ protected abstract IJavaElement findElement(IJavaProject project, String className) throws CoreException;
+
+ protected abstract void reveal(ITextEditor editor);
+
+ protected final IType findType(final IJavaProject project, String className) {
+ final IType[] result = { null };
+ final String dottedName = className.replace('$', '.'); // for nested classes...
+ if (project == null) {
+ return null;
+ }
+ try {
+ PlatformUI.getWorkbench().getProgressService().busyCursorWhile(monitor -> {
+ try {
+ result[0] = internalFindType(project, dottedName, new HashSet<IJavaProject>(), monitor);
+ if (result[0] == null) {
+ int lastDot = dottedName.lastIndexOf('.');
+ TypeNameMatchRequestor nameMatchRequestor = new TypeNameMatchRequestor() {
+ @Override
+ public void acceptTypeNameMatch(TypeNameMatch match) {
+ result[0] = match.getType();
+ }
+ };
+ new SearchEngine().searchAllTypeNames(
+ lastDot >= 0 ? dottedName.substring(0, lastDot).toCharArray() : null,
+ SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
+ (lastDot >= 0 ? dottedName.substring(lastDot + 1) : dottedName).toCharArray(),
+ SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE, IJavaSearchConstants.TYPE,
+ SearchEngine.createWorkspaceScope(), nameMatchRequestor,
+ IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor);
+ }
+ } catch (JavaModelException e) {
+ throw new InvocationTargetException(e);
+ }
+ });
+ } catch (InvocationTargetException e) {
+ JUnitTestPlugin.log(e);
+ } catch (InterruptedException e) {
+ // user cancelled
+ }
+ return result[0];
+ }
+
+ private IType internalFindType(IJavaProject project, String className, Set<IJavaProject> visitedProjects,
+ IProgressMonitor monitor) throws JavaModelException {
+ try {
+ if (visitedProjects.contains(project))
+ return null;
+ monitor.beginTask("", 2); //$NON-NLS-1$
+ IType type = project.findType(className, SubMonitor.convert(monitor, 1));
+ if (type != null)
+ return type;
+ // fix for bug 87492: visit required projects explicitly to also find not
+ // exported types
+ visitedProjects.add(project);
+ IJavaModel javaModel = project.getJavaModel();
+ String[] requiredProjectNames = project.getRequiredProjectNames();
+ IProgressMonitor reqMonitor = SubMonitor.convert(monitor, 1);
+ reqMonitor.beginTask("", requiredProjectNames.length); //$NON-NLS-1$
+ for (String requiredProjectName : requiredProjectNames) {
+ IJavaProject requiredProject = javaModel.getJavaProject(requiredProjectName);
+ if (requiredProject.exists()) {
+ type = internalFindType(requiredProject, className, visitedProjects,
+ SubMonitor.convert(reqMonitor, 1));
+ if (type != null)
+ return type;
+ }
+ }
+ return null;
+ } finally {
+ monitor.done();
+ }
+ }
+
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenEditorAtLineAction.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenEditorAtLineAction.java
new file mode 100644
index 0000000000..a719a235e9
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenEditorAtLineAction.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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.jdt.ui.unittest.junit.ui;
+
+import org.eclipse.unittest.model.ITestRunSession;
+
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+
+/**
+ * Open a test in the Java editor and reveal a given line
+ */
+public class OpenEditorAtLineAction extends OpenEditorAction {
+
+ private int fLineNumber;
+
+ public OpenEditorAtLineAction(Shell shell, String className, int line, ITestRunSession session) {
+ super(shell, className, session);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJUnitHelpContextIds.OPENEDITORATLINE_ACTION);
+ fLineNumber = line;
+ }
+
+ @Override
+ protected void reveal(ITextEditor textEditor) {
+ if (fLineNumber >= 0) {
+ try {
+ IDocument document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
+ textEditor.selectAndReveal(document.getLineOffset(fLineNumber - 1),
+ document.getLineLength(fLineNumber - 1));
+ } catch (BadLocationException x) {
+ // marker refers to invalid text position -> do nothing
+ }
+ }
+ }
+
+ @Override
+ protected IJavaElement findElement(IJavaProject project, String className) throws CoreException {
+ return findType(project, className);
+ }
+
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenTestAction.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenTestAction.java
new file mode 100644
index 0000000000..7bb5346349
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/OpenTestAction.java
@@ -0,0 +1,321 @@
+/*******************************************************************************
+ * 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.jdt.ui.unittest.junit.ui;
+
+import java.lang.reflect.InvocationTargetException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.eclipse.unittest.model.ITestCaseElement;
+import org.eclipse.unittest.model.ITestElement.FailureTrace;
+import org.eclipse.unittest.model.ITestRunSession;
+
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.JavaConventions;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
+import org.eclipse.jdt.core.search.IJavaSearchConstants;
+import org.eclipse.jdt.core.search.IJavaSearchScope;
+import org.eclipse.jdt.core.search.SearchEngine;
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.core.search.SearchParticipant;
+import org.eclipse.jdt.core.search.SearchPattern;
+import org.eclipse.jdt.core.search.SearchRequestor;
+
+import org.eclipse.jdt.internal.corext.util.JavaConventionsUtil;
+
+import org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin;
+
+import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
+
+/**
+ * Open a class on a Test method.
+ */
+public class OpenTestAction extends OpenEditorAction {
+
+ private String fMethodName;
+ private String[] fMethodParamTypes;
+ private IMethod fMethod;
+ private int fLineNumber = -1;
+
+ private IType fType;
+
+ public OpenTestAction(Shell shell, ITestCaseElement testCase, String[] methodParamTypes) {
+ this(shell, JUnitTestViewSupport.getClassName(testCase), extractRealMethodName(testCase), methodParamTypes,
+ true, testCase.getTestRunSession());
+ FailureTrace trace = testCase.getFailureTrace();
+ if (trace != null) {
+ String rawClassName = JUnitTestViewSupport.extractRawClassName(testCase.getTestName());
+ rawClassName = rawClassName.replaceAll("\\.", "\\\\."); //$NON-NLS-1$//$NON-NLS-2$
+ rawClassName = rawClassName.replaceAll("\\$", "\\\\\\$"); //$NON-NLS-1$//$NON-NLS-2$
+ Pattern pattern = Pattern.compile(
+ JUnitTestViewSupport.FRAME_LINE_PREFIX + rawClassName + '.' + fMethodName + "\\(.*:(\\d+)\\)" //$NON-NLS-1$
+ );
+ Matcher matcher = pattern.matcher(trace.getTrace());
+ if (matcher.find()) {
+ try {
+ fLineNumber = Integer.parseInt(matcher.group(1));
+ } catch (NumberFormatException e) {
+ // continue
+ }
+ }
+ }
+ }
+
+ public OpenTestAction(Shell shell, String className, ITestRunSession session) {
+ this(shell, className, null, null, true, session);
+ }
+
+ public OpenTestAction(Shell shell, String className, String method, String[] methodParamTypes, boolean activate,
+ ITestRunSession session) {
+ super(shell, className, activate, session);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJUnitHelpContextIds.OPENTEST_ACTION);
+ fMethodName = method;
+ fMethodParamTypes = methodParamTypes;
+ }
+
+ private static String extractRealMethodName(ITestCaseElement testCase) {
+ // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=334864 :
+ if (testCase.isIgnored() && JavaConventions
+ .validateJavaTypeName(testCase.getTestName(), JavaCore.VERSION_1_5, JavaCore.VERSION_1_5, null)
+ .getSeverity() != IStatus.ERROR) {
+ return null;
+ }
+
+ // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=275308 :
+ String testMethodName = JUnitTestViewSupport.getTestMethodName(testCase);
+ for (int i = 0; i < testMethodName.length(); i++) {
+ if (!Character.isJavaIdentifierPart(testMethodName.charAt(i))) {
+ return testMethodName.substring(0, i);
+ }
+ }
+ return testMethodName;
+ }
+
+ @Override
+ protected IJavaElement findElement(IJavaProject project, String className) throws JavaModelException {
+ IType type = findType(project, className);
+ if (type == null)
+ return null;
+
+ if (fMethodName == null) {
+ fType = type;
+ return type;
+ }
+
+ IMethod method = null;
+ try {
+ method = findMethod(type);
+ if (method == null) {
+ ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(null);
+ IType[] supertypes = typeHierarchy.getAllSupertypes(type);
+ for (IType supertype : supertypes) {
+ method = findMethod(supertype);
+ if (method != null)
+ break;
+ }
+ }
+ } catch (OperationCanceledException e) {
+ // user cancelled the selection dialog - ignore and proceed
+ }
+ if (method == null) {
+ if (fLineNumber < 0) {
+ String title = JUnitMessages.OpenTestAction_dialog_title;
+ String message = MessageFormat.format(JUnitMessages.OpenTestAction_error_methodNoFound,
+ BasicElementLabels.getJavaElementName(fMethodName));
+ MessageDialog.openInformation(getShell(), title, message);
+ }
+ return type;
+ }
+
+ fMethod = method;
+ return method;
+ }
+
+ private IMethod findMethod(IType type) {
+ IStatus status = JavaConventionsUtil.validateMethodName(fMethodName, type);
+ if (!status.isOK())
+ return null;
+
+ List<IMethod> foundMethods = new ArrayList<>();
+ try {
+ PlatformUI.getWorkbench().getProgressService().busyCursorWhile(monitor -> {
+ String methodPattern = type.getFullyQualifiedName('.') + '.' + fMethodName;
+ if (fMethodParamTypes != null && fMethodParamTypes.length > 0) {
+ String paramTypes = Arrays.stream(fMethodParamTypes).map(paramType -> {
+ try {
+ return paramType = Signature.toString(paramType);
+ } catch (IllegalArgumentException e1) {
+ // return the paramType as it is
+ }
+ return paramType.replace('$', '.'); // for nested classes... See OpenEditorAction#findType also.
+ }).collect(Collectors.joining(", ", "(", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ methodPattern += paramTypes;
+ } else {
+ methodPattern += "()"; //$NON-NLS-1$
+ }
+ int matchRule = SearchPattern.R_ERASURE_MATCH | SearchPattern.R_EXACT_MATCH
+ | SearchPattern.R_CASE_SENSITIVE;
+ SearchPattern searchPattern = SearchPattern.createPattern(methodPattern, IJavaSearchConstants.METHOD,
+ IJavaSearchConstants.DECLARATIONS, matchRule);
+ if (searchPattern == null) {
+ return;
+ }
+ SearchRequestor requestor = new SearchRequestor() {
+ @Override
+ public void acceptSearchMatch(SearchMatch match) throws CoreException {
+ Object element = match.getElement();
+ if (element instanceof IMethod) {
+ foundMethods.add((IMethod) element);
+ }
+ }
+ };
+ SearchParticipant[] participants = new SearchParticipant[] {
+ SearchEngine.getDefaultSearchParticipant() };
+ IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] { type });
+ try {
+ new SearchEngine().search(searchPattern, participants, scope, requestor, monitor);
+ } catch (CoreException e2) {
+ JUnitTestPlugin.log(e2);
+ }
+ });
+ } catch (InvocationTargetException e) {
+ JUnitTestPlugin.log(e);
+ } catch (InterruptedException e) {
+ // user cancelled
+ }
+
+ if (foundMethods.size() == 1) {
+ return foundMethods.get(0);
+ } else if (foundMethods.size() > 1) {
+ IMethod method = openSelectionDialog(foundMethods);
+ if (method == null) {
+ throw new OperationCanceledException();
+ }
+ return method;
+ }
+
+ // search just by name and number of parameters, if method not found yet
+ try {
+ for (IMethod method : type.getMethods()) {
+ String methodName = method.getElementName();
+ if (fMethodName.equals(methodName)) {
+ int numOfParams = method.getNumberOfParameters();
+ int requiredNumOfParams = 0;
+ if (fMethodParamTypes != null) {
+ requiredNumOfParams = fMethodParamTypes.length;
+ }
+ if (numOfParams == requiredNumOfParams) {
+ foundMethods.add(method);
+ }
+ }
+ }
+ if (foundMethods.isEmpty()) {
+ return null;
+ } else if (foundMethods.size() > 1) {
+ IMethod method = openSelectionDialog(foundMethods);
+ if (method == null) {
+ throw new OperationCanceledException();
+ }
+ return method;
+ } else {
+ return foundMethods.get(0);
+ }
+ } catch (JavaModelException e) {
+ // if type does not exist or if an exception occurs while accessing its resource
+ // => ignore (no method found)
+ }
+
+ return null;
+ }
+
+ private IMethod openSelectionDialog(List<IMethod> foundMethods) {
+ IMethod[] elements = foundMethods.toArray(new IMethod[foundMethods.size()]);
+ String title = JUnitMessages.OpenTestAction_dialog_title;
+ String message = JUnitMessages.OpenTestAction_select_element;
+ return (IMethod) SelectionConverter.selectJavaElement(elements, getShell(), title, message);
+ }
+
+ @Override
+ protected void reveal(ITextEditor textEditor) {
+ if (fLineNumber >= 0) {
+ try {
+ IDocument document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
+ int lineOffset = document.getLineOffset(fLineNumber - 1);
+ int lineLength = document.getLineLength(fLineNumber - 1);
+ if (fMethod != null) {
+ try {
+ ISourceRange sr = fMethod.getSourceRange();
+ if (sr == null || sr.getOffset() == -1 || lineOffset < sr.getOffset()
+ || sr.getOffset() + sr.getLength() < lineOffset + lineLength) {
+ throw new BadLocationException();
+ }
+ } catch (JavaModelException e) {
+ // not a problem
+ }
+ }
+ textEditor.selectAndReveal(lineOffset, lineLength);
+ return;
+ } catch (BadLocationException x) {
+ // marker refers to invalid text position -> do nothing
+ }
+ }
+ if (fMethod != null) {
+ try {
+ ISourceRange range = fMethod.getNameRange();
+ if (range != null && range.getOffset() >= 0)
+ textEditor.selectAndReveal(range.getOffset(), range.getLength());
+ return;
+ } catch (JavaModelException e) {
+ // not a problem
+ }
+ }
+ if (fType != null) {
+ try {
+ ISourceRange range = fType.getNameRange();
+ if (range != null && range.getOffset() >= 0)
+ textEditor.selectAndReveal(range.getOffset(), range.getLength());
+ } catch (JavaModelException e) {
+ // not a problem
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/ShowStackTraceInConsoleViewActionDelegate.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/ShowStackTraceInConsoleViewActionDelegate.java
new file mode 100644
index 0000000000..68a5309c20
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/ui/ShowStackTraceInConsoleViewActionDelegate.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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.jdt.ui.unittest.junit.ui;
+
+import org.eclipse.jdt.debug.ui.console.JavaStackTraceConsoleFactory;
+import org.eclipse.unittest.model.ITestElement;
+import org.eclipse.unittest.model.ITestElement.FailureTrace;
+
+/**
+ * Action delegate to show the stack trace of a failed test from JUnit view's
+ * failure trace in debug's Java stack trace console.
+ */
+public class ShowStackTraceInConsoleViewActionDelegate implements Runnable {
+
+ private ITestElement failedTest;
+ private JavaStackTraceConsoleFactory fFactory;
+
+ public ShowStackTraceInConsoleViewActionDelegate(ITestElement failedTest) {
+ this.failedTest = failedTest;
+ }
+
+ @Override
+ public void run() {
+ FailureTrace stackTrace = failedTest.getFailureTrace();
+ if (stackTrace != null) {
+ if (fFactory == null) {
+ fFactory = new JavaStackTraceConsoleFactory();
+ }
+ fFactory.openConsole(stackTrace.getTrace());
+ }
+ }
+
+}
diff --git a/org.eclipse.jdt.ui/META-INF/MANIFEST.MF b/org.eclipse.jdt.ui/META-INF/MANIFEST.MF
index 8f9c78b8d7..884f7bf159 100644
--- a/org.eclipse.jdt.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.ui/META-INF/MANIFEST.MF
@@ -37,7 +37,7 @@ Export-Package: org.eclipse.jdt.internal.corext;x-friends:="org.eclipse.jdt.juni
org.eclipse.jdt.internal.corext.template.java;x-friends:="org.eclipse.jdt.debug.ui",
org.eclipse.jdt.internal.corext.util;x-friends:="org.eclipse.jdt.junit",
org.eclipse.jdt.internal.ui;x-friends:="org.eclipse.jdt.junit,org.eclipse.jdt.apt.ui,org.eclipse.jdt.debug.ui",
- org.eclipse.jdt.internal.ui.actions;x-friends:="org.eclipse.jdt.junit",
+ org.eclipse.jdt.internal.ui.actions;x-friends:="org.eclipse.jdt.junit,org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.internal.ui.browsing;x-internal:=true,
org.eclipse.jdt.internal.ui.callhierarchy;x-internal:=true,
org.eclipse.jdt.internal.ui.commands;x-internal:=true,
@@ -85,12 +85,12 @@ Export-Package: org.eclipse.jdt.internal.corext;x-friends:="org.eclipse.jdt.juni
org.eclipse.jdt.internal.ui.text.template.contentassist;x-friends:="org.eclipse.jdt.debug.ui",
org.eclipse.jdt.internal.ui.text.template.preferences;x-internal:=true,
org.eclipse.jdt.internal.ui.typehierarchy;x-internal:=true,
- org.eclipse.jdt.internal.ui.util;x-friends:="org.eclipse.jdt.junit,org.eclipse.jdt.apt.ui,org.eclipse.jdt.debug.ui",
+ org.eclipse.jdt.internal.ui.util;x-friends:="org.eclipse.jdt.junit,org.eclipse.jdt.apt.ui,org.eclipse.jdt.debug.ui,org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.internal.ui.viewsupport;x-friends:="org.eclipse.jdt.junit,org.eclipse.jdt.debug.ui",
- org.eclipse.jdt.internal.ui.wizards;x-friends:="org.eclipse.jdt.junit,org.eclipse.jdt.apt.ui",
+ org.eclipse.jdt.internal.ui.wizards;x-friends:="org.eclipse.jdt.junit,org.eclipse.jdt.apt.ui,org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.internal.ui.wizards.buildpaths;x-internal:=true,
org.eclipse.jdt.internal.ui.wizards.buildpaths.newsourcepage;x-internal:=true,
- org.eclipse.jdt.internal.ui.wizards.dialogfields;x-friends:="org.eclipse.jdt.apt.ui,org.eclipse.jdt.junit",
+ org.eclipse.jdt.internal.ui.wizards.dialogfields;x-friends:="org.eclipse.jdt.apt.ui,org.eclipse.jdt.junit,org.eclipse.jdt.ui.unittest.junit",
org.eclipse.jdt.internal.ui.wizards.importer;x-internal:=true,
org.eclipse.jdt.internal.ui.workingsets;x-internal:=true,
org.eclipse.jdt.ui,
diff --git a/pom.xml b/pom.xml
index 0c3eb90a6f..46fab0255d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -87,5 +87,7 @@
<module>org.eclipse.jdt.junit.runtime</module>
<module>org.eclipse.jdt.junit4.runtime</module>
<module>org.eclipse.jdt.junit5.runtime</module>
+ <module>org.eclipse.jdt.ui.unittest.junit</module>
+ <module>org.eclipse.jdt.ui.unittest.junit.feature</module>
</modules>
-</project> \ No newline at end of file
+</project>

Back to the top