org.evosuite.testcase.ValueMinimizer 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.testcase;
import org.apache.commons.lang3.math.NumberUtils;
import org.evosuite.Properties;
import org.evosuite.lm.StringLMOptimizer;
import org.evosuite.testcase.execution.ExecutionResult;
import org.evosuite.testcase.statements.*;
import org.evosuite.testcase.statements.numeric.BooleanPrimitiveStatement;
import org.evosuite.testcase.statements.numeric.NumericalPrimitiveStatement;
import org.evosuite.testcase.variable.ConstantValue;
import org.evosuite.testcase.variable.VariableReference;
import org.evosuite.testsuite.TestSuiteChromosome;
import org.evosuite.testsuite.TestSuiteFitnessFunction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* ValueMinimizer class.
*
*
* @author Gordon Fraser
*/
public class ValueMinimizer extends TestVisitor {
private static Logger logger = LoggerFactory.getLogger(ValueMinimizer.class);
public static interface Minimization {
public boolean isNotWorse();
}
private static class TestMinimization implements Minimization {
private final TestFitnessFunction fitness;
private final TestChromosome individual;
private double lastFitness;
public TestMinimization(TestFitnessFunction fitness, TestChromosome test) {
this.fitness = fitness;
this.individual = test;
this.lastFitness = test.getFitness(fitness);
}
/* (non-Javadoc)
* @see org.evosuite.ga.LocalSearchObjective#hasImproved(org.evosuite.ga.Chromosome)
*/
@Override
public boolean isNotWorse() {
ExecutionResult lastResult = individual.getLastExecutionResult();
individual.setChanged(true);
individual.getTestCase().clearCoveredGoals();
double newFitness = fitness.getFitness(individual);
boolean worse = false;
if(fitness.isMaximizationFunction()) {
if (newFitness < lastFitness)
worse = true;
} else {
if (newFitness > lastFitness)
worse = true;
}
if (!worse) {
lastFitness = newFitness;
individual.setFitness(fitness, lastFitness);
return true;
} else {
individual.setFitness(fitness, lastFitness);
individual.setLastExecutionResult(lastResult);
return false;
}
}
}
private static class SuiteMinimization implements Minimization {
private final TestSuiteFitnessFunction fitness;
private final TestSuiteChromosome suite;
private final TestChromosome individual;
private final int testIndex;
private double lastFitness;
private double lastCoverage;
public SuiteMinimization(TestSuiteFitnessFunction fitness,
TestSuiteChromosome suite, int index) {
this.fitness = fitness;
this.suite = suite;
this.individual = suite.getTestChromosome(index);
this.testIndex = index;
this.lastFitness = suite.getFitness(fitness);
this.lastCoverage = suite.getCoverage();
}
/* (non-Javadoc)
* @see org.evosuite.ga.LocalSearchObjective#hasImproved(org.evosuite.ga.Chromosome)
*/
@Override
public boolean isNotWorse() {
ExecutionResult lastResult = individual.getLastExecutionResult().clone();
individual.setChanged(true);
suite.setTestChromosome(testIndex, individual);
double newFitness = fitness.getFitness(suite);
// individual.setChanged(true);
boolean worse = false;
if(fitness.isMaximizationFunction()) {
if (newFitness < lastFitness)
worse = true;
} else {
if (newFitness > lastFitness)
worse = true;
}
if (!worse) {
logger.debug("Fitness changed from " + lastFitness + " to " + newFitness);
lastFitness = newFitness;
lastCoverage = suite.getCoverage();
suite.setFitness(fitness, lastFitness);
return true;
} else {
individual.setLastExecutionResult(lastResult);
suite.setFitness(fitness, lastFitness);
suite.setCoverage(fitness, lastCoverage);
return false;
}
}
}
private Minimization objective;
/**
*
* minimize
*
*
* @param test a {@link org.evosuite.testcase.TestChromosome} object.
* @param objective a {@link org.evosuite.testcase.TestFitnessFunction} object.
*/
public void minimize(TestChromosome test, TestFitnessFunction objective) {
this.objective = new TestMinimization(objective, test);
test.test.accept(this);
}
/**
*
* minimize
*
*
* @param suite a {@link org.evosuite.testsuite.TestSuiteChromosome} object.
* @param objective a {@link org.evosuite.testsuite.TestSuiteFitnessFunction}
* object.
*/
public void minimize(TestSuiteChromosome suite, TestSuiteFitnessFunction objective) {
int i = 0;
objective.getFitness(suite); // Ensure all tests have an execution result cached
for (TestChromosome test : suite.getTestChromosomes()) {
this.objective = new SuiteMinimization(objective, suite, i);
test.test.accept(this);
i++;
}
}
/* Generics, blargh */
@SuppressWarnings("unchecked")
private N increment(N n, int x) {
if (n instanceof Double) {
return (N) (Double) (((Double)n) + x);
} else if (n instanceof Float) {
return (N) (Float) (((Float)n) + x);
} else if (n instanceof Integer) {
return (N) (Integer) (((Integer)n) + x);
} else if (n instanceof Long) {
return (N) (Long) (((Long)n) + x);
} else if (n instanceof Short) {
return (N) (Short) (short)(((Short)n) + (short)x);
} else if(n instanceof Byte) {
return (N) (Byte) (byte)(((Byte)n) + (byte)x);
} else if (n == null) {
throw new NullPointerException();
} else {
throw new IllegalArgumentException("Unexpected number type: " + n.getClass());
}
}
/* Generics, blargh
* TODO: Why are we not going min + max / 2 ?
*/
@SuppressWarnings("unchecked")
private N getMid(N min, N max) {
if (min instanceof Double) {
return (N) (Double) (((Double)min) + (((Double)max - (Double)min)/2.0));
} else if (min instanceof Float) {
return (N) (Float) (((Float)min) + (((Float)max - (Float)min)/2F));
} else if (min instanceof Integer) {
return (N) (Integer) (((Integer)min) + (((Integer)max - (Integer)min)/2));
} else if (min instanceof Long) {
return (N) (Long) (((Long)min) + (((Long)max - (Long)min)/2L));
} else if (min instanceof Short) {
return (N) (Short) (short)(((Short)min) + (((Short)max - (Short)min)/(short)2));
} else if(min instanceof Byte) {
return (N) (Byte) (byte)(((Byte)min) + (((Byte)max - (Byte)min)/(byte)2));
} else if (min == null) {
throw new NullPointerException();
} else {
throw new IllegalArgumentException("Unexpected number type: " + min.getClass());
}
}
/* Generics, blargh
*/
@SuppressWarnings("unchecked")
private N getZero(N n) {
if (n instanceof Double) {
return (N) (Double.valueOf(0.0));
} else if (n instanceof Float) {
return (N) (Float.valueOf(0F));
} else if (n instanceof Integer) {
return (N) (Integer.valueOf(0));
} else if (n instanceof Long) {
return (N) Long.valueOf(0L);
} else if (n instanceof Short) {
return (N) Short.valueOf((short)0);
} else if(n instanceof Byte) {
return (N) Byte.valueOf((byte)0);
} else if (n == null) {
throw new NullPointerException();
} else {
throw new IllegalArgumentException("Unexpected number type: " + n.getClass());
}
}
private void binarySearch(ConstantValue constantValue, T number) {
T min = getZero(number);
T max = number;
boolean positive = number.doubleValue() >= 0.0;
T lastValue = null;
boolean done = false;
while (!done) {
Object oldValue = constantValue.getValue();
constantValue.setValue(getMid(min, max));
T newValue = (T)constantValue.getValue();
if (oldValue.equals(newValue)) {
break;
}
if (lastValue != null && lastValue.equals(newValue)) {
break;
}
if (lastValue instanceof Double) {
double oldVal = Math.abs((Double) lastValue);
if (oldVal < 1.0) {
newValue = (T) new Double(0.0);
constantValue.setValue(newValue);
if (!objective.isNotWorse()) {
constantValue.setValue(lastValue);
}
break;
}
}
if (lastValue instanceof Float) {
double oldVal = Math.abs((Float) lastValue);
if (oldVal < 1.0F) {
newValue = (T) new Float(0.0F);
constantValue.setValue(newValue);
if (!objective.isNotWorse()) {
constantValue.setValue(lastValue);
}
break;
}
}
lastValue = newValue;
logger.info("Trying " + constantValue.getValue() + " " + min + "/" + max + " - "
+ constantValue.getSimpleClassName());
if (min.equals(max) || constantValue.getValue().equals(min)
|| constantValue.getValue().equals(max)) {
done = true;
logger.info("Fixpoint.");
//assert (objective.isNotWorse());
}
if (objective.isNotWorse()) {
logger.info("Fitness hasn't decreased");
// If fitness has not decreased, new max is new value
max = (T)constantValue.getValue();
} else {
logger.info("Fitness has decreased!");
// Else has to be larger
if (positive) {
min = increment((T)constantValue.getValue(), 1);
}
else {
min = increment((T)constantValue.getValue(), -1);
}
constantValue.setValue(max);
System.out.println("Setting value back to "+max);
constantValue.getTestCase().clearCoveredGoals();
}
}
constantValue.getTestCase().clearCoveredGoals();
}
/**
* Shorten the string as much as possible, until the objective value is affected.
*
* @param constantValue StringPrimitiveStatement containing a string to be minimised.
*/
private void removeCharacters(ConstantValue constantValue) {
assert(constantValue.getValue() instanceof String);
String oldString = (String)constantValue.getValue();
for (int i = oldString.length() - 1; i >= 0; i--) {
String newString = oldString.substring(0, i) + oldString.substring(i + 1);
constantValue.setValue(newString);
//logger.info(" " + i + " " + oldValue + "/" + oldValue.length() + " -> "
// + newString + "/" + newString.length());
if (objective.isNotWorse()) {
oldString = (String)constantValue.getValue();
} else {
constantValue.setValue(oldString);
}
}
}
/**
* Try to remove non-ASCII characters
*
* Try to shorten the string.
* Performs several transformations on the string:
* 1. Strip ASCII and control characters
* 2. Strip any non-alphanumerics
*
* If any transformation negatively impacts the objective function value, then
* the transformation is reversed and the next one tried.
*/
private void cleanString(ConstantValue constantValue) {
assert(constantValue.getValue() instanceof String);
String oldString = (String)constantValue.getValue();
String newString = oldString.replaceAll("[^\\p{ASCII}]", "").replaceAll("\\p{Cntrl}",
"");
constantValue.setValue(newString);
if (!objective.isNotWorse()) {
constantValue.setValue(oldString);
newString = oldString;
}
oldString = newString;
newString = newString.replaceAll("[^\\p{L}\\p{N}]", "");
constantValue.setValue(newString);
if (!objective.isNotWorse()) {
constantValue.setValue(oldString);
}
}
/**
* Attempt to use the language model to improve the string constant.
* If a better string is found that doesn't negatively impact the fitness value,
* statement will be overwritten to use the new improved value.
*
* @param constantValue
*/
private void replaceWithLanguageModel(ConstantValue constantValue) {
assert(constantValue.getValue() instanceof String);
String oldString = (String)constantValue.getValue();
StringLMOptimizer slmo = new StringLMOptimizer(constantValue, objective);
String newString = slmo.optimize();
constantValue.setValue(newString);
if (!objective.isNotWorse()) {
constantValue.setValue(oldString);
}
}
/* (non-Javadoc)
* @see org.evosuite.testcase.TestVisitor#visitTestCase(org.evosuite.testcase.TestCase)
*/
/** {@inheritDoc} */
@Override
public void visitTestCase(TestCase test) {
}
@Override
public void visitStatement(Statement statement) {
for(VariableReference var : statement.getVariableReferences()) {
if(var instanceof ConstantValue) {
ConstantValue constantValue = (ConstantValue)var;
Object value = constantValue.getValue();
if(value instanceof String) {
logger.info("Statement before minimization: " + statement.getCode());
cleanString(constantValue);
removeCharacters(constantValue);
if(Properties.LM_STRINGS) {
replaceWithLanguageModel(constantValue);
}
logger.info("Statement after minimization: " + statement.getCode());
// TODO: Try to delete characters, or at least replace non-ascii characters with ascii characters
} else if(value instanceof Number) {
logger.info("Statement before minimization: " + statement.getCode());
binarySearch(constantValue, (Number)constantValue.getValue());
logger.info("Statement after minimization: " + statement.getCode());
}
}
}
}
/* (non-Javadoc)
* @see org.evosuite.testcase.TestVisitor#visitPrimitiveStatement(org.evosuite.testcase.PrimitiveStatement)
*/
/** {@inheritDoc} */
@Override
public void visitPrimitiveStatement(PrimitiveStatement> statement) {
}
/* (non-Javadoc)
* @see org.evosuite.testcase.TestVisitor#visitFieldStatement(org.evosuite.testcase.FieldStatement)
*/
/** {@inheritDoc} */
@Override
public void visitFieldStatement(FieldStatement statement) {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see org.evosuite.testcase.TestVisitor#visitMethodStatement(org.evosuite.testcase.MethodStatement)
*/
/** {@inheritDoc} */
@Override
public void visitMethodStatement(MethodStatement statement) {
}
/* (non-Javadoc)
* @see org.evosuite.testcase.TestVisitor#visitConstructorStatement(org.evosuite.testcase.ConstructorStatement)
*/
/** {@inheritDoc} */
@Override
public void visitConstructorStatement(ConstructorStatement statement) {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see org.evosuite.testcase.TestVisitor#visitArrayStatement(org.evosuite.testcase.ArrayStatement)
*/
/** {@inheritDoc} */
@Override
public void visitArrayStatement(ArrayStatement statement) {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see org.evosuite.testcase.TestVisitor#visitAssignmentStatement(org.evosuite.testcase.AssignmentStatement)
*/
/** {@inheritDoc} */
@Override
public void visitAssignmentStatement(AssignmentStatement statement) {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see org.evosuite.testcase.TestVisitor#visitNullStatement(org.evosuite.testcase.NullStatement)
*/
/** {@inheritDoc} */
@Override
public void visitNullStatement(NullStatement statement) {
// TODO Auto-generated method stub
}
/** {@inheritDoc} */
@Override
public void visitPrimitiveExpression(PrimitiveExpression primitiveExpression) {
// TODO-JRO Implement method visitPrimitiveExpression
logger.warn("Method visitPrimitiveExpression not implemented!");
}
@Override
public void visitFunctionalMockStatement(FunctionalMockStatement functionalMockStatement) {
}
}