diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index c1078729..51c694c1 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -23,6 +23,10 @@ jobs:
       uses: actions/setup-java@v1
       with:
         java-version: 11
+    - name: Install WebKit # required for SWT Browser
+      run: |
+        sudo apt update
+        sudo apt install -y libwebkit2gtk-4.0-37
     - name: Cache Maven packages
       uses: actions/cache@v2
       with:
@@ -42,7 +46,13 @@ jobs:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
     - name: Archive Test Results
       uses: actions/upload-artifact@v2
-      if: ${{ failure() }}
+      if: ${{ failure() || cancelled() }}
       with:
         name: test-results-${{ runner.os }}
         path: '**/*.tests/target'
+    - name: Archive UI Tests Screenshots
+      uses: actions/upload-artifact@v2
+      if: ${{ failure() || cancelled() }}
+      with:
+        name: screenshots-${{ runner.os }}
+        path: '**/*.tests/screenshots'
diff --git a/.github/workflows/windows-mac.yml b/.github/workflows/windows-mac.yml
index c1e94753..0f6aa97d 100644
--- a/.github/workflows/windows-mac.yml
+++ b/.github/workflows/windows-mac.yml
@@ -7,10 +7,6 @@ on:
   push:
     paths-ignore:
       - 'README.md'
-    # Sequence of patterns matched against refs/heads
-    branches:
-      # Push events on the main branch
-      - master
   pull_request:
     paths-ignore:
       - 'README.md'
@@ -39,7 +35,13 @@ jobs:
       run: mvn '-Dtycho.disableP2Mirrors=true' verify
     - name: Archive Test Results
       uses: actions/upload-artifact@v2
-      if: ${{ failure() }}
+      if: ${{ failure() || cancelled() }}
       with:
         name: test-results-${{ runner.os }}
         path: '**/*.tests/target'
+    - name: Archive UI Tests Screenshots
+      uses: actions/upload-artifact@v2
+      if: ${{ failure() || cancelled() }}
+      with:
+        name: screenshots-${{ runner.os }}
+        path: '**/*.tests/screenshots'
diff --git a/bundles/org.pitest.pitclipse.core/src/org/pitest/pitclipse/core/preferences/PreferenceInitializer.java b/bundles/org.pitest.pitclipse.core/src/org/pitest/pitclipse/core/preferences/PreferenceInitializer.java
index 892d6476..8681404d 100644
--- a/bundles/org.pitest.pitclipse.core/src/org/pitest/pitclipse/core/preferences/PreferenceInitializer.java
+++ b/bundles/org.pitest.pitclipse.core/src/org/pitest/pitclipse/core/preferences/PreferenceInitializer.java
@@ -19,6 +19,7 @@
 import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
 import org.eclipse.jface.preference.IPreferenceStore;
 import org.pitest.pitclipse.core.PitCoreActivator;
+import org.pitest.pitclipse.runner.config.PitConfiguration;
 
 import static org.pitest.pitclipse.core.preferences.PitPreferences.AVOID_CALLS_TO;
 import static org.pitest.pitclipse.core.preferences.PitPreferences.EXCLUDED_CLASSES;
@@ -53,7 +54,7 @@ public void initializeDefaultPreferences() {
         store.setDefault(DEFAULT_MUTATORS, "defaultMutators");
         store.setDefault(TIMEOUT, DEFAULT_TIMEOUT);
         store.setDefault(TIMEOUT_FACTOR, DEFAULT_TIMEOUT_FACTOR.toString());
-        store.setDefault(EXCLUDED_CLASSES, "*Test");
+        store.setDefault(EXCLUDED_CLASSES, PitConfiguration.DEFAULT_EXCLUDED_CLASSES);
     }
 
 }
diff --git a/bundles/org.pitest.pitclipse.launch.ui/META-INF/MANIFEST.MF b/bundles/org.pitest.pitclipse.launch.ui/META-INF/MANIFEST.MF
index 7cacd130..38e7c3e2 100644
--- a/bundles/org.pitest.pitclipse.launch.ui/META-INF/MANIFEST.MF
+++ b/bundles/org.pitest.pitclipse.launch.ui/META-INF/MANIFEST.MF
@@ -18,3 +18,4 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.11.1,4.0.0)",
  org.eclipse.ui.workbench;bundle-version="[3.107.1,4.0.0)",
  org.eclipse.jdt.debug.ui;bundle-version="[3.7.101,4.0.0)"
 Import-Package: com.google.common.collect;version="21.0.0"
+Export-Package: org.pitest.pitclipse.launch.ui
diff --git a/bundles/org.pitest.pitclipse.launch.ui/src/org/pitest/pitclipse/launch/ui/PitLaunchShortcut.java b/bundles/org.pitest.pitclipse.launch.ui/src/org/pitest/pitclipse/launch/ui/PitLaunchShortcut.java
index c6e0a129..002574a4 100644
--- a/bundles/org.pitest.pitclipse.launch.ui/src/org/pitest/pitclipse/launch/ui/PitLaunchShortcut.java
+++ b/bundles/org.pitest.pitclipse.launch.ui/src/org/pitest/pitclipse/launch/ui/PitLaunchShortcut.java
@@ -111,7 +111,8 @@ private void launch(Object[] elements, String mode) {
         try {
             if (elements.length == 1) {
                 Optional<IJavaElement> selected = asJavaElement(elements[0]);
-                Optional<IJavaElement> launchElement = selected.map(this::getLaunchElementFor).get();
+                Optional<IJavaElement> launchElement =
+                        selected.flatMap(this::getLaunchElementFor);
 
                 if (launchElement.isPresent()) {
                     performLaunch(launchElement.get(), mode);
diff --git a/bundles/org.pitest.pitclipse.runner/META-INF/MANIFEST.MF b/bundles/org.pitest.pitclipse.runner/META-INF/MANIFEST.MF
index afa39a20..8e1f0e88 100644
--- a/bundles/org.pitest.pitclipse.runner/META-INF/MANIFEST.MF
+++ b/bundles/org.pitest.pitclipse.runner/META-INF/MANIFEST.MF
@@ -15,8 +15,7 @@ Export-Package: org.pitest.pitclipse.runner,
  org.pitest.pitclipse.runner.results,
  org.pitest.pitclipse.runner.results.mutations,
  org.pitest.pitclipse.runner.results.summary,
- org.pitest.pitclipse.runner.server,
- org.pitest.pitclipse.runner.service
+ org.pitest.pitclipse.runner.server
 Import-Package: com.google.common.annotations;version="21.0.0",
  com.google.common.base;version="21.0.0",
  com.google.common.collect;version="21.0.0",
diff --git a/bundles/org.pitest.pitclipse.runner/src/org/pitest/pitclipse/runner/config/PitConfiguration.java b/bundles/org.pitest.pitclipse.runner/src/org/pitest/pitclipse/runner/config/PitConfiguration.java
index 29aa006f..57da7c4d 100644
--- a/bundles/org.pitest.pitclipse.runner/src/org/pitest/pitclipse/runner/config/PitConfiguration.java
+++ b/bundles/org.pitest.pitclipse.runner/src/org/pitest/pitclipse/runner/config/PitConfiguration.java
@@ -23,6 +23,7 @@
 public class PitConfiguration {
     public static final String DEFAULT_AVOID_CALLS_TO_LIST = "java.util.logging, org.apache.log4j, org.slf4j, org.apache.commons.logging, org.apache.logging.log4j";
     public static final String DEFAULT_MUTATORS = "DEFAULTS";
+    public static final String DEFAULT_EXCLUDED_CLASSES = "*Test";
     public static final int DEFAULT_TIMEOUT = 3000;
     public static final BigDecimal DEFAULT_TIMEOUT_FACTOR = BigDecimal.valueOf(1.25);
 
@@ -36,7 +37,7 @@ public class PitConfiguration {
     private final int timeout;
     private final BigDecimal timeoutFactor;
 
-    private PitConfiguration(PitExecutionMode executionMode, boolean parallelExecution, boolean incrementalAnalysis,
+    private PitConfiguration(PitExecutionMode executionMode, boolean parallelExecution, boolean incrementalAnalysis, // NOSONAR this is used by our builder
             String excludedClasses, String excludedMethods, String avoidCallsTo, String mutators, int timeout,
             BigDecimal timeoutFactor) {
         this.executionMode = executionMode;
diff --git a/bundles/org.pitest.pitclipse.ui/src/org/pitest/pitclipse/ui/view/mutations/PitMutationsView.java b/bundles/org.pitest.pitclipse.ui/src/org/pitest/pitclipse/ui/view/mutations/PitMutationsView.java
index 26d86e2a..f9ff3b44 100644
--- a/bundles/org.pitest.pitclipse.ui/src/org/pitest/pitclipse/ui/view/mutations/PitMutationsView.java
+++ b/bundles/org.pitest.pitclipse.ui/src/org/pitest/pitclipse/ui/view/mutations/PitMutationsView.java
@@ -39,7 +39,6 @@ private void createTreeViewer(Composite parent) {
         viewer.setLabelProvider(new ViewLabelProvider());
         viewer.addDoubleClickListener(ExpandingDoubleClick.LISTENER);
         viewer.addDoubleClickListener(OpenMutationDoubleClick.LISTENER);
-        // viewer.addSelectionChangedListener(listener);
         viewer.setInput(MutationsModel.EMPTY_MODEL);
     }
 
diff --git a/releng/org.pitest.pitclipse.target/org.pitest.pitclipse.target.target b/releng/org.pitest.pitclipse.target/org.pitest.pitclipse.target.target
index de12515e..c8cbf21c 100644
--- a/releng/org.pitest.pitclipse.target/org.pitest.pitclipse.target.target
+++ b/releng/org.pitest.pitclipse.target/org.pitest.pitclipse.target.target
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde version="3.8"?>
-<target name="org.pitest.pitclipse.target" sequenceNumber="1">
+<target name="org.pitest.pitclipse.target" sequenceNumber="2">
 	<locations>
 		<location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="planner" includeSource="true" type="InstallableUnit">
 			<unit id="org.eclipse.e4.core.tools.feature.source.feature.group" version="0.0.0"/>
@@ -10,7 +10,7 @@
 			<unit id="org.eclipse.sdk.feature.group" version="0.0.0"/>
 			<unit id="org.eclipse.swtbot.eclipse.feature.group" version="0.0.0"/>
 			<unit id="org.eclipse.swtbot.feature.group" version="0.0.0"/>
-			<repository location="https://download.eclipse.org/releases/oxygen"/>
+			<repository location="https://download.eclipse.org/releases/photon"/>
 		</location>
 		<location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="planner" includeSource="true" type="InstallableUnit">
 			<unit id="com.google.guava" version="0.0.0"/>
diff --git a/tests/org.pitest.pitclipse.tests.coverage.report/pom.xml b/tests/org.pitest.pitclipse.tests.coverage.report/pom.xml
index a70cbc45..bc582c82 100644
--- a/tests/org.pitest.pitclipse.tests.coverage.report/pom.xml
+++ b/tests/org.pitest.pitclipse.tests.coverage.report/pom.xml
@@ -88,14 +88,11 @@
 			<version>${project.version}</version>
 			<scope>compile</scope>
 		</dependency>
-		<!-- Hangs in GitHub Actions so it's temporarily disabled -->
-		<!--
 		<dependency>
 			<groupId>org.pitest</groupId>
 			<artifactId>org.pitest.pitclipse.ui.tests</artifactId>
 			<version>${project.version}</version>
 			<scope>test</scope>
 		</dependency>
-		-->
 	</dependencies>
 </project>
diff --git a/tests/org.pitest.pitclipse.ui.tests/.gitignore b/tests/org.pitest.pitclipse.ui.tests/.gitignore
new file mode 100644
index 00000000..b19ec16d
--- /dev/null
+++ b/tests/org.pitest.pitclipse.ui.tests/.gitignore
@@ -0,0 +1 @@
+/screenshots/
diff --git a/tests/org.pitest.pitclipse.ui.tests/META-INF/MANIFEST.MF b/tests/org.pitest.pitclipse.ui.tests/META-INF/MANIFEST.MF
index 16654ce5..3e396c0b 100644
--- a/tests/org.pitest.pitclipse.ui.tests/META-INF/MANIFEST.MF
+++ b/tests/org.pitest.pitclipse.ui.tests/META-INF/MANIFEST.MF
@@ -24,4 +24,5 @@ Require-Bundle: org.eclipse.swtbot.eclipse.finder,
  org.pitest.pitclipse.launch;bundle-version="2.0.0",
  org.pitest.pitclipse.launch.ui;bundle-version="2.0.0",
  org.pitest.pitclipse.preferences.ui;bundle-version="2.0.0",
- io.cucumber;bundle-version="4.3.0"
+ io.cucumber;bundle-version="4.3.0",
+ org.eclipse.debug.core
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/BundleResourceClassLoader.java b/tests/org.pitest.pitclipse.ui.tests/old-cucumber/BundleResourceClassLoader.java
similarity index 100%
rename from tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/BundleResourceClassLoader.java
rename to tests/org.pitest.pitclipse.ui.tests/old-cucumber/BundleResourceClassLoader.java
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/OsgiCucumberRunner.java b/tests/org.pitest.pitclipse.ui.tests/old-cucumber/OsgiCucumberRunner.java
similarity index 100%
rename from tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/OsgiCucumberRunner.java
rename to tests/org.pitest.pitclipse.ui.tests/old-cucumber/OsgiCucumberRunner.java
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/PitclipseFeaturesTest.java b/tests/org.pitest.pitclipse.ui.tests/old-cucumber/PitclipseFeaturesTest.java
similarity index 100%
rename from tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/PitclipseFeaturesTest.java
rename to tests/org.pitest.pitclipse.ui.tests/old-cucumber/PitclipseFeaturesTest.java
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j00_default_options.feature b/tests/org.pitest.pitclipse.ui.tests/old-cucumber/j00_default_options.feature
similarity index 100%
rename from tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j00_default_options.feature
rename to tests/org.pitest.pitclipse.ui.tests/old-cucumber/j00_default_options.feature
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j01_simple_project.feature b/tests/org.pitest.pitclipse.ui.tests/old-cucumber/j01_simple_project.feature
similarity index 100%
rename from tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j01_simple_project.feature
rename to tests/org.pitest.pitclipse.ui.tests/old-cucumber/j01_simple_project.feature
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j02_tests_in_different_packages.feature b/tests/org.pitest.pitclipse.ui.tests/old-cucumber/j02_tests_in_different_packages.feature
similarity index 100%
rename from tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j02_tests_in_different_packages.feature
rename to tests/org.pitest.pitclipse.ui.tests/old-cucumber/j02_tests_in_different_packages.feature
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j03_refactored_tests_can_be_run.feature b/tests/org.pitest.pitclipse.ui.tests/old-cucumber/j03_refactored_tests_can_be_run.feature
similarity index 100%
rename from tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j03_refactored_tests_can_be_run.feature
rename to tests/org.pitest.pitclipse.ui.tests/old-cucumber/j03_refactored_tests_can_be_run.feature
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j04_multiple projects_in_workspace.feature b/tests/org.pitest.pitclipse.ui.tests/old-cucumber/j04_multiple projects_in_workspace.feature
similarity index 100%
rename from tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j04_multiple projects_in_workspace.feature
rename to tests/org.pitest.pitclipse.ui.tests/old-cucumber/j04_multiple projects_in_workspace.feature
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j05_default_preferences_apply_to_launch_configuration.feature b/tests/org.pitest.pitclipse.ui.tests/old-cucumber/j05_default_preferences_apply_to_launch_configuration.feature
similarity index 100%
rename from tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j05_default_preferences_apply_to_launch_configuration.feature
rename to tests/org.pitest.pitclipse.ui.tests/old-cucumber/j05_default_preferences_apply_to_launch_configuration.feature
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j06_mutations_view.feature b/tests/org.pitest.pitclipse.ui.tests/old-cucumber/j06_mutations_view.feature
similarity index 100%
rename from tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/j06_mutations_view.feature
rename to tests/org.pitest.pitclipse.ui.tests/old-cucumber/j06_mutations_view.feature
diff --git a/tests/org.pitest.pitclipse.ui.tests/org.pitest.pitclipse.ui.tests.launch b/tests/org.pitest.pitclipse.ui.tests/org.pitest.pitclipse.ui.tests.launch
index 3e7b377f..7df304b7 100644
--- a/tests/org.pitest.pitclipse.ui.tests/org.pitest.pitclipse.ui.tests.launch
+++ b/tests/org.pitest.pitclipse.ui.tests/org.pitest.pitclipse.ui.tests.launch
@@ -28,7 +28,7 @@
     <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog"/>
     <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.pitest.pitclipse.ui.tests"/>
     <stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
-    <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea"/>
+    <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Dorg.eclipse.swtbot.search.timeout=20000"/>
     <stringAttribute key="pde.version" value="3.3"/>
     <stringAttribute key="product" value="org.eclipse.platform.ide"/>
     <booleanAttribute key="show_selected_only" value="false"/>
diff --git a/tests/org.pitest.pitclipse.ui.tests/pom.xml b/tests/org.pitest.pitclipse.ui.tests/pom.xml
index cabb8c25..d2ba450a 100644
--- a/tests/org.pitest.pitclipse.ui.tests/pom.xml
+++ b/tests/org.pitest.pitclipse.ui.tests/pom.xml
@@ -1,9 +1,10 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<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/maven-v4_0_0.xsd">
 	<modelVersion>4.0.0</modelVersion>
-    <groupId>org.pitest</groupId>
-    <artifactId>org.pitest.pitclipse.ui.tests</artifactId>
-    <packaging>eclipse-test-plugin</packaging>
+	<groupId>org.pitest</groupId>
+	<artifactId>org.pitest.pitclipse.ui.tests</artifactId>
+	<packaging>eclipse-test-plugin</packaging>
 
 	<parent>
 		<groupId>org.pitest</groupId>
@@ -11,39 +12,112 @@
 		<version>2.1.2-SNAPSHOT</version>
 	</parent>
 
+	<properties>
+		<!-- For tests for Java 9 and above -->
+		<moduleProperties></moduleProperties>
+		<!-- OS specific flags -->
+		<os-jvm-flags />
+		<!-- Additional Test arguments -->
+		<additionalTestArgLine>${moduleProperties}</additionalTestArgLine>
+	</properties>
+
+	<profiles>
+		<profile>
+			<id>jdk9-or-newer</id>
+			<activation>
+				<jdk>[9,)</jdk>
+			</activation>
+			<properties>
+				<moduleProperties>--add-modules=ALL-SYSTEM</moduleProperties>
+			</properties>
+		</profile>
+		<profile>
+			<id>macosx-jvm-flags</id>
+			<activation>
+				<os>
+					<family>mac</family>
+				</os>
+			</activation>
+			<properties>
+				<os-jvm-flags>-XstartOnFirstThread</os-jvm-flags>
+			</properties>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.eclipse.tycho</groupId>
+						<artifactId>target-platform-configuration</artifactId>
+						<configuration>
+							<dependency-resolution>
+								<extraRequirements combine.children="append">
+									<!-- This allows us to take the fragment org.eclipse.jdt.launching.macosx 
+										See https://github.com/LorenzoBettini/jbase/issues/3 https://www.eclipse.org/forums/index.php/t/1073366/
+										Without this in macOS Java projects are not compiled since JRE is not bound. -->
+									<requirement>
+										<type>eclipse-feature</type>
+										<id>org.eclipse.jdt</id>
+										<versionRange>0.0.0</versionRange>
+									</requirement>
+								</extraRequirements>
+							</dependency-resolution>
+						</configuration>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+		<profile>
+			<id>jacoco</id>
+			<activation>
+				<activeByDefault>false</activeByDefault>
+			</activation>
+			<properties>
+				<additionalTestArgLine>${tycho.testArgLine} ${moduleProperties}</additionalTestArgLine>
+			</properties>
+		</profile>
+	</profiles>
+
 	<build>
-        <plugins>
-			<!-- 
-				Required to run SWTBot 
-			 -->
-            <plugin>
-                <groupId>org.eclipse.tycho</groupId>
-                <artifactId>tycho-surefire-plugin</artifactId>
-                <version>${tycho-version}</version>
-                <configuration>
-                    <useUIHarness>true</useUIHarness>
-                    <useUIThread>false</useUIThread>
-                    <product>org.eclipse.platform.ide</product>
-                    <application>org.eclipse.ui.ide.workbench</application>
-                    <showEclipseLog>true</showEclipseLog>
-                </configuration>
-            </plugin>
-			<!-- 
-				Explicit dependency to the listeners fragment:
-				make Pitclipse's mutation listeners available during test runtime 
-			 -->
+		<plugins>
+			<!-- Required to run SWTBot -->
+			<plugin>
+				<groupId>org.eclipse.tycho</groupId>
+				<artifactId>tycho-surefire-plugin</artifactId>
+				<version>${tycho-version}</version>
+				<configuration>
+					<useUIHarness>true</useUIHarness>
+					<useUIThread>false</useUIThread>
+					<showEclipseLog>true</showEclipseLog>
+					<!-- Increase the timeout for SWTBot especially for the CI -->
+					<argLine>${additionalTestArgLine} ${os-jvm-flags} -Dorg.eclipse.swtbot.search.timeout=60000</argLine>
+				</configuration>
+			</plugin>
+			<!-- Explicit dependency to the listeners fragment: make Pitclipse's mutation 
+				listeners available during test runtime -->
 			<plugin>
 				<groupId>org.eclipse.tycho</groupId>
 				<artifactId>target-platform-configuration</artifactId>
 				<version>${tycho-version}</version>
 				<configuration>
 					<dependency-resolution>
-						<extraRequirements>
+						<extraRequirements combine.children="append">
 							<requirement>
 								<type>eclipse-plugin</type>
 								<id>org.pitest.pitclipse.listeners</id>
 								<versionRange>0.0.0</versionRange>
 							</requirement>
+							<!-- This is required since our SWTBot tests do not
+								depend directly on JUnit 5 and we need JUnit 5 to verify
+								Pitclipse integration with JUnit 5 -->
+							<requirement>
+								<type>eclipse-plugin</type>
+								<id>org.eclipse.jdt.junit5.runtime</id>
+								<versionRange>0.0.0</versionRange>
+							</requirement>
+							<!-- This is required to verify Pitclipse integration with JUnit 5 -->
+							<requirement>
+								<type>eclipse-plugin</type>
+								<id>org.pitest.pitest-junit5-plugin</id>
+								<versionRange>0.0.0</versionRange>
+							</requirement>
 						</extraRequirements>
 					</dependency-resolution>
 				</configuration>
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/AbstractSyntaxTree.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/AbstractSyntaxTree.java
index a405e1da..16ce114b 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/AbstractSyntaxTree.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/AbstractSyntaxTree.java
@@ -53,7 +53,7 @@ private IJavaProject getJavaProject(ClassContext context) {
         return getJavaProject(context.getProjectName());
     }
 
-    private IJavaProject getJavaProject(String projectName) {
+    public IJavaProject getJavaProject(String projectName) {
         IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
         return JavaCore.create(project);
     }
@@ -80,9 +80,18 @@ public void deleteProject(String projectName) {
     }
 
     public void addJUnitToClassPath(String projectName) {
+        addJUnitToClassPath(projectName, 4);
+    }
+
+    public void addJUnit5ToClassPath(String projectName) {
+        addJUnitToClassPath(projectName, 5);
+    }
+
+    public void addJUnitToClassPath(String projectName, int junitVersion) {
         IJavaProject project = getJavaProject(projectName);
         try {
-            Path junitPath = new Path("org.eclipse.jdt.junit.JUNIT_CONTAINER/4");
+            Path junitPath = new Path(
+                "org.eclipse.jdt.junit.JUNIT_CONTAINER/" + junitVersion);
             IClasspathEntry junitEntry = JavaCore.newContainerEntry(junitPath);
             IClasspathEntry junitClasspath = JavaCore.newContainerEntry(junitEntry.getPath());
 
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/BuildProgress.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/BuildProgress.java
index 58ad22aa..ab8f8694 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/BuildProgress.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/BuildProgress.java
@@ -17,11 +17,12 @@
 package org.pitest.pitclipse.ui.behaviours.pageobjects;
 
 import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
+import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences;
 import org.pitest.pitclipse.ui.swtbot.WaitForBuildCondition;
 
 public class BuildProgress {
 
-    private static final long BUILD_TIMEOUT = 20000L;
+    private static final long BUILD_TIMEOUT = SWTBotPreferences.TIMEOUT;
     private final SWTWorkbenchBot bot;
     private WaitForBuildCondition buildCompleteCondition;
 
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/NewProjectWizard.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/NewProjectWizard.java
index 8d032c63..0995f6bc 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/NewProjectWizard.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/NewProjectWizard.java
@@ -34,8 +34,11 @@ public void createJavaProject(String projectName) {
         bot.tree().expandNode("Java").select("Java Project");
         bot.button("Next >").click();
         bot.textWithLabel("Project name:").setText(projectName);
+        // make sure to set Java 8 otherwise we get another dialog for module-info.java
+        bot.radio("Use an execution environment JRE:").click();
+        bot.comboBox().setSelection("JavaSE-1.8");
         bot.button("Finish").click();
-        
+
         // Ensure the project is fully created before moving on
         bot.waitUntil(Conditions.shellCloses(shell));
     }
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/PitMutationsView.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/PitMutationsView.java
index ef9fe0a1..bec966b0 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/PitMutationsView.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/PitMutationsView.java
@@ -16,10 +16,13 @@
 
 package org.pitest.pitclipse.ui.behaviours.pageobjects;
 
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
+import static org.pitest.pitclipse.ui.behaviours.pageobjects.PageObjects.PAGES;
+import static org.pitest.pitclipse.ui.behaviours.pageobjects.SwtBotTreeHelper.expand;
+
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Pattern;
 
-import junit.framework.AssertionFailedError;
 import org.eclipse.core.runtime.jobs.IJobManager;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
@@ -33,13 +36,10 @@
 import org.pitest.pitclipse.ui.behaviours.steps.PitMutation;
 import org.pitest.pitclipse.ui.view.mutations.OpenMutationDoubleClick;
 
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-import java.util.regex.Pattern;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
 
-import static org.pitest.pitclipse.ui.behaviours.pageobjects.PageObjects.PAGES;
-import static org.pitest.pitclipse.ui.behaviours.pageobjects.SwtBotTreeHelper.expand;
-import static org.pitest.pitclipse.ui.util.StepUtil.safeSleep;
+import junit.framework.AssertionFailedError;
 
 public class PitMutationsView {
 
@@ -50,13 +50,12 @@ public PitMutationsView(SWTWorkbenchBot bot) {
     }
 
     public List<PitMutation> getMutations() {
-        safeSleep(1000);
-        PAGES.views().closeConsole();
         SWTBotTree mutationTree = mutationTreeRoot();
         return mutationsFrom(mutationTree);
     }
 
     private SWTBotTree mutationTreeRoot() {
+        PAGES.views().waitForTestsAreRunOnConsole();
         SWTBotView mutationsView = bot.viewByTitle("PIT Mutations");
         mutationsView.show();
         // Make sure the 'PIT Mutations' view is opened
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/PitPreferenceSelector.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/PitPreferenceSelector.java
index 61063749..a53c1624 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/PitPreferenceSelector.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/PitPreferenceSelector.java
@@ -52,7 +52,6 @@ public void setPitExecutionMode(PitExecutionMode mode) {
         activatePreferenceShell();
         expandPitPreferences();
         selectExecutionMode(mode);
-        close();
     }
 
     private void selectExecutionMode(PitExecutionMode mode) {
@@ -164,12 +163,8 @@ public PreferenceGetterBuilder(PreferenceGetter<T> getter) {
 
         public T from(String label) {
             activatePreferenceShell();
-            try {
-                expandPitPreferences();
-                return getter.getPreference(label);
-            } finally {
-                close();
-            }
+            expandPitPreferences();
+            return getter.getPreference(label);
         }
     }
 
@@ -209,12 +204,8 @@ public void to(final int value) {
 
         private <T> void updatePreference(PreferenceSetter<T> s) {
             activatePreferenceShell();
-            try {
-                expandPitPreferences();
-                s.setPreference();
-            } finally {
-                close();
-            }
+            expandPitPreferences();
+            s.setPreference();
         }
     }
 
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/RunMenu.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/RunMenu.java
index e1dcb28b..a08a07d8 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/RunMenu.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/RunMenu.java
@@ -16,15 +16,16 @@
 
 package org.pitest.pitclipse.ui.behaviours.pageobjects;
 
+import java.util.List;
+
 import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
 import org.eclipse.swtbot.swt.finder.exceptions.WidgetNotFoundException;
 import org.eclipse.swtbot.swt.finder.widgets.SWTBotMenu;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell;
 import org.pitest.pitclipse.runner.PitOptions;
 import org.pitest.pitclipse.ui.swtbot.PitOptionsNotifier;
 import org.pitest.pitclipse.ui.swtbot.SWTBotMenuHelper;
 
-import java.util.List;
-
 public class RunMenu {
 
     private static final String RUN = "Run";
@@ -51,15 +52,28 @@ public void runPit() {
                                   .menu(RUN_AS);
         menuHelper.findMenu(runAsMenu, PIT_MUTATION_TEST)
                   .click();
-        try {
-            bot.shell("Select a Test Configuration")
-               .bot()
-               .button("OK")
-               .click();
-        } catch (WidgetNotFoundException e) {
-            // The 'Select a Test Configuration' dialog only appears when Pit has been launched
-            // at least once. If it is not found then PIT has been launched directly during the 
-            // click on 'Run As > PIT Mutation Test' so everything's alright.
+        
+        ensureSelectTestConfigurationDialogIsClosed();
+    }
+
+    /**
+     * The 'Select a Test Configuration' dialog only appears when Pit has been
+     * launched at least once. If it is not found then PIT has been launched
+     * directly during the click on 'Run As > PIT Mutation Test' so everything's
+     * alright.
+     * 
+     * This is a fast way for closing the dialog by iterating over the shells, instead
+     * of searching for such a shell swallowing {@link WidgetNotFoundException}.
+     */
+    private void ensureSelectTestConfigurationDialogIsClosed() {
+        SWTBotShell[] shells = bot.shells();
+        for (SWTBotShell shell : shells) {
+            if ("Select a Test Configuration".equals(shell.getText())) {
+                shell.bot()
+                    .button("OK")
+                    .click();
+                return;
+            }
         }
     }
 
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/Views.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/Views.java
index 789202fe..8197caf2 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/Views.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/Views.java
@@ -16,10 +16,12 @@
 
 package org.pitest.pitclipse.ui.behaviours.pageobjects;
 
+import java.util.List;
+
 import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
 import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
-
-import java.util.List;
+import org.eclipse.swtbot.swt.finder.SWTBot;
+import org.eclipse.swtbot.swt.finder.waits.ICondition;
 
 public class Views {
 
@@ -38,4 +40,67 @@ public void closeConsole() {
         }
     }
 
+    /**
+     * If the Console view can be found then clear it, to make sure we don't get
+     * output from previous test runs
+     */
+    public void clearConsole() {
+        List<SWTBotView> allViews = bot.views();
+        for (SWTBotView view : allViews) {
+            if ("Console".equals(view.getTitle())) {
+                view.show();
+                if (!view.bot().styledText().getText().isEmpty()) {
+                    // use the toolbar button instead of .bot().styledText().setText("")
+                    // which does not seem to work synchronously
+                    view.toolbarButton("Clear Console").click();
+                }
+                return;
+            }
+        }
+    }
+
+    public void waitForTestsAreRunOnConsole() {
+        System.out.println("Waiting for PIT to finish on Console...");
+        bot.waitUntil(new ICondition() {
+            static final String EXPECTED_END_STRING = "tests per mutation)";
+            String currentText = "";
+            long start = System.currentTimeMillis();
+
+            @Override
+            public boolean test() {
+                currentText = showConsole().bot()
+                    .styledText().getText()
+                    .trim();
+                final String end = currentText
+                    .substring(
+                        currentText.length() - EXPECTED_END_STRING.length(),
+                        currentText.length());
+                System.out.print("Console ends with: " + end);
+                boolean matched = EXPECTED_END_STRING.equals(end);
+                System.out.println
+                    ("... " +
+                     (System.currentTimeMillis() - start) + "ms" +
+                     (matched ? " OK!" : ""));
+                return matched;
+            }
+
+            @Override
+            public void init(SWTBot bot) {
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "Console View does not end with '" + EXPECTED_END_STRING + "'\n:"
+                        + "CURRENT CONSOLE TEXT:\n"
+                        + currentText;
+            }
+        });
+    }
+
+    public SWTBotView showConsole() {
+        SWTBotView consoleView = bot.viewByPartName("Console");
+        consoleView.show();
+        return consoleView;
+    }
+
 }
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/WindowsMenu.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/WindowsMenu.java
index 5877ed9e..4a9024e2 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/WindowsMenu.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/pageobjects/WindowsMenu.java
@@ -16,13 +16,18 @@
 
 package org.pitest.pitclipse.ui.behaviours.pageobjects;
 
+import java.math.BigDecimal;
+
+import org.eclipse.jface.preference.PreferenceDialog;
 import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
+import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
+import org.eclipse.swtbot.swt.finder.utils.SWTUtils;
+import org.eclipse.swtbot.swt.finder.waits.Conditions;
+import org.eclipse.ui.dialogs.PreferencesUtil;
 import org.pitest.pitclipse.core.PitCoreActivator;
 import org.pitest.pitclipse.core.PitMutators;
 import org.pitest.pitclipse.runner.config.PitExecutionMode;
 
-import java.math.BigDecimal;
-
 public class WindowsMenu {
 
     private static final String WINDOWS = "Window";
@@ -102,8 +107,21 @@ public String getExcludedClasses() {
         return preferenceSelector.getExcludedClasses();
     }
 
-    private PreferenceDsl openPreferences() {
-        bot.menu(WINDOWS).menu(PREFERENCES).click();
+    public PreferenceDsl openPreferences() {
+        if (SWTUtils.isMac()) {
+            // on macOS we cannot open the Preferences dialog
+            // (it's under the application name and we cannot access it,
+            // using the keyboard shortcut does not seem to work either)
+            UIThreadRunnable.asyncExec(() -> {
+                PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(
+                    bot.activeShell().widget, null, null, null);
+                dialog.open();
+            });
+        } else {
+            bot.menu(WINDOWS).menu(PREFERENCES).click();
+        }
+        bot.waitUntil(Conditions.shellIsActive(PREFERENCES));
+        bot.shell(PREFERENCES).activate();
         return new PreferenceDsl();
     }
 
@@ -161,7 +179,7 @@ public BigDecimal getTimeoutFactor() {
         return openPreferences().andThen().getPitTimeoutFactor();
     }
 
-    private class PreferenceDsl {
+    public class PreferenceDsl {
         public PitPreferenceSelector andThen() {
             return preferenceSelector;
         }
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/steps/LaunchConfigurationSteps.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/steps/LaunchConfigurationSteps.java
index 96130ac8..531ad49f 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/steps/LaunchConfigurationSteps.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/steps/LaunchConfigurationSteps.java
@@ -103,7 +103,7 @@ public void launchConfigurationsMatch(DataTable configTable) {
         configurationsMatch(configTable.asMaps(), launchConfigurations);
     }
 
-    private void configurationsMatch(List<Map<String, String>> expectedRows, List<PitRunConfiguration> launchConfigurations) {
+    public void configurationsMatch(List<Map<String, String>> expectedRows, List<PitRunConfiguration> launchConfigurations) {
         launchConfigurations = new ArrayList<>(launchConfigurations);
         launchConfigurations.removeIf(conf -> conf.getName().contains("("));
         assertEquals("The number of existing configurations is not the expected one", expectedRows.size(), launchConfigurations.size());
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/steps/PitclipseSteps.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/steps/PitclipseSteps.java
index e4571ca8..58a40ba2 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/steps/PitclipseSteps.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/behaviours/steps/PitclipseSteps.java
@@ -16,19 +16,30 @@
 
 package org.pitest.pitclipse.ui.behaviours.steps;
 
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
+import static com.google.common.collect.ImmutableSet.copyOf;
+import static java.lang.Integer.parseInt;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.collection.IsEmptyCollection.empty;
+import static org.hamcrest.number.OrderingComparison.greaterThan;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.pitest.pitclipse.ui.behaviours.pageobjects.PageObjects.PAGES;
+import static org.pitest.pitclipse.ui.util.AssertUtil.assertDoubleEquals;
 
-import org.eclipse.core.resources.IncrementalProjectBuilder;
+import java.io.File;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
-import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotEditor;
-import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
 import org.eclipse.swtbot.swt.finder.exceptions.WidgetNotFoundException;
-import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
-import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
 import org.eclipse.swtbot.swt.finder.widgets.TimeoutException;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
@@ -37,35 +48,25 @@
 import org.pitest.pitclipse.runner.results.DetectionStatus;
 import org.pitest.pitclipse.ui.behaviours.pageobjects.PackageContext;
 import org.pitest.pitclipse.ui.behaviours.pageobjects.PitSummaryView;
+import org.pitest.pitclipse.ui.swtbot.PitNotifier;
 
-import java.io.File;
-import java.math.BigDecimal;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
 
 import cucumber.api.java.en.Then;
 import cucumber.api.java.en.When;
 import io.cucumber.datatable.DataTable;
 
-import static com.google.common.collect.ImmutableSet.copyOf;
-import static java.lang.Integer.parseInt;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.number.OrderingComparison.greaterThan;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.pitest.pitclipse.ui.behaviours.pageobjects.PageObjects.PAGES;
-import static org.pitest.pitclipse.ui.util.AssertUtil.assertDoubleEquals;
-
 public class PitclipseSteps {
 
     @When("test {word} in package {word} is run for project {word}")
     public void runTest(final String testClassName, final String packageName, final String projectName) throws CoreException {
+        // No need to do a full build: we should be now synchronized with building
         // Build the whole workspace to prevent random compilation failures
-        ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor());
-        
+        // ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor());
+        System.out.println
+            (String.format("Run PIT on: %s %s.%s",
+                projectName, packageName, testClassName));
         runPit(new SelectTestClass(testClassName, packageName, projectName));
     }
 
@@ -93,6 +94,10 @@ public void mutationsAre(DataTable tableOfMutations) {
     @When("the following mutation is selected")
     public void mutationIsSelected(DataTable tableOfMutations) {
         PitMutation mutation = mutationsFromExampleTable(tableOfMutations).get(0);
+        doubleClickMutationInMutationsView(mutation);
+    }
+
+    public void doubleClickMutationInMutationsView(PitMutation mutation) {
         PAGES.getPitMutationsView()
              .select(mutation);
     }
@@ -117,16 +122,25 @@ public void closeTheConsole() {
 
     @When("tests in package {word} are run for project {word}")
     public void runPackageTest(final String packageName, final String projectName) {
+        System.out.println
+            (String.format("Run PIT on: %s %s",
+                projectName, packageName));
         runPit(new SelectPackage(packageName, projectName));
     }
 
     @When("tests in source root {word} are run for project {word}")
     public void runPackageRootTest(final String packageRoot, final String projectName) {
+        System.out.println
+            (String.format("Run PIT on: %s %s",
+                projectName, packageRoot));
         runPit(new SelectPackageRoot(packageRoot, projectName));
     }
 
     @When("tests are run for project {word}")
     public void runProjectTest(String projectName) {
+        System.out.println
+            (String.format("Run PIT on: %s",
+                projectName));
         runPit(new SelectProject(projectName));
     }
 
@@ -136,10 +150,19 @@ public void runtimeOptionsMatch(DataTable configTable) {
         assertThat(configTable.height(), is(greaterThan(0)));
         assertThat(options, match(configTable.asMaps().get(0)));
     }
-    
+
+    public void runtimeOptionsMatch(Map<String, String> configMap) {
+        PitOptions options = PAGES.getRunMenu().getLastUsedPitOptions();
+        assertThat(options, match(configMap));
+    }
+
     private void runPit(Runnable runnable) {
-        assertPitCanRun();
-        
+        assertNoErrorsInWorkspace();
+        // make sure to clear the console to avoid interferences
+        // with the output of previous runs
+        PAGES.views().clearConsole();
+        // make sure notifications not read are cleared
+        PitNotifier.INSTANCE.reset();
         int retryCount = 20;
         int counter = 0;
         while (counter < retryCount) {
@@ -155,72 +178,52 @@ private void runPit(Runnable runnable) {
         }
     }
     
+    public void assertNoErrorsInWorkspace() {
+        Set<String> errors = errorsInWorkspace();
+        assertThat(errors, empty());
+    }
+    
     /**
-     * This method is an attempt to "fix" a flaky test, or at least
-     * to reduce its effects by:
-     *      1) failing fast (instead of freezing the whole build)
-     *      2) giving details about the failure
+     * Makes sure there are no errors in the Workspace, by accessing the error
+     * markers directly.
      * 
-     * The test seems to be flaky because of compilation errors produced
-     * by missing imports and that's why this method uses 'auto import'
-     * on all open editors as an attempt to work around the issue.
+     * IMPORTANT: do not retrieve errors from the "Problems" view because that view
+     * might be updated asynchronously: we might lose errors and when running the
+     * tests an error Dialog will popup, making the tests flaky.
      * 
-     * See https://github.com/pitest/pitclipse/issues/81
+     * @return
      */
-    private final void assertPitCanRun() {
-        Set<String> errors = errorsInProblemsView();
-        if (errors.isEmpty()) {
-            // So far, so good. Let's run PIT.
-            return;
-        }
-        for (SWTBotEditor editor : new SWTWorkbenchBot().editors()) {
-            editor.setFocus();
-            try {
-                PAGES.getSourceMenu().organizeImports();
-                PAGES.getSourceMenu().format();
-            } catch (TimeoutException e) {
-                System.err.println("Errors have been found, but attempt to fix them failed on editor " + editor.getTitle() + " => " + e.getMessage());
-            }
-        }
-        Set<String> errorsAfterCleaning = errorsInProblemsView();
-        if (! errorsAfterCleaning.isEmpty()) {
-            throw new IllegalStateException("Unexpected errors may prevent PIT from running. This is likely due to a flaky test; please relaunch the build." +
-                                            "\n    Errors are: " + errorsAfterCleaning);
-        }
-        else {
-            System.err.println("Unexpected errors have been detected before running PIT but have been succesfully fixed." +
-                               "\n      Errors were: " + errors);
+    private final Set<String> errorsInWorkspace() {
+        try {
+            return getErrorMarkers().stream()
+                .map(it -> {
+                    try {
+                        return it.getAttribute(IMarker.MESSAGE).toString();
+                    } catch (CoreException e) {
+                        return "";
+                    }
+                })
+                .filter(s -> !s.isEmpty())
+                .collect(Collectors.toSet());
+        } catch (CoreException e) {
+            throw new IllegalStateException(e);
         }
     }
     
-    /** @return all the Errors shown in the 'Problems' view */
-    private final Set<String> errorsInProblemsView() {
-        Set<String> errors = new HashSet<>();
-        String category = "Errors";
-        
-        SWTBotView view = new SWTWorkbenchBot().viewByPartName("Problems");
-        view.show();
-        SWTBotTree tree = view.bot().tree();
-        
-        for (SWTBotTreeItem item : tree.getAllItems()) {
-            String text = item.getText();
-            if (text != null && text.startsWith(category)) {
-                item.expand();
-                for (String problem : item.getNodes()) {
-                    // Sometimes "Unknown" errors are reported but do not seem to be relevant
-                    if (! "Unknown".equals(problem)) {
-                        errors.add(problem);
-                    }
-                }
-                break;
-            }
-        }
-        return errors;
+    public List<IMarker> getErrorMarkers() throws CoreException {
+        return Stream.of(getMarkers())
+            .filter(it -> it.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) == IMarker.SEVERITY_ERROR)
+            .collect(Collectors.toList());
+    }
+    
+    private IMarker[] getMarkers() throws CoreException {
+        return ResourcesPlugin.getWorkspace().getRoot().findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
     }
     
     private List<PitMutation> mutationsFromExampleTable(DataTable tableOfMutations) {
+        List<Map<String, String>> asMaps = tableOfMutations.asMaps();
         ImmutableList.Builder<PitMutation> projectsBuilder = ImmutableList.builder();
-        for (Map<String, String> mutationRow : tableOfMutations.asMaps()) {
+        for (Map<String, String> mutationRow : asMaps) {
             DetectionStatus status = DetectionStatus.valueOf(mutationRow.get("status"));
             String project = mutationRow.get("project");
             String pkg = mutationRow.get("package");
@@ -232,7 +235,6 @@ private List<PitMutation> mutationsFromExampleTable(DataTable tableOfMutations)
             projectsBuilder.add(pitMutation);
         }
         return projectsBuilder.build();
-        
     }
 
     private Matcher<PitOptions> match(final Map<String, String> optionRow) {
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/swtbot/PitNotifier.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/swtbot/PitNotifier.java
index 20797e20..a467182a 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/swtbot/PitNotifier.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/swtbot/PitNotifier.java
@@ -18,6 +18,9 @@
 
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences;
 
 public enum PitNotifier {
     INSTANCE;
@@ -26,12 +29,12 @@ public enum PitNotifier {
             1);
 
     public PitResultsView getResults() throws InterruptedException {
-        return resultQueue.take();
+        return resultQueue.poll(SWTBotPreferences.TIMEOUT, TimeUnit.MILLISECONDS);
     }
 
     public void notifyResults(PitResultsView resultsView)
             throws InterruptedException {
-        resultQueue.put(resultsView);
+        resultQueue.offer(resultsView, SWTBotPreferences.TIMEOUT, TimeUnit.MILLISECONDS);
     }
 
     public void reset() {
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/swtbot/PitResultNotifier.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/swtbot/PitResultNotifier.java
index ff845c8f..2c015a91 100644
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/swtbot/PitResultNotifier.java
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/swtbot/PitResultNotifier.java
@@ -17,16 +17,13 @@
 package org.pitest.pitclipse.ui.swtbot;
 
 import org.pitest.pitclipse.core.extension.point.ResultNotifier;
-import org.pitest.pitclipse.ui.PitclipseTestActivator;
 import org.pitest.pitclipse.ui.extension.point.PitUiUpdate;
 import org.pitest.pitclipse.ui.swtbot.ResultsParser.Summary;
 
 public class PitResultNotifier implements ResultNotifier<PitUiUpdate> {
     @Override
     public void handleResults(PitUiUpdate updateEvent) {
-        if (testsAreInProgress()) {
-            notifiyTestsOfHtmlResults(updateEvent);
-        }
+        notifiyTestsOfHtmlResults(updateEvent);
     }
 
     private void notifiyTestsOfHtmlResults(PitUiUpdate updateEvent) {
@@ -51,7 +48,4 @@ private void tryNotifyResults(PitResultsView view) {
         }
     }
 
-    private boolean testsAreInProgress() {
-        return PitclipseTestActivator.getDefault().areTestsInProgress();
-    }
 }
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/AbstractPitclipseSWTBotTest.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/AbstractPitclipseSWTBotTest.java
new file mode 100644
index 00000000..8e798dbc
--- /dev/null
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/AbstractPitclipseSWTBotTest.java
@@ -0,0 +1,425 @@
+/*******************************************************************************
+ * Copyright 2021 Lorenzo Bettini and contributors
+ *  
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License.  You may obtain a copy
+ * of the License at
+ *  
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ ******************************************************************************/
+package org.pitest.pitclipse.ui.tests;
+
+import static java.lang.Integer.parseInt;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.pitest.pitclipse.ui.behaviours.pageobjects.PageObjects.PAGES;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
+import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotEditor;
+import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.WorkbenchException;
+import org.eclipse.ui.intro.IIntroManager;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.pitest.pitclipse.core.PitCoreActivator;
+import org.pitest.pitclipse.launch.ui.PitLaunchShortcut;
+import org.pitest.pitclipse.runner.results.DetectionStatus;
+import org.pitest.pitclipse.ui.behaviours.pageobjects.PitRunConfiguration;
+import org.pitest.pitclipse.ui.behaviours.steps.LaunchConfigurationSteps;
+import org.pitest.pitclipse.ui.behaviours.steps.PitMutation;
+import org.pitest.pitclipse.ui.behaviours.steps.PitclipseSteps;
+
+/**
+ * @author Lorenzo Bettini
+ * 
+ */
+@RunWith(SWTBotJunit4ClassRunner.class)
+public abstract class AbstractPitclipseSWTBotTest {
+    protected static SWTWorkbenchBot bot;
+    private static int statusIndex = 0;
+    private static int projectIndex = 1;
+    private static int packageIndex = 2;
+    private static int classIndex = 3;
+    private static int lineIndex = 4;
+    private static int mutationIndex = 5;
+
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        bot = new SWTWorkbenchBot();
+
+        closeWelcomePage();
+        openJavaPerspective();
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        deleteAllProjects();
+        // DON'T CALL bot.resetWorkbench();
+        // otherwise the PIT views will be closed and will not
+        // be opened again on the next test
+        // (maybe because it saves the state of the closed views?)
+        bot.closeAllShells();
+        bot.saveAllEditors();
+        bot.closeAllEditors();
+    }
+
+    protected static void closeWelcomePage() throws InterruptedException {
+        Display.getDefault().syncExec(new Runnable() {
+            public void run() {
+                IIntroManager introManager = PlatformUI.getWorkbench().getIntroManager();
+                if (introManager.getIntro() != null) {
+                    introManager
+                            .closeIntro(introManager.getIntro());
+                }
+            }
+        });
+    }
+
+    protected static void openJavaPerspective() throws InterruptedException {
+        Display.getDefault().syncExec(new Runnable() {
+            public void run() {
+                IWorkbench workbench = PlatformUI.getWorkbench();
+                try {
+                    workbench.showPerspective("org.eclipse.jdt.ui.JavaPerspective",
+                            workbench.getActiveWorkbenchWindow());
+                } catch (WorkbenchException e) {
+                    e.printStackTrace();
+                }
+            }
+        });
+    }
+
+    protected static void createJavaProjectWithJUnit4(String projectName) {
+        PAGES.getBuildProgress().listenForBuild();
+        PAGES.getFileMenu().newJavaProject(projectName);
+        PAGES.getAbstractSyntaxTree().addJUnitToClassPath(projectName);
+        PAGES.getBuildProgress().waitForBuild();
+        assertNoErrorsInWorkspace();
+    }
+
+    protected static void createJavaProjectWithJUnit5(String projectName) {
+        PAGES.getBuildProgress().listenForBuild();
+        PAGES.getFileMenu().newJavaProject(projectName);
+        PAGES.getAbstractSyntaxTree().addJUnit5ToClassPath(projectName);
+        PAGES.getBuildProgress().waitForBuild();
+        assertNoErrorsInWorkspace();
+    }
+
+    protected static void assertNoErrorsInWorkspace() {
+        new PitclipseSteps().assertNoErrorsInWorkspace();
+    }
+
+    protected static void verifyProjectExists(String projectName) {
+        for (String project : PAGES.getPackageExplorer().getProjectsInWorkspace()) {
+            if (projectName.equals(project)) {
+                // Project does indeed exist
+                return;
+            }
+        }
+        fail("Project: " + projectName + " not found.");
+    }
+
+    protected static void deleteAllProjects() {
+        for (String project : PAGES.getPackageExplorer().getProjectsInWorkspace()) {
+            PAGES.getAbstractSyntaxTree().deleteProject(project);
+        }
+        File historyFile = PitCoreActivator.getDefault().getHistoryFile();
+        if (historyFile.exists()) {
+            assertTrue(historyFile.delete());
+        }
+    }
+
+    protected static void deleteSrcContents(String projectName) throws CoreException {
+        PAGES.getBuildProgress().listenForBuild();
+        IJavaProject javaProject = PAGES.getAbstractSyntaxTree().getJavaProject(projectName);
+        javaProject.getProject().getFolder("src").delete(true, null);
+        javaProject.getProject().getFolder("src").create(true, true, null);
+        PAGES.getBuildProgress().waitForBuild();
+    }
+
+    protected static void addToBuildPath(String dependentProject, String projectName) {
+        PAGES.getPackageExplorer().selectProject(projectName);
+        PAGES.getAbstractSyntaxTree().addProjectToClassPathOfProject(projectName, dependentProject);
+    }
+
+    /**
+     * See {@link #setClassContents(String, String, String, String)} for the
+     * method specification.
+     * 
+     * @param className
+     * @param packageName
+     * @param projectName
+     * @param method
+     */
+    protected static void createClassWithMethod(String className, String packageName, String projectName,
+            String method) {
+        createClass(className, packageName, projectName);
+        createMethod(className, packageName, projectName, method);
+    }
+
+    protected static void createClass(String className, String packageName, String projectName) {
+        PAGES.getBuildProgress().listenForBuild();
+        PAGES.getPackageExplorer().selectPackageRoot(projectName, "src");
+        // Cannot use the Package explorer right click context menu
+        // to create a class due to SWTBot bug 261360
+        PAGES.getFileMenu().createClass(packageName, className);
+        PAGES.getBuildProgress().waitForBuild();
+    }
+
+    /**
+     * See {@link #setClassContents(String, String, String, String)} for the
+     * method specification.
+     * 
+     * @param className
+     * @param packageName
+     * @param projectName
+     * @param method
+     */
+    protected static void createMethod(String className, String packageName, String projectName,
+            String method) {
+        setClassContents(className, packageName, projectName, method);
+    }
+
+    /**
+     * Sets the class contents: the package declaration and the class declaration
+     * are generated automatically, and only the contents to be inserted inside the class
+     * must be specified (thus, all types must be fully qualified since there's no way
+     * of specifying the imports).
+     * 
+     * Example:
+     * 
+     * <pre>
+     * setClassContents("FooTest", "foo.bar", TEST_PROJECT,
+     *          "@org.junit.Test\n"
+     *        + "public void fooTest3() {\n"
+     *        + "    org.junit.Assert.assertEquals(2,\n"
+     *        + "            new Foo().doFoo(1));\n"
+     *        + "}");
+     * </pre>
+     * 
+     * @param className
+     * @param packageName
+     * @param project
+     * @param contents
+     */
+    protected static void setClassContents(String className, String packageName, String project,
+            String contents) {
+        PAGES.getBuildProgress().listenForBuild();
+        SWTBotEditor editor = bot.editorByTitle(className + ".java");
+        editor.setFocus();
+        editor.toTextEditor().setText(
+        "package " + packageName + ";\n\n" +
+        "public class " + className + " {\n\n" +
+            indent(contents) + "\n\n" +
+        "}\n"
+        );
+        editor.save();
+        PAGES.getBuildProgress().waitForBuild();
+    }
+
+    protected static void removeMethods(String className, String packageName, String project) {
+        setClassContents(className, packageName, project, "\n\n");
+    }
+
+    private static String indent(String contents) {
+        return Stream.of(contents.split("\n"))
+            .map(s -> "    " + s)
+            .collect(Collectors.joining("\n"));
+    }
+
+    protected static void runTest(final String testClassName, final String packageName, final String projectName) throws CoreException {
+        new PitclipseSteps().runTest(testClassName, packageName, projectName);
+    }
+
+    protected static void runPackageTest(final String packageName, final String projectName) throws CoreException {
+        new PitclipseSteps().runPackageTest(packageName, projectName);
+    }
+
+    protected static void runPackageRootTest(final String packageRoot, final String projectName) throws CoreException {
+        new PitclipseSteps().runPackageRootTest(packageRoot, projectName);
+    }
+
+    protected static void runProjectTest(final String projectName) throws CoreException {
+        new PitclipseSteps().runProjectTest(projectName);
+    }
+
+    protected static void consoleContains(int generatedMutants, int killedMutants,
+            int killedPercentage,
+            int testsRun,
+            int testsPerMutations) {
+        PAGES.views().waitForTestsAreRunOnConsole();
+        SWTBotView consoleView = bot.viewByPartName("Console");
+        consoleView.show();
+        String consoleText = consoleView.bot()
+                .styledText().getText()
+                .replace("\r", "");
+        // System.out.println(consoleText);
+        assertThat(consoleText,
+            containsString(
+                String.format(
+                    ">> Generated %d mutations Killed %d (%d%%)\n"
+                  + ">> Ran %d tests (%d tests per mutation)",
+                  generatedMutants, killedMutants,
+                  killedPercentage, testsRun, testsPerMutations)
+            )
+        );
+    }
+
+    /**
+     * The expectedMutationsTable String argument represents the expected
+     * mutations table, this is an example of String:
+     * (the headers of the table are the following ones:)
+     * <pre>
+     *  status      | project  | package | class       | line | mutation
+     * </pre>
+     * 
+     * <pre>
+     * "NO_COVERAGE | project1 | foo.bar | foo.bar.Foo | 6 | Replaced integer addition with subtraction       \n" +
+     * "NO_COVERAGE | project1 | foo.bar | foo.bar.Foo | 6 | replaced int return with 0 for foo/bar/Foo::doFoo "
+     * </pre>
+     * 
+     * @param expectedMutationsTable
+     */
+    protected static void mutationsAre(String expectedMutationsTable) {
+        List<PitMutation> expectedMutations = new ArrayList<>();
+        String[] lines = expectedMutationsTable.split("\n");
+        for (String line : lines) {
+            PitMutation pitMutation = fromMutationLine(line);
+            expectedMutations.add(pitMutation);
+        }
+        mutationsAre(expectedMutations);
+    }
+
+    protected static PitMutation fromMutationLine(String line) {
+        String[] mutationRow = line.split("\\|");
+        DetectionStatus status = DetectionStatus.valueOf(mutationRow[statusIndex].trim());
+        String project = mutationRow[projectIndex].trim();
+        String pkg = mutationRow[packageIndex].trim();
+        String className = mutationRow[classIndex].trim();
+        int lineNum = parseInt(mutationRow[lineIndex].trim());
+        String mutation = mutationRow[mutationIndex].trim();
+        PitMutation pitMutation = PitMutation.builder().withStatus(status).withProject(project).withPackage(pkg)
+                .withClassName(className).withLineNumber(lineNum).withMutation(mutation).build();
+        return pitMutation;
+    }
+
+    protected static void mutationsAre(List<PitMutation> expectedMutations) {
+        List<PitMutation> actualMutations = PAGES.getPitMutationsView().getMutations();
+        assertThat(actualMutations, equalTo(expectedMutations));
+    }
+
+    protected static void coverageReportGenerated(int classes, double totalCoverage, double mutationCoverage) {
+        new PitclipseSteps().coverageReportGenerated(classes, totalCoverage, mutationCoverage);
+    }
+
+    /**
+     * The configTable String argument represents the expected
+     * runtime options, a two row table is expected with the same number
+     * of columns; the first row contains the keys and the second row contains the values.
+     * Example (the alignment is optional and spaces are trimmed):
+     * 
+     * <pre>
+     * "classUnderTest  | classesToMutate              | excludedClasses | excludedMethods | runInParallel | incrementalAnalysis | avoidCallsTo                                                               \n" +
+     * "foo.bar.FooTest | foo.bar.BarTest, foo.bar.Foo | *Test           |                 | true          | false               | java.util.logging, org.apache.log4j, org.slf4j, org.apache.commons.logging, org.apache.logging.log4j"
+     * </pre>
+     * 
+     * @param configTable
+     */
+    protected static void runtimeOptionsMatch(String configTable) {
+        new PitclipseSteps().runtimeOptionsMatch(fromTwoRowTableToMap(configTable));
+    }
+
+    protected static Map<String, String> fromTwoRowTableToMap(String table) {
+        Map<String, String> map = new HashMap<>();
+        String[] lines = table.split("\n");
+        assertThat("You must specify a row with keys and a row with values",
+            lines.length, equalTo(2));
+        String[] keyRow = lines[0].split("\\|");
+        String[] valueRow = lines[1].split("\\|");
+        assertThat("Keys and values do not match in number",
+            keyRow.length, equalTo(valueRow.length));
+        for (int i = 0; i < keyRow.length; ++i) {
+            map.put(keyRow[i].trim(), valueRow[i].trim());
+        }
+        return map;
+    }
+
+    /**
+     * The configTable String argument represents the expected
+     * launch configurations elements, a table with at least two rows is expected with the same number
+     * of columns; the first row contains the keys and other ones contain the values.
+     * Example (the alignment is optional and spaces are trimmed):
+     * 
+     * <pre>
+     *   "name    | runInParallel | useIncrementalAnalysis | excludedClasses | excludedMethods | avoidCallsTo                                                               \n"
+     * + "BarTest | true          | false                  | *Test           |                 | java.util.logging, org.apache.log4j, org.slf4j, org.apache.commons.logging, org.apache.logging.log4j \n"
+     * + "FooTest | true          | false                  | *Test           |                 | java.util.logging, org.apache.log4j, org.slf4j, org.apache.commons.logging, org.apache.logging.log4j"
+     * </pre>
+     * 
+     * @param configTable
+     */
+    protected static void launchConfigurationsMatch(String configTable) {
+        List<PitRunConfiguration> launchConfigurations = PAGES.getRunMenu().runConfigurations();
+        new LaunchConfigurationSteps().configurationsMatch
+            (fromTableToMap(configTable), launchConfigurations);
+    }
+
+    protected static List<Map<String, String>> fromTableToMap(String table) {
+        List<Map<String, String>> maps = new ArrayList<>();
+        String[] lines = table.split("\n");
+        assertThat("You must specify at least a row with keys and rows with values",
+            lines.length, greaterThanOrEqualTo(2));
+        String[] keyRow = lines[0].split("\\|");
+        for (int i = 1; i < lines.length; ++i) {
+            Map<String, String> map = new HashMap<>();
+            maps.add(map);
+            String[] valueRow = lines[i].split("\\|");
+            assertThat("Keys and values do not match in number",
+                keyRow.length, equalTo(valueRow.length));
+            for (int j = 0; j < keyRow.length; ++j) {
+                map.put(keyRow[j].trim(), valueRow[j].trim());
+            }
+        }
+        return maps;
+    }
+
+    protected static void removePitLaunchConfigurations() throws CoreException {
+        ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
+        ILaunchConfigurationType type =
+            manager.getLaunchConfigurationType(PitLaunchShortcut.PIT_CONFIGURATION_TYPE);
+        ILaunchConfiguration[] lcs = manager.getLaunchConfigurations(type);
+        for (ILaunchConfiguration launchConfiguration : lcs) {
+            launchConfiguration.delete();
+        }
+    }
+
+}
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipseOptionsTest.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipseOptionsTest.java
new file mode 100644
index 00000000..027bac99
--- /dev/null
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipseOptionsTest.java
@@ -0,0 +1,185 @@
+package org.pitest.pitclipse.ui.tests;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.pitest.pitclipse.core.PitMutators.STRONGER;
+import static org.pitest.pitclipse.runner.config.PitConfiguration.DEFAULT_AVOID_CALLS_TO_LIST;
+import static org.pitest.pitclipse.runner.config.PitConfiguration.DEFAULT_MUTATORS;
+import static org.pitest.pitclipse.runner.config.PitExecutionMode.PROJECT_ISOLATION;
+import static org.pitest.pitclipse.ui.behaviours.pageobjects.PageObjects.PAGES;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.pitest.pitclipse.core.PitCoreActivator;
+import org.pitest.pitclipse.core.PitMutators;
+import org.pitest.pitclipse.core.preferences.PitPreferences;
+import org.pitest.pitclipse.runner.config.PitConfiguration;
+import org.pitest.pitclipse.ui.behaviours.pageobjects.PitPreferenceSelector;
+
+/**
+ * @author Lorenzo Bettini
+ * 
+ */
+@RunWith(SWTBotJunit4ClassRunner.class)
+public class PitclipseOptionsTest extends AbstractPitclipseSWTBotTest {
+
+    private static final String TEST_PROJECT = "project1";
+    private static final String FOO_BAR_PACKAGE = "foo.bar";
+    private static final String FOO_CLASS = "Foo";
+    private static final String FOO_TEST_CLASS = "FooTest";
+    private static final String BAR_CLASS = "Bar";
+    private static final String BAR_TEST_CLASS = "BarTest";
+
+    @BeforeClass
+    public static void setupJavaProject() {
+        createJavaProjectWithJUnit4(TEST_PROJECT);
+        verifyProjectExists(TEST_PROJECT);
+        createClassWithMethod(FOO_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+             "public int f(int i) {\n"
+           + "    java.util.ArrayList<Object> pointless = new java.util.ArrayList<>();\n"
+           + "    if (pointless.size() == 1)\n"
+           + "        return i + 1;\n"
+           + "    else\n"
+           + "        return 0;\n"
+           + "}");
+        createClassWithMethod(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+            "@org.junit.Test public void badTest() {\n"
+          + "    " + FOO_CLASS + " x = new " + FOO_CLASS + "();\n"
+          + "    x.f(1);\n"
+          + "}");
+        createClassWithMethod(BAR_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+            "public int f(int i) {\n"
+          + "    java.util.ArrayList<Object> pointless = new java.util.ArrayList<>();\n"
+          + "    if (pointless.size() == 1)\n"
+          + "        return i + 1;\n"
+          + "    else\n"
+          + "        return 0;\n"
+          + "}");
+        createClassWithMethod(BAR_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+           "@org.junit.Test public void badTest() {\n"
+          + "    " + BAR_CLASS + " x = new " + BAR_CLASS + "();\n"
+          + "    x.f(1);\n"
+          + "}");
+    }
+
+    @Before
+    public void removeLaunchConfigurations() throws CoreException {
+        removePitLaunchConfigurations();
+    }
+
+    @Test
+    public void defaultOptions() {
+        PitPreferenceSelector selector = PAGES.getWindowsMenu().openPreferences().andThen();
+        assertEquals(PROJECT_ISOLATION, selector.getPitExecutionMode());
+        assertTrue(selector.isPitRunInParallel());
+        assertFalse(selector.isIncrementalAnalysisEnabled());
+        assertEquals("The 'Excluded Classes' preference has not the expected value", 
+                "*Test", selector.getExcludedClasses());
+        assertTrue(selector.getExcludedMethods().isEmpty());
+        assertThat(selector.getAvoidCallsTo(), equalTo(DEFAULT_AVOID_CALLS_TO_LIST));
+        assertThat(selector.getTimeout(), equalTo(PitConfiguration.DEFAULT_TIMEOUT));
+        assertThat(selector.getPitTimeoutFactor(), equalTo(PitConfiguration.DEFAULT_TIMEOUT_FACTOR));
+        assertThat(selector.getMutators().toString(), equalTo(DEFAULT_MUTATORS));
+        selector.close();
+    }
+
+    @Test
+    public void useDefaultMutators() throws CoreException {
+        runPackageTest(FOO_BAR_PACKAGE, TEST_PROJECT);
+        coverageReportGenerated(2, 80, 0);
+        mutationsAre(
+        "SURVIVED    | project1 | foo.bar | foo.bar.Bar |    7 | negated conditional\n" +
+        "SURVIVED    | project1 | foo.bar | foo.bar.Foo |    7 | negated conditional\n" +
+        "NO_COVERAGE | project1 | foo.bar | foo.bar.Bar |    8 | Replaced integer addition with subtraction\n" +
+        "NO_COVERAGE | project1 | foo.bar | foo.bar.Bar |    8 | replaced int return with 0 for foo/bar/Bar::f\n" +
+        "NO_COVERAGE | project1 | foo.bar | foo.bar.Foo |    8 | Replaced integer addition with subtraction\n" +
+        "NO_COVERAGE | project1 | foo.bar | foo.bar.Foo |    8 | replaced int return with 0 for foo/bar/Foo::f");
+    }
+
+    @Test
+    public void useStrongerMutators() throws CoreException {
+        // now set STRONGER mutators
+        PAGES.getWindowsMenu().setMutators(STRONGER);
+        try {
+            runPackageTest(FOO_BAR_PACKAGE, TEST_PROJECT);
+            coverageReportGenerated(2, 80, 0);
+            mutationsAre(
+            "SURVIVED    | project1 | foo.bar | foo.bar.Bar |    7 | negated conditional\n" +
+            "SURVIVED    | project1 | foo.bar | foo.bar.Bar |    7 | removed conditional - replaced equality check with false\n" +
+            "SURVIVED    | project1 | foo.bar | foo.bar.Foo |    7 | negated conditional\n" +
+            "SURVIVED    | project1 | foo.bar | foo.bar.Foo |    7 | removed conditional - replaced equality check with false\n" +
+            "NO_COVERAGE | project1 | foo.bar | foo.bar.Bar |    8 | Replaced integer addition with subtraction\n" +
+            "NO_COVERAGE | project1 | foo.bar | foo.bar.Bar |    8 | replaced int return with 0 for foo/bar/Bar::f\n" +
+            "NO_COVERAGE | project1 | foo.bar | foo.bar.Foo |    8 | Replaced integer addition with subtraction\n" +
+            "NO_COVERAGE | project1 | foo.bar | foo.bar.Foo |    8 | replaced int return with 0 for foo/bar/Foo::f");
+        } finally {
+            // it's crucial to reset it to the default or we break other tests
+            PAGES.getWindowsMenu().setMutators(PitMutators.DEFAULTS);
+        }
+    }
+
+    @Test
+    public void launchConfigurationsWithDefaultOptions() throws CoreException {
+        runTest(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+        coverageReportGenerated(2, 40, 0);
+        runtimeOptionsMatch(
+        "classUnderTest           | classesToMutate  | excludedClasses | excludedMethods | runInParallel | incrementalAnalysis | avoidCallsTo                                                               \n" +
+        "foo.bar.FooTest | foo.bar.BarTest, foo.bar.Foo, foo.bar.Bar, foo.bar.FooTest       | *Test           |                 | true          | false               | java.util.logging, org.apache.log4j, org.slf4j, org.apache.commons.logging, org.apache.logging.log4j"
+        );
+        launchConfigurationsMatch(
+        "name    | runInParallel | useIncrementalAnalysis | excludedClasses | excludedMethods | avoidCallsTo                                                               \n"
+      + "FooTest | true          | false                  | *Test           |                 | java.util.logging, org.apache.log4j, org.slf4j, org.apache.commons.logging, org.apache.logging.log4j"
+        );
+        runTest(BAR_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+        coverageReportGenerated(2, 40, 0);
+        launchConfigurationsMatch(
+        "name    | runInParallel | useIncrementalAnalysis | excludedClasses | excludedMethods | avoidCallsTo                                                               \n"
+      + "BarTest | true          | false                  | *Test           |                 | java.util.logging, org.apache.log4j, org.slf4j, org.apache.commons.logging, org.apache.logging.log4j |\n"
+      + "FooTest | true          | false                  | *Test           |                 | java.util.logging, org.apache.log4j, org.slf4j, org.apache.commons.logging, org.apache.logging.log4j"
+        );
+    }
+
+    @Test
+    public void launchConfigurationsWithChangedValues() throws CoreException {
+        try {
+            PitPreferenceSelector selector = PAGES.getWindowsMenu().openPreferences().andThen();
+            selector.setPitTimeoutConst(2000);
+            selector.setPitTimeoutFactor(2);
+            selector.setPitRunInParallel(false);
+            selector.setPitIncrementalAnalysisEnabled(true);
+            selector.setExcludedClasses("org.foo.*IntTest, *DbTest");
+            selector.setAvoidCallsTo("org.slf4j, org.apache");
+            selector.setExcludedMethods("*toString*, doNotMutateMe*");
+            selector.close();
+            runTest(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+            coverageReportGenerated(2, 40, 0);
+            runtimeOptionsMatch(
+            "classUnderTest  | timeoutConst | timeoutFactor | runInParallel | incrementalAnalysis | excludedClasses           | avoidCallsTo          | excludedMethods \n" +
+            "foo.bar.FooTest | 2000         | 2             | false         | true                | org.foo.*IntTest, *DbTest | org.slf4j, org.apache | *toString*, doNotMutateMe*"
+            );
+            launchConfigurationsMatch(
+            "name    | runInParallel | useIncrementalAnalysis | excludedClasses          | excludedMethods            | avoidCallsTo \n"
+          + "FooTest | false         | true                  | org.foo.*IntTest, *DbTest | *toString*, doNotMutateMe* | org.slf4j, org.apache"
+            );
+        } finally {
+            // reset default values
+            IPreferenceStore preferenceStore = PitCoreActivator.getDefault().getPreferenceStore();
+            preferenceStore.setValue(PitPreferences.TIMEOUT, PitConfiguration.DEFAULT_TIMEOUT);
+            preferenceStore.setValue(PitPreferences.TIMEOUT_FACTOR, PitConfiguration.DEFAULT_TIMEOUT_FACTOR.toString());
+            preferenceStore.setValue(PitPreferences.RUN_IN_PARALLEL, true);
+            preferenceStore.setValue(PitPreferences.INCREMENTAL_ANALYSIS, false);
+            preferenceStore.setValue(PitPreferences.EXCLUDED_CLASSES, PitConfiguration.DEFAULT_EXCLUDED_CLASSES);
+            preferenceStore.setValue(PitPreferences.AVOID_CALLS_TO, PitConfiguration.DEFAULT_AVOID_CALLS_TO_LIST);
+            preferenceStore.setValue(PitPreferences.EXCLUDED_METHODS, "");
+        }
+    }
+
+}
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipsePitMutationsViewTest.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipsePitMutationsViewTest.java
new file mode 100644
index 00000000..98a040e9
--- /dev/null
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipsePitMutationsViewTest.java
@@ -0,0 +1,66 @@
+package org.pitest.pitclipse.ui.tests;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.pitest.pitclipse.ui.behaviours.steps.PitMutation;
+import org.pitest.pitclipse.ui.behaviours.steps.PitclipseSteps;
+
+/**
+ * @author Lorenzo Bettini
+ * 
+ */
+@RunWith(SWTBotJunit4ClassRunner.class)
+public class PitclipsePitMutationsViewTest extends AbstractPitclipseSWTBotTest {
+
+    private static final String TEST_PROJECT = "project1";
+    private static final String FOO_BAR_PACKAGE = "foo.bar";
+    private static final String FOO_CLASS = "Foo";
+    private static final String FOO_TEST_CLASS = "FooTest";
+    private static final String BAR_CLASS = "Bar";
+    private static final String BAR_TEST_CLASS = "BarTest";
+
+    @Test
+    public void selectMutationOpensTheClassAtTheRightLineNumber() throws CoreException {
+        createJavaProjectWithJUnit4(TEST_PROJECT);
+        verifyProjectExists(TEST_PROJECT);
+        createClassWithMethod(FOO_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+             "public int f(int i) {\n"
+           + "    java.util.ArrayList<Object> pointless = new java.util.ArrayList<>();\n"
+           + "    if (pointless.size() == 1)\n"
+           + "        return i + 1;\n"
+           + "    else\n"
+           + "        return 0;\n"
+           + "}");
+        createClassWithMethod(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+            "@org.junit.Test public void badTest() {\n"
+          + "    " + FOO_CLASS + " x = new " + FOO_CLASS + "();\n"
+          + "    x.f(1);\n"
+          + "}");
+        createClassWithMethod(BAR_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+            "public int f(int i) {\n"
+          + "    java.util.ArrayList<Object> pointless = new java.util.ArrayList<>();\n"
+          + "    if (pointless.size() == 1)\n"
+          + "        return i + 1;\n"
+          + "    else\n"
+          + "        return 0;\n"
+          + "}");
+        createClassWithMethod(BAR_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+           "@org.junit.Test public void badTest() {\n"
+          + "    " + BAR_CLASS + " x = new " + BAR_CLASS + "();\n"
+          + "    x.f(1);\n"
+          + "}");
+        runPackageTest(FOO_BAR_PACKAGE, TEST_PROJECT);
+        coverageReportGenerated(2, 80, 0);
+        PitclipseSteps pitclipseSteps = new PitclipseSteps();
+        PitMutation mutation = fromMutationLine(
+        "SURVIVED    | project1 | foo.bar | foo.bar.Foo |    7 | negated conditional");
+        pitclipseSteps.doubleClickMutationInMutationsView(mutation);
+        pitclipseSteps.mutationIsOpened(FOO_CLASS + ".java", 7);
+        mutation = fromMutationLine(
+        "SURVIVED    | project1 | foo.bar | foo.bar.Bar |    7 | negated conditional");
+        pitclipseSteps.doubleClickMutationInMutationsView(mutation);
+        pitclipseSteps.mutationIsOpened(BAR_CLASS + ".java", 7);
+    }
+}
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipseUiRunnerJUnit5Test.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipseUiRunnerJUnit5Test.java
new file mode 100644
index 00000000..2ddc9542
--- /dev/null
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipseUiRunnerJUnit5Test.java
@@ -0,0 +1,54 @@
+package org.pitest.pitclipse.ui.tests;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Similar to {@link PitclipseUiRunnerTest} but using JUnit 5 and only executing
+ * a smaller number of tests.
+ * 
+ * @author Lorenzo Bettini
+ * 
+ */
+@RunWith(SWTBotJunit4ClassRunner.class)
+public class PitclipseUiRunnerJUnit5Test extends AbstractPitclipseSWTBotTest {
+
+    private static final String TEST_PROJECT = "project1";
+
+    @BeforeClass
+    public static void setupJavaProject() {
+        createJavaProjectWithJUnit5(TEST_PROJECT);
+        verifyProjectExists(TEST_PROJECT);
+    }
+
+    @Before
+    public void cleanProject() throws CoreException {
+        deleteSrcContents(TEST_PROJECT);
+    }
+
+    @Test
+    public void runPitAtPackageAndPackageRootAndProjectLevel() throws CoreException {
+        createClassWithMethod("Foo", "foo.bar", TEST_PROJECT,
+                "public int doFoo(int i) {\n"
+              + "    return i + 1;\n"
+              + "}");
+        createClassWithMethod("FooTest", "foo.bar", TEST_PROJECT,
+                "@org.junit.jupiter.api.Test\n"
+              + "public void fooTest3() {\n"
+              + "    org.junit.jupiter.api.Assertions\n"
+              + "        .assertEquals(2,\n"
+              + "            new Foo().doFoo(1));\n"
+              + "}");
+        runPackageTest("foo.bar", TEST_PROJECT);
+        consoleContains(2, 2, 100, 2, 1);
+        runPackageRootTest("src", TEST_PROJECT);
+        consoleContains(2, 2, 100, 2, 1);
+        runProjectTest(TEST_PROJECT);
+        consoleContains(2, 2, 100, 2, 1);
+    }
+
+}
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipseUiRunnerTest.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipseUiRunnerTest.java
new file mode 100644
index 00000000..e5ba20f6
--- /dev/null
+++ b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/tests/PitclipseUiRunnerTest.java
@@ -0,0 +1,136 @@
+package org.pitest.pitclipse.ui.tests;
+
+import java.util.Collections;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * @author Lorenzo Bettini
+ * 
+ */
+@RunWith(SWTBotJunit4ClassRunner.class)
+public class PitclipseUiRunnerTest extends AbstractPitclipseSWTBotTest {
+
+    private static final String TEST_PROJECT = "project1";
+    private static final String FOO_BAR_PACKAGE = "foo.bar";
+    private static final String FOO_CLASS = "Foo";
+    private static final String FOO_TEST_CLASS = "FooTest";
+
+    @BeforeClass
+    public static void setupJavaProject() {
+        createJavaProjectWithJUnit4(TEST_PROJECT);
+        verifyProjectExists(TEST_PROJECT);
+        createClass(FOO_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+        createClass(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+    }
+
+    @Test
+    public void emptyClassAndEmptyTest() throws CoreException {
+        removeMethods(FOO_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+        removeMethods(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+        runTest(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+        consoleContains(0, 0, 100, 0, 0);
+        mutationsAre(Collections.emptyList());
+        coverageReportGenerated(0, 100, 100);
+    }
+
+    @Test
+    public void emptyClassAndEmptyTestMethod() throws CoreException {
+        removeMethods(FOO_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+        createMethod(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+                "@org.junit.Test\n"
+              + "public void fooTest1() {\n"
+              + "    Foo foo = new Foo();\n"
+              + "}");
+        runTest(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+        consoleContains(0, 0, 100, 0, 0);
+        mutationsAre(Collections.emptyList());
+        coverageReportGenerated(0, 100, 100);
+    }
+
+    @Test
+    public void classWithMethodAndNoCoverageTestMethod() throws CoreException {
+        createMethod(FOO_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+                "public int doFoo(int i) {\n"
+              + "    return i + 1;\n"
+              + "}");
+        createMethod(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+                "@org.junit.Test\n"
+              + "public void fooTest1() {\n"
+              + "    Foo foo = new Foo();\n"
+              + "}");
+        runTest(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+        consoleContains(2, 0, 0, 0, 0);
+        mutationsAre(
+        "NO_COVERAGE | project1 | foo.bar | foo.bar.Foo |    6 | Replaced integer addition with subtraction       \n" +
+        "NO_COVERAGE | project1 | foo.bar | foo.bar.Foo |    6 | replaced int return with 0 for foo/bar/Foo::doFoo ");
+        coverageReportGenerated(1, 50, 0);
+    }
+
+    @Test
+    public void classWithMethodAndBadTestMethod() throws CoreException {
+        createMethod(FOO_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+                "public int doFoo(int i) {\n"
+              + "    return i + 1;\n"
+              + "}");
+        createMethod(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+                "@org.junit.Test\n"
+              + "public void fooTest2() {\n"
+              + "    new Foo().doFoo(1);\n"
+              + "}");
+        runTest(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+        consoleContains(2, 0, 0, 2, 1);
+        mutationsAre(
+        "SURVIVED | project1 | foo.bar | foo.bar.Foo |    6 | Replaced integer addition with subtraction       \n" +
+        "SURVIVED | project1 | foo.bar | foo.bar.Foo |    6 | replaced int return with 0 for foo/bar/Foo::doFoo ");
+        coverageReportGenerated(1, 100, 0);
+    }
+
+    @Test
+    public void classWithMethodAndBetterTestMethod() throws CoreException {
+        createMethod(FOO_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+                "public int doFoo(int i) {\n"
+              + "    return i + 1;\n"
+              + "}");
+        createMethod(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+                "@org.junit.Test\n"
+              + "public void fooTest3() {\n"
+              + "    org.junit.Assert.assertEquals(2,\n"
+              + "            new Foo().doFoo(1));\n"
+              + "}");
+        runTest(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT);
+        consoleContains(2, 2, 100, 2, 1);
+        mutationsAre(
+        "KILLED | project1 | foo.bar | foo.bar.Foo |    6 | Replaced integer addition with subtraction       \n" +
+        "KILLED | project1 | foo.bar | foo.bar.Foo |    6 | replaced int return with 0 for foo/bar/Foo::doFoo ");
+        coverageReportGenerated(1, 100, 100);
+    }
+
+    @Test
+    public void runPitAtPackageAndPackageRootAndProjectLevel() throws CoreException {
+        createMethod(FOO_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+                "public int doFoo(int i) {\n"
+              + "    return i + 1;\n"
+              + "}");
+        createMethod(FOO_TEST_CLASS, FOO_BAR_PACKAGE, TEST_PROJECT,
+                "@org.junit.Test\n"
+              + "public void fooTest3() {\n"
+              + "    org.junit.Assert.assertEquals(2,\n"
+              + "            new Foo().doFoo(1));\n"
+              + "}");
+        runPackageTest(FOO_BAR_PACKAGE, TEST_PROJECT);
+        consoleContains(2, 2, 100, 2, 1);
+        coverageReportGenerated(1, 100, 100);
+        runPackageRootTest("src", TEST_PROJECT);
+        consoleContains(2, 2, 100, 2, 1);
+        coverageReportGenerated(1, 100, 100);
+        runProjectTest(TEST_PROJECT);
+        consoleContains(2, 2, 100, 2, 1);
+        coverageReportGenerated(1, 100, 100);
+    }
+
+}
diff --git a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/util/StepUtil.java b/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/util/StepUtil.java
deleted file mode 100644
index cdd03a5d..00000000
--- a/tests/org.pitest.pitclipse.ui.tests/src/org/pitest/pitclipse/ui/util/StepUtil.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*******************************************************************************
- * Copyright 2012-2019 Phil Glover and contributors
- *  
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License.  You may obtain a copy
- * of the License at
- *  
- *  http://www.apache.org/licenses/LICENSE-2.0
- *  
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
- * License for the specific language governing permissions and limitations under
- * the License.
- ******************************************************************************/
-
-package org.pitest.pitclipse.ui.util;
-
-public class StepUtil {
-
-    private StepUtil() {
-    }
-
-    public static void safeSleep(long millis) {
-        try {
-            Thread.sleep(millis);
-        } catch (InterruptedException e) {
-            // Swallowed - no pretence of handling this. Unlikely to hit this in
-            // test code.
-        }
-    }
-}
diff --git a/tests/pom.xml b/tests/pom.xml
index 8af5e499..1ed15bfb 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -21,8 +21,7 @@
 	<modules>
         <!-- Test fragments -->
         <module>org.pitest.pitclipse.runner.tests</module>
-        <!-- Hangs in GitHub Actions so it's temporarily disabled -->
-        <!-- <module>org.pitest.pitclipse.ui.tests</module> -->
+        <module>org.pitest.pitclipse.ui.tests</module>
 
         <!-- Test dependencies -->
         <module>io.cucumber</module>