package org.pitest.mutationtest.incremental;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import org.pitest.classinfo.ClassName;
import org.pitest.coverage.CoverageDatabase;
import org.pitest.coverage.TestInfo;
import org.pitest.functional.F;
import org.pitest.functional.FCollection;
import org.pitest.functional.Option;
import org.pitest.mutationtest.DetectionStatus;
import org.pitest.mutationtest.MutationAnalyser;
import org.pitest.mutationtest.MutationResult;
import org.pitest.mutationtest.MutationStatusTestPair;
import org.pitest.mutationtest.engine.MutationDetails;
import org.pitest.util.Log;
public class IncrementalAnalyser implements MutationAnalyser {
private static final Logger LOG = Log.getLogger();
private final CodeHistory history;
private final CoverageDatabase coverage;
private final Map<DetectionStatus, Long> preAnalysed = createStatusMap();
public IncrementalAnalyser(final CodeHistory history,
final CoverageDatabase coverage) {
this.history = history;
this.coverage = coverage;
}
private static Map<DetectionStatus, Long> createStatusMap() {
final EnumMap<DetectionStatus, Long> map = new EnumMap<DetectionStatus, Long>(DetectionStatus.class);
for (final DetectionStatus each : DetectionStatus.values()) {
map.put(each, 0L);
}
return map;
}
@Override
public Collection<MutationResult> analyse(
final Collection<MutationDetails> mutation) {
final List<MutationResult> mrs = new ArrayList<MutationResult>(
mutation.size());
for (final MutationDetails each : mutation) {
final Option<MutationStatusTestPair> maybeResult = this.history
.getPreviousResult(each.getId());
if (maybeResult.hasNone()) {
mrs.add(analyseFromScratch(each));
} else {
mrs.add(analyseFromHistory(each, maybeResult.value()));
}
}
logTotals();
return mrs;
}
private void logTotals() {
for (final Entry<DetectionStatus, Long> each : this.preAnalysed.entrySet()) {
if (each.getValue() != 0) {
LOG.fine("Incremental analysis set " + each.getValue()
+ " mutations to a status of " + each.getKey());
}
}
}
private MutationResult analyseFromHistory(final MutationDetails each,
final MutationStatusTestPair mutationStatusTestPair) {
final ClassName clazz = each.getClassName();
if (this.history.hasClassChanged(clazz)) {
return analyseFromScratch(each);
}
if (mutationStatusTestPair.getStatus() == DetectionStatus.TIMED_OUT) {
return makeResult(each, DetectionStatus.TIMED_OUT);
}
if ((mutationStatusTestPair.getStatus() == DetectionStatus.KILLED)
&& killingTestHasNotChanged(each, mutationStatusTestPair)) {
return makeResult(each, DetectionStatus.KILLED, mutationStatusTestPair
.getKillingTest().value());
}
if ((mutationStatusTestPair.getStatus() == DetectionStatus.SURVIVED)
&& !this.history.hasCoverageChanged(clazz,
this.coverage.getCoverageIdForClass(clazz))) {
return makeResult(each, DetectionStatus.SURVIVED);
}
return analyseFromScratch(each);
}
private boolean killingTestHasNotChanged(final MutationDetails each,
final MutationStatusTestPair mutationStatusTestPair) {
final Collection<TestInfo> allTests = this.coverage.getTestsForClass(each
.getClassName());
final List<ClassName> testClasses = FCollection.filter(allTests,
testIsCalled(mutationStatusTestPair.getKillingTest().value())).map(
TestInfo.toDefiningClassName());
if (testClasses.isEmpty()) {
return false;
}
return !this.history.hasClassChanged(testClasses.get(0));
}
private static F<TestInfo, Boolean> testIsCalled(final String testName) {
return new F<TestInfo, Boolean>() {
@Override
public Boolean apply(final TestInfo a) {
return a.getName().equals(testName);
}
};
}
private MutationResult analyseFromScratch(final MutationDetails mutation) {
return makeResult(mutation, DetectionStatus.NOT_STARTED);
}
private MutationResult makeResult(final MutationDetails each,
final DetectionStatus status) {
return makeResult(each, status, null);
}
private MutationResult makeResult(final MutationDetails each,
final DetectionStatus status, final String killingTest) {
updatePreanalysedTotal(status);
return new MutationResult(each, new MutationStatusTestPair(0, status,
killingTest));
}
private void updatePreanalysedTotal(final DetectionStatus status) {
if (status != DetectionStatus.NOT_STARTED) {
final long count = this.preAnalysed.get(status);
this.preAnalysed.put(status, count + 1);
}
}
}