package org.pitest.coverage.execute;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.pitest.SystemTest;
import org.pitest.classinfo.ClassName;
import org.pitest.classpath.ClassPath;
import org.pitest.coverage.BlockLocation;
import org.pitest.coverage.CoverageResult;
import org.pitest.functional.F;
import org.pitest.functional.FCollection;
import org.pitest.functional.FunctionalList;
import org.pitest.functional.MutableList;
import org.pitest.functional.SideEffect1;
import org.pitest.functional.predicate.Predicate;
import org.pitest.junit.JUnitCompatibleConfiguration;
import org.pitest.mutationtest.engine.Location;
import org.pitest.mutationtest.engine.MethodName;
import org.pitest.mutationtest.execute.DefaultPITClassloader;
import org.pitest.mutationtest.tooling.JarCreatingJarFinder;
import org.pitest.process.LaunchOptions;
import org.pitest.process.ProcessArgs;
import org.pitest.testapi.TestGroupConfig;
import org.pitest.util.ExitCode;
import org.pitest.util.IsolationUtils;
import org.pitest.util.SocketFinder;
import com.example.coverage.execute.samples.exceptions.CoveredBeforeExceptionTestee;
import com.example.coverage.execute.samples.exceptions.TestThrowsExceptionFromLargeMethodTestee;
import com.example.coverage.execute.samples.exceptions.TestThrowsExceptionInFinallyBlock;
import com.example.coverage.execute.samples.exceptions.TestsClassWithException;
import com.example.coverage.execute.samples.exceptions.ThrowsExceptionFromLargeMethodTestee;
import com.example.coverage.execute.samples.exceptions.ThrowsExceptionInFinallyBlockTestee;
import com.example.coverage.execute.samples.exceptions.ThrowsExceptionTestee;
import com.example.coverage.execute.samples.simple.Testee;
import com.example.coverage.execute.samples.simple.Testee2;
import com.example.coverage.execute.samples.simple.TesteeWithComplexConstructorsTest;
import com.example.coverage.execute.samples.simple.TesteeWithMultipleLines;
import com.example.coverage.execute.samples.simple.Tests;
import com.example.coverage.execute.samples.simple.TestsForMultiBlockCoverage;
@Category(SystemTest.class)
public class CoverageProcessSystemTest {
private final MethodName foo = MethodName.fromString("foo");
@Test
public void shouldRecordSomeCoverage() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coverage = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertFalse(coverage.iterator().next().getCoverage().isEmpty());
}
// check all the specialised implementations broadly work
@Test
public void shouldCalculateCoverageForSingleBlockMethods()
throws IOException, InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test1", 1);
}
// @Test
// public void shouldCalculateCoverageFor2BlockMethods() throws IOException,
// InterruptedException, ExecutionException {
// final FunctionalList<CoverageResult> coveredClasses =
// runCoverageForTest(TestsForMultiBlockCoverage.class);
// assertCoverage(coveredClasses, "test2", 2);
// }
@Test
public void shouldCalculateCoverageFor3BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test3", 2);
}
@Test
public void shouldCalculateCoverageForConstructors() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TesteeWithComplexConstructorsTest.class);
assertTrue(coversBlock(coveredClasses, "testHigh", 0));
assertTrue(coversBlock(coveredClasses, "testHigh", 1));
assertFalse(coversBlock(coveredClasses, "testHigh", 2));
assertTrue(coversBlock(coveredClasses, "testLow", 0));
assertTrue(coversBlock(coveredClasses, "testLow", 2));
assertFalse(coversBlock(coveredClasses, "testLow", 1));
}
@Test
public void shouldCalculateCoverageFor4BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test4", 2);
}
@Test
public void shouldCalculateCoverageFor5BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test5", 2);
}
@Test
public void shouldCalculateCoverageForBlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test6", 2);
}
@Test
public void shouldCalculateCoverageFor7BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test7", 2);
}
@Test
public void shouldCalculateCoverageFor8BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test8", 2);
}
@Test
public void shouldCalculateCoverageFor9BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test9", 2);
}
@Test
public void shouldCalculateCoverageFor10BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test10", 2);
}
@Test
public void shouldCalculateCoverageFor11BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test11", 2);
}
@Test
public void shouldCalculateCoverageFor12BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test12", 2);
}
@Test
public void shouldCalculateCoverageFor13BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test13", 2);
}
@Test
public void shouldCalculateCoverageFor14BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test14", 2);
}
@Test
public void shouldCalculateCoverageFor15BlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "test15", 2);
}
@Test
public void shouldCalculateCoverageForLargeBlockMethods() throws IOException,
InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class);
assertCoverage(coveredClasses, "testMany", 2);
}
@Test
public void shouldCalculateCoverageForAllRelevantClasses()
throws IOException, InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(Tests.class);
assertTrue(coveredClasses.contains(coverageFor(Testee2.class)));
assertTrue(coveredClasses.contains(coverageFor(Testee.class)));
assertTrue(coveredClasses
.contains(coverageFor(TesteeWithMultipleLines.class)));
}
@Test
public void shouldCalculateCoverageForSmallMethodThatThrowsException()
throws IOException, InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestsClassWithException.class);
assertTrue(coveredClasses
.contains(coverageFor(CoveredBeforeExceptionTestee.class)));
ClassName throwsException = ClassName
.fromClass(ThrowsExceptionTestee.class);
assertTrue(coveredClasses.contains(coverageFor(BlockLocation.blockLocation(
Location.location(throwsException, this.foo, "()V"), 0))));
assertTrue(coveredClasses.contains(coverageFor(BlockLocation.blockLocation(
Location.location(throwsException,
MethodName.fromString("throwsException"), "()V"), 0))));
}
@Test
public void shouldCalculateCoverageForMethodThatThrowsExceptionWithFinallyBlock()
throws IOException, InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestThrowsExceptionInFinallyBlock.class);
ClassName clazz = ClassName
.fromClass(ThrowsExceptionInFinallyBlockTestee.class);
assertTrue(coveredClasses.contains(coverageFor(BlockLocation.blockLocation(
Location.location(clazz, this.foo, "()V"), 0))));
assertTrue(coveredClasses.contains(coverageFor(BlockLocation.blockLocation(
Location.location(clazz, this.foo, "()V"), 1))));
}
@Test
public void shouldCalculateCoverageForLargeMethodThatThrowsException()
throws IOException, InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestThrowsExceptionFromLargeMethodTestee.class);
ClassName clazz = ClassName
.fromClass(ThrowsExceptionFromLargeMethodTestee.class);
assertTrue(coveredClasses.contains(coverageFor(BlockLocation.blockLocation(
Location.location(clazz, this.foo, "()I"), 0))));
}
public static class TestInDifferentClassLoader {
@Test
public void testFoo() {
final ClassLoader cl = new DefaultPITClassloader(new ClassPath(),
IsolationUtils.bootClassLoader());
final Testee testee = new Testee();
final Runnable r = (Runnable) IsolationUtils.cloneForLoader(testee, cl);
r.run();
}
}
@Test
public void shouldCalculateCoverageOfClassesRunInDifferentClassLoader()
throws IOException, InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(TestInDifferentClassLoader.class);
assertTrue(coveredClasses.contains(coverageFor(Testee2.class)));
assertTrue(coveredClasses.contains(coverageFor(Testee.class)));
}
public static class ReliesOnNewLine {
public static String parseNewLines() throws IOException {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
pw.println("foo");
pw.println("bar");
final BufferedReader in = new BufferedReader(new StringReader(sw
.getBuffer().toString()));
return in.readLine();
}
}
public static class ReliesOnNewLineTest {
@Test
public void testNewLine() throws IOException {
assertEquals("foo", ReliesOnNewLine.parseNewLines());
}
}
@Test
public void shouldNotCorruptedTheSystemNewLineProperty() throws Exception {
final FunctionalList<CoverageResult> coveredClasses = runCoverageForTest(ReliesOnNewLineTest.class);
assertFalse(coveredClasses.contains(failingTest()));
}
@Test
public void shouldFailWithExitCode() throws Exception {
final SideEffect1<CoverageResult> noOpHandler = new SideEffect1<CoverageResult>() {
@Override
public void apply(final CoverageResult a) {
}
};
final CoverageOptions sa = new CoverageOptions(coverOnlyTestees(),
new JUnitCompatibleConfiguration(new TestGroupConfig(), Collections.<String>emptyList()), true, -1);
final JarCreatingJarFinder agent = new JarCreatingJarFinder();
final LaunchOptions lo = new LaunchOptions(agent);
final SocketFinder sf = new SocketFinder();
final CoverageProcess process = new CoverageProcess(ProcessArgs
.withClassPath(classPathWithoutJUnit()).andLaunchOptions(lo), sa,
sf.getNextAvailableServerSocket(),
Arrays.asList(TestsForMultiBlockCoverage.class.getName()), noOpHandler);
process.start();
final ExitCode exitCode = process.waitToDie();
assertEquals(ExitCode.JUNIT_ISSUE, exitCode);
}
private ClassPath classPathWithoutJUnit() {
FunctionalList<File> cpWithoutJUnit = FCollection.filter(
ClassPath.getClassPathElementsAsFiles(), new F<File, Boolean>() {
@Override
public Boolean apply(File file) {
return !file.getName().contains("junit");
}
});
return new ClassPath(cpWithoutJUnit);
}
private F<CoverageResult, Boolean> failingTest() {
return new F<CoverageResult, Boolean>() {
@Override
public Boolean apply(final CoverageResult a) {
return !a.isGreenTest();
}
};
}
private FunctionalList<CoverageResult> runCoverageForTest(final Class<?> test)
throws IOException, InterruptedException, ExecutionException {
final FunctionalList<CoverageResult> coveredClasses = new MutableList<CoverageResult>();
runCoverageProcess(test, coveredClasses);
return coveredClasses;
}
private void runCoverageProcess(final Class<?> test,
final FunctionalList<CoverageResult> coveredClasses) throws IOException,
InterruptedException {
final SideEffect1<CoverageResult> handler = new SideEffect1<CoverageResult>() {
@Override
public void apply(final CoverageResult a) {
coveredClasses.add(a);
}
};
final CoverageOptions sa = new CoverageOptions(coverOnlyTestees(),
new JUnitCompatibleConfiguration(new TestGroupConfig(), Collections.<String>emptyList()), true, -1);
final JarCreatingJarFinder agent = new JarCreatingJarFinder();
try {
final LaunchOptions lo = new LaunchOptions(agent);
final SocketFinder sf = new SocketFinder();
final CoverageProcess process = new CoverageProcess(ProcessArgs
.withClassPath(new ClassPath()).andLaunchOptions(lo), sa,
sf.getNextAvailableServerSocket(), Arrays.asList(test.getName()),
handler);
process.start();
final ExitCode exitCode = process.waitToDie();
assertEquals(ExitCode.OK, exitCode);
} finally {
agent.close();
}
}
private F<CoverageResult, Boolean> coverageFor(final Class<?> class1) {
return new F<CoverageResult, Boolean>() {
@Override
public Boolean apply(final CoverageResult a) {
return FCollection.contains(a.getCoverage(), resultFor(class1));
}
private F<BlockLocation, Boolean> resultFor(final Class<?> class1) {
return new F<BlockLocation, Boolean>() {
@Override
public Boolean apply(final BlockLocation a) {
return a.isFor(ClassName.fromClass(class1));
}
};
}
};
}
private F<CoverageResult, Boolean> coverageFor(final BlockLocation location) {
return new F<CoverageResult, Boolean>() {
@Override
public Boolean apply(final CoverageResult a) {
return a.getCoverage().contains(location);
}
};
}
private Predicate<String> coverOnlyTestees() {
return new Predicate<String>() {
@Override
public Boolean apply(final String a) {
return a.contains("Testee") && !a.endsWith("Test");
}
};
}
private F<CoverageResult, Boolean> coverage(final String testName,
final int numberOfBlocks) {
return new F<CoverageResult, Boolean>() {
@Override
public Boolean apply(final CoverageResult a) {
return a.getTestUnitDescription().getName().startsWith(testName)
&& (a.getNumberOfCoveredBlocks() == numberOfBlocks);
}
};
}
private void assertCoverage(
final FunctionalList<CoverageResult> coveredClasses,
final String testName, final int numberOfBlocks) {
assertTrue(coveredClasses.contains(coverage(testName, numberOfBlocks)));
}
private boolean coversBlock(
final FunctionalList<CoverageResult> coveredClasses,
final String testName, final int block) {
return coveredClasses.contains(hitsBlock(testName, block));
}
private F<CoverageResult, Boolean> hitsBlock(final String testName,
final int block) {
return new F<CoverageResult, Boolean>() {
@Override
public Boolean apply(final CoverageResult a) {
return a.getTestUnitDescription().getName().startsWith(testName)
&& (FCollection.contains(a.getCoverage(), hasBlock(block)));
}
private F<BlockLocation, Boolean> hasBlock(final int block) {
return new F<BlockLocation, Boolean>() {
@Override
public Boolean apply(BlockLocation a) {
System.out.println(a);
return a.getBlock() == block;
}
};
}
};
}
}