org.evosuite.coverage.branch.OnlyBranchCoverageSuiteFitness Maven / Gradle / Ivy
/**
* Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* EvoSuite is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see .
*/
package org.evosuite.coverage.branch;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.evosuite.Properties;
import org.evosuite.TestGenerationContext;
import org.evosuite.ga.archive.Archive;
import org.evosuite.testcase.ExecutableChromosome;
import org.evosuite.testcase.TestChromosome;
import org.evosuite.testcase.TestFitnessFunction;
import org.evosuite.testcase.execution.ExecutionResult;
import org.evosuite.testsuite.AbstractTestSuiteChromosome;
import org.evosuite.testsuite.TestSuiteFitnessFunction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Fitness function for a whole test suite for all branches
*
* @author Gordon Fraser, Jose Miguel Rojas
*/
public class OnlyBranchCoverageSuiteFitness extends TestSuiteFitnessFunction {
private static final long serialVersionUID = 2991632394620406243L;
private final static Logger logger = LoggerFactory.getLogger(TestSuiteFitnessFunction.class);
// Coverage targets
public int totalBranches;
public int totalGoals;
private final Set branchesId;
// Some stuff for debug output
public int maxCoveredBranches = 0;
public double bestFitness = Double.MAX_VALUE;
// Each test gets a set of distinct covered goals, these are mapped by branch id
private final Map branchCoverageTrueMap = new LinkedHashMap();
private final Map branchCoverageFalseMap = new LinkedHashMap();
private final Set toRemoveBranchesT = new LinkedHashSet<>();
private final Set toRemoveBranchesF = new LinkedHashSet<>();
private final Set removedBranchesT = new LinkedHashSet<>();
private final Set removedBranchesF = new LinkedHashSet<>();
/**
*
* Constructor for OnlyBranchCoverageSuiteFitness.
*
*/
public OnlyBranchCoverageSuiteFitness() {
String prefix = Properties.TARGET_CLASS_PREFIX;
if (prefix.isEmpty()) {
prefix = Properties.TARGET_CLASS;
}
if (Properties.TARGET_METHOD.isEmpty()) {
totalBranches = BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).
getBranchCountForPrefix(prefix);
} else {
totalBranches = BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).
getBranchCountForMethod(prefix, Properties.TARGET_METHOD);
}
branchesId = new LinkedHashSet<>();
totalGoals = 2 * totalBranches;
logger.info("Total branch coverage goals: " + totalGoals);
logger.info("Total branches: " + totalBranches);
determineCoverageGoals();
assert totalGoals == this.branchCoverageTrueMap.size() + this.branchCoverageFalseMap.size();
}
/**
* Initialize the set of known coverage goals
*/
private void determineCoverageGoals() {
List goals = new OnlyBranchCoverageFactory().getCoverageGoals();
for (OnlyBranchCoverageTestFitness goal : goals) {
if(Properties.TEST_ARCHIVE)
Archive.getArchiveInstance().addTarget(goal);
branchesId.add(goal.getBranch().getActualBranchId());
if (goal.getBranchExpressionValue())
branchCoverageTrueMap.put(goal.getBranch().getActualBranchId(), goal);
else
branchCoverageFalseMap.put(goal.getBranch().getActualBranchId(), goal);
}
}
/**
* Iterate over all execution results and summarize statistics
*
* @param results
* @param predicateCount
* @param callCount
* @param trueDistance
* @param falseDistance
* @return
*/
private boolean analyzeTraces( AbstractTestSuiteChromosome extends ExecutableChromosome> suite, List results,
Map predicateCount,
Map trueDistance, Map falseDistance) {
boolean hasTimeoutOrTestException = false;
for (ExecutionResult result : results) {
if (result.hasTimeout() || result.hasTestException()) {
hasTimeoutOrTestException = true;
continue;
}
TestChromosome test = new TestChromosome();
test.setTestCase(result.test);
test.setLastExecutionResult(result);
test.setChanged(false);
for (Entry entry : result.getTrace().getPredicateExecutionCount().entrySet()) {
if (!branchesId.contains(entry.getKey())
|| (removedBranchesT.contains(entry.getKey())
&& removedBranchesF.contains(entry.getKey())))
continue;
if (!predicateCount.containsKey(entry.getKey()))
predicateCount.put(entry.getKey(), entry.getValue());
else {
predicateCount.put(entry.getKey(),
predicateCount.get(entry.getKey())
+ entry.getValue());
}
}
for (Entry entry : result.getTrace().getTrueDistances().entrySet()) {
if(!branchesId.contains(entry.getKey())||removedBranchesT.contains(entry.getKey())) continue;
if (!trueDistance.containsKey(entry.getKey()))
trueDistance.put(entry.getKey(), entry.getValue());
else {
trueDistance.put(entry.getKey(),
Math.min(trueDistance.get(entry.getKey()),
entry.getValue()));
}
OnlyBranchCoverageTestFitness goal = (OnlyBranchCoverageTestFitness) branchCoverageTrueMap.get(entry.getKey());
if ((Double.compare(entry.getValue(), 0.0) ==0)) {
test.getTestCase().addCoveredGoal(goal);
toRemoveBranchesT.add(entry.getKey());
}
if(Properties.TEST_ARCHIVE) {
Archive.getArchiveInstance().updateArchive(goal, test, entry.getValue());
}
}
for (Entry entry : result.getTrace().getFalseDistances().entrySet()) {
if(!branchesId.contains(entry.getKey())||removedBranchesF.contains(entry.getKey())) continue;
if (!falseDistance.containsKey(entry.getKey()))
falseDistance.put(entry.getKey(), entry.getValue());
else {
falseDistance.put(entry.getKey(),
Math.min(falseDistance.get(entry.getKey()),
entry.getValue()));
}
OnlyBranchCoverageTestFitness goal = (OnlyBranchCoverageTestFitness) branchCoverageFalseMap.get(entry.getKey());
if ((Double.compare(entry.getValue(), 0.0) ==0)) {
test.getTestCase().addCoveredGoal(goal);
toRemoveBranchesF.add(entry.getKey());
}
if(Properties.TEST_ARCHIVE) {
Archive.getArchiveInstance().updateArchive(goal, test, entry.getValue());
}
}
}
return hasTimeoutOrTestException;
}
@Override
public boolean updateCoveredGoals() {
if (!Properties.TEST_ARCHIVE) {
return false;
}
for (Integer branch : toRemoveBranchesT) {
TestFitnessFunction f = branchCoverageTrueMap.remove(branch);
if (f != null) {
removedBranchesT.add(branch);
if (removedBranchesF.contains(branch)) {
totalBranches--;
//if(isFullyCovered(f.getTargetClass(), f.getTargetMethod())) {
// removeTestCall(f.getTargetClass(), f.getTargetMethod());
//}
}
} else {
throw new IllegalStateException("goal to remove not found");
}
}
for (Integer branch : toRemoveBranchesF) {
TestFitnessFunction f = branchCoverageFalseMap.remove(branch);
if (f != null) {
removedBranchesF.add(branch);
if (removedBranchesT.contains(branch)) {
totalBranches--;
//if(isFullyCovered(f.getTargetClass(), f.getTargetMethod())) {
// removeTestCall(f.getTargetClass(), f.getTargetMethod());
//}
}
} else {
throw new IllegalStateException("goal to remove not found");
}
}
toRemoveBranchesF.clear();
toRemoveBranchesT.clear();
logger.info("Current state of archive: " + Archive.getArchiveInstance().toString());
return true;
}
/**
* {@inheritDoc}
*
* Execute all tests and count covered branches
*/
@Override
public double getFitness(
AbstractTestSuiteChromosome extends ExecutableChromosome> suite) {
logger.trace("Calculating branch fitness");
double fitness = 0.0;
List results = runTestSuite(suite);
Map trueDistance = new LinkedHashMap();
Map falseDistance = new LinkedHashMap();
Map predicateCount = new LinkedHashMap();
// Collect stats in the traces
boolean hasTimeoutOrTestException = analyzeTraces(suite, results, predicateCount,
trueDistance,
falseDistance);
// Collect branch distances of covered branches
int numCoveredBranches = 0;
for (Integer key : predicateCount.keySet()) {
double df = 0.0;
double dt = 0.0;
int numExecuted = predicateCount.get(key);
if(removedBranchesT.contains(key))
numExecuted++;
if(removedBranchesF.contains(key))
numExecuted++;
if (trueDistance.containsKey(key)) {
dt = trueDistance.get(key);
}
if(falseDistance.containsKey(key)){
df = falseDistance.get(key);
}
// If the branch predicate was only executed once, then add 1
if (numExecuted == 1) {
fitness += 1.0;
} else {
fitness += normalize(df) + normalize(dt);
}
if (falseDistance.containsKey(key)&&(Double.compare(df, 0.0) == 0))
numCoveredBranches++;
if (trueDistance.containsKey(key)&&(Double.compare(dt, 0.0) == 0))
numCoveredBranches++;
}
// +1 for every branch that was not executed
fitness += 2 * (totalBranches - predicateCount.size());
printStatusMessages(suite, numCoveredBranches, fitness);
// Calculate coverage
int coverage = numCoveredBranches;
coverage +=removedBranchesF.size();
coverage +=removedBranchesT.size();
if (totalGoals > 0)
suite.setCoverage(this, (double) coverage / (double) totalGoals);
else
suite.setCoverage(this, 1);
suite.setNumOfCoveredGoals(this, coverage);
suite.setNumOfNotCoveredGoals(this, totalGoals-coverage);
if (hasTimeoutOrTestException) {
logger.info("Test suite has timed out, setting fitness to max value "
+ (totalBranches * 2));
fitness = totalBranches * 2;
//suite.setCoverage(0.0);
}
updateIndividual(this, suite, fitness);
assert (coverage <= totalGoals) : "Covered " + coverage + " vs total goals "
+ totalGoals;
assert (fitness >= 0.0);
assert (fitness != 0.0 || coverage == totalGoals) : "Fitness: " + fitness + ", "
+ "coverage: " + coverage + "/" + totalGoals;
assert (suite.getCoverage(this) <= 1.0) && (suite.getCoverage(this) >= 0.0) : "Wrong coverage value "
+ suite.getCoverage(this);
return fitness;
}
/**
* Some useful debug information
*
* @param coveredBranches
* @param coveredMethods
* @param fitness
*/
private void printStatusMessages(
AbstractTestSuiteChromosome extends ExecutableChromosome> suite,
int coveredBranches, double fitness) {
if (coveredBranches > maxCoveredBranches) {
maxCoveredBranches = coveredBranches;
logger.info("(Branches) Best individual covers " + coveredBranches + "/"
+ (totalBranches * 2) + " branches");
logger.info("Fitness: " + fitness + ", size: " + suite.size() + ", length: "
+ suite.totalLengthOfTestCases());
}
if (fitness < bestFitness) {
logger.info("(Fitness) Best individual covers " + coveredBranches + "/"
+ (totalBranches * 2) + " branches");
bestFitness = fitness;
logger.info("Fitness: " + fitness + ", size: " + suite.size() + ", length: "
+ suite.totalLengthOfTestCases());
}
}
}