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>