/* * Copyright 2010 Henry Coles * * 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.mutationtest; import static org.junit.Assert.assertEquals; import static org.pitest.mutationtest.DetectionStatus.KILLED; import static org.pitest.mutationtest.DetectionStatus.NO_COVERAGE; import static org.pitest.mutationtest.DetectionStatus.RUN_ERROR; import static org.pitest.mutationtest.DetectionStatus.SURVIVED; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.Test; import org.junit.experimental.categories.Category; import org.pitest.SystemTest; import org.pitest.classpath.ClassPath; import org.pitest.help.PitHelpError; import org.pitest.testapi.TestGroupConfig; import org.pitest.testng.TestNGConfiguration; import org.pitest.util.FileUtil; import org.pitest.util.IsolationUtils; import com.example.BeforeAfterClassTest; import com.example.CoveredByABeforeAfterClassTest; import com.example.CoveredByEasyMock; import com.example.CoveredByJUnitThreeSuite; import com.example.CrashesJVMWhenMutated; import com.example.FailsTestWhenEnvVariableSetTestee; import com.example.FullyCoveredTestee; import com.example.FullyCoveredTesteeTest; import com.example.HasMutationInFinallyBlockNonTest; import com.example.HasMutationInFinallyBlockTest; import com.example.HasMutationsInFinallyBlock; import com.example.JUnitThreeSuite; import com.example.KeepAliveThread; import com.example.MultipleMutations; @Category(SystemTest.class) public class MutationCoverageReportSystemTest extends ReportTestBase { private static final int ONE_MINUTE = 60000; @Test public void shouldPickRelevantTestsAndKillMutationsBasedOnCoverageData() { this.data.setTargetClasses(predicateFor("com.example.FullyCovered*")); this.data.setVerbose(true); createAndRun(); verifyResults(KILLED); } @Test public void shouldPickRelevantTestsAndKillMutationsBasedOnCoverageDataWhenLimitedByClassReach() { this.data.setDependencyAnalysisMaxDistance(2); this.data.setTargetTests(predicateFor("com.example.*FullyCovered*")); this.data.setTargetClasses(predicateFor("com.example.FullyCovered*")); createAndRun(); verifyResults(KILLED); } @Test public void shouldReportUnCoveredMutations() { this.data.setTargetClasses(predicateFor("com.example.PartiallyCovered*")); createAndRun(); verifyResults(KILLED, NO_COVERAGE); } @Test public void shouldReportSurvivingMutations() { this.data .setTargetClasses(predicateFor("com.example.CoveredButOnlyPartiallyTested*")); createAndRun(); verifyResults(KILLED, SURVIVED); } @Test public void shouldKillMutationsInStaticInitializersWhenThereIsCoverageAndMutateStaticFlagIsSet() { this.data.setMutateStaticInitializers(true); this.data .setTargetClasses(predicateFor("com.example.HasMutableStaticInitializer*")); createAndRun(); verifyResults(KILLED); } @Test public void shouldNotCreateMutationsInStaticInitializersWhenFlagNotSet() { this.data.setMutateStaticInitializers(false); this.data .setTargetClasses(predicateFor("com.example.HasMutableStaticInitializer*")); createAndRun(); verifyResults(); } @Test(expected = PitHelpError.class) public void shouldFailRunWithHelpfulMessageIfTestsNotGreen() { setMutators("MATH"); this.data .setTargetClasses(predicateFor("com.example.FailsTestWhenEnvVariableSet*")); this.data.addChildJVMArgs(Arrays.asList("-D" + FailsTestWhenEnvVariableSetTestee.class.getName() + "=true")); createAndRun(); // should not get here } @Test public void shouldOnlyRunTestsMathchingSuppliedFilter() { this.data.setMutateStaticInitializers(true); this.data .setTargetClasses(predicateFor(com.example.HasMutableStaticInitializer.class)); this.data .setTargetTests(predicateFor(com.example.HasMutableStaticInitializerTest.class)); createAndRun(); verifyResults(KILLED); } @Test public void shouldLoadResoucesOffClassPathFromFolderWithSpaces() { setMutators("RETURN_VALS"); this.data .setTargetClasses(predicateFor("com.example.LoadsResourcesFromClassPath*")); createAndRun(); verifyResults(KILLED); } @Test public void shouldPickRelevantTestsFromSuppliedTestSuites() { this.data.setTargetClasses(predicateFor("com.example.FullyCovered*")); this.data .setTargetTests(predicateFor(com.example.SuiteForFullyCovered.class)); createAndRun(); verifyResults(KILLED); } @Test public void shouldNotMutateMethodsMatchingExclusionPredicate() { this.data.setTargetClasses(predicateFor("com.example.HasExcludedMethods*")); this.data.setExcludedMethods(predicateFor("excludeMe")); createAndRun(); verifyResults(); } @Test public void shouldLimitNumberOfMutationsPerClass() { this.data.setTargetClasses(predicateFor(MultipleMutations.class)); this.data .setTargetTests(predicateFor(com.example.FullyCoveredTesteeTest.class)); this.data.setMaxMutationsPerClass(1); createAndRun(); verifyResults(NO_COVERAGE); } @Test public void shouldWorkWithEasyMock() { this.data.setTargetClasses(predicateFor(CoveredByEasyMock.class)); this.data.setTargetTests(predicateFor(com.example.EasyMockTest.class)); createAndRun(); verifyResults(KILLED, KILLED, KILLED); } @Test public void shouldWorkWithMockitoJUnitRunner() { this.data.setTargetClasses(predicateFor("com.example.MockitoCallFoo")); this.data.setTargetTests(predicateFor(com.example.MockitoRunnerTest.class)); this.data.setVerbose(true); createAndRun(); verifyResults(KILLED); } @Test(expected = PitHelpError.class) public void shouldReportHelpfulErrorIfNoMutationsFounds() { this.data.setFailWhenNoMutations(true); this.data.setTargetClasses(predicateFor("foo")); createAndRun(); } @Test public void shouldExcludeFilteredTests() { this.data.setTargetTests(predicateFor("com.example.*FullyCoveredTestee*")); this.data.setTargetClasses(predicateFor("com.example.FullyCovered*")); this.data.setExcludedClasses(predicateFor(FullyCoveredTesteeTest.class)); createAndRun(); verifyResults(NO_COVERAGE); } @Test public void willAllowExcludedClassesToBeReIncludedViaSuite() { this.data .setTargetTests(predicateFor("com.example.*SuiteForFullyCovered*")); this.data.setTargetClasses(predicateFor("com.example.FullyCovered*")); this.data.setExcludedClasses(predicateFor(FullyCoveredTesteeTest.class)); createAndRun(); verifyResults(KILLED); } @Test(expected = PitHelpError.class) public void shouldExcludeFilteredClasses() { this.data.setFailWhenNoMutations(true); this.data.setTargetClasses(predicateFor(FullyCoveredTestee.class)); this.data.setExcludedClasses(predicateFor(FullyCoveredTestee.class)); createAndRun(); } @Test public void shouldMutateClassesSuppliedToAlternateClassPath() throws IOException { // yes, this is horrid final String location = FileUtil.randomFilename() + ".jar"; try { final FileOutputStream fos = new FileOutputStream(location); final InputStream stream = IsolationUtils.getContextClassLoader() .getResourceAsStream("outofcp.jar"); copy(stream, fos); fos.close(); this.data.setTargetClasses(predicateFor("com.outofclasspath.*Mutee*")); this.data.setTargetTests(predicateFor("com.outofclasspath.*")); List<String> cp = new ArrayList<String>(); cp.addAll(ClassPath.getClassPathElementsAsPaths()); cp.add(location); this.data.setClassPathElements(cp); this.data.setDependencyAnalysisMaxDistance(-1); this.data.setExcludedClasses(predicateFor("*Power*", "*JMockit*")); createAndRun(); verifyResults(KILLED); } finally { new File(location).delete(); } } @Test public void shouldSupportTestNG() { this.data .setTargetClasses(predicateFor("com.example.testng.FullyCovered*")); this.data.setVerbose(true); createAndRun(new TestNGConfiguration(new TestGroupConfig( Collections.<String> emptyList(), Collections.<String> emptyList()))); verifyResults(KILLED); } @Test(timeout = ONE_MINUTE) public void shouldTerminateWhenThreadpoolCreated() { this.data.setTargetClasses(predicateFor(KeepAliveThread.class)); this.data .setTargetTests(predicateFor(com.example.KeepAliveThreadTest.class)); createAndRun(); verifyResults(SURVIVED); } @Test public void shouldMarkChildJVMCrashesAsRunErrors() { setMutators("NEGATE_CONDITIONALS"); this.data.setTargetClasses(predicateFor(CrashesJVMWhenMutated.class)); this.data .setTargetTests(predicateFor(com.example.TestCrashesJVMWhenMutated.class)); createAndRun(); verifyResults(RUN_ERROR); } @Test public void shouldCombineAndKillInlinedMutationsInFinallyBlocks() { setMutators("INCREMENTS"); this.data.setTargetClasses(predicateFor(HasMutationsInFinallyBlock.class)); this.data.setTargetTests(predicateFor(HasMutationInFinallyBlockTest.class)); this.data.setDetectInlinedCode(true); createAndRun(); verifyResults(KILLED); } @Test public void shouldUseTestsDefinedInASuppliedJUnitThreeSuite() { setMutators("RETURN_VALS"); this.data.setTargetClasses(predicateFor(CoveredByJUnitThreeSuite.class)); this.data.setTargetTests(predicateFor(JUnitThreeSuite.class)); this.data.setVerbose(true); createAndRun(); verifyResults(KILLED); } @Test public void shouldReportCombinedCoveredButNotTestedMutationsInFinallyBlocksAsSurvived() { setMutators("INCREMENTS"); this.data.setTargetClasses(predicateFor(HasMutationsInFinallyBlock.class)); this.data .setTargetTests(predicateFor(HasMutationInFinallyBlockNonTest.class)); this.data.setDetectInlinedCode(true); createAndRun(); verifyResults(SURVIVED); } @Test public void shouldExitAfterFirstFailureWhenTestClassAnnotatedWithBeforeClass() { setMutators("RETURN_VALS"); this.data .setTargetClasses(predicateFor(CoveredByABeforeAfterClassTest.class)); this.data.setTargetTests(predicateFor(BeforeAfterClassTest.class)); createAndRun(); verifyResults(KILLED); assertEquals(1, this.metaDataExtractor.getNumberOfTestsRun()); } @Test public void shouldKillMutationsWhenMutationsPreventsConstructionOfTestClass() { setMutators("RETURN_VALS"); this.data .setTargetClasses(predicateFor(com.example.mutatablecodeintest.Mutee.class)); this.data .setTargetTests(predicateFor(com.example.mutatablecodeintest.MuteeTest.class)); createAndRun(); verifyResults(KILLED); } @Test public void shouldKillMutationsWhenKillingTestClassContainsAnIgnoreOnAnotherMethod() { setMutators("RETURN_VALS"); this.data .setTargetClasses(predicateFor(com.example.testhasignores.Mutee.class)); this.data .setTargetTests(predicateFor(com.example.testhasignores.MuteeTest.class)); createAndRun(); verifyResults(KILLED); } private static void copy(final InputStream in, final OutputStream out) throws IOException { // Read bytes and write to destination until eof final byte[] buf = new byte[1024]; int len = 0; while ((len = in.read(buf)) >= 0) { out.write(buf, 0, len); } } }