org.evosuite.testcase.ConstraintVerifier 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.evosuite.runtime.annotation.*;
import org.evosuite.runtime.util.Inputs;
import org.evosuite.symbolic.expr.Constraint;
import org.evosuite.testcase.statements.*;
import org.evosuite.testcase.variable.NullReference;
import org.evosuite.testcase.variable.VariableReference;
import org.evosuite.utils.Randomness;
import org.evosuite.utils.generic.GenericAccessibleObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* Class used to verify that the constraints on the usage of the external
* resources (mock environment and JavaEE) are properly satisfied.
*
* The checks are mainly for debugging EvoSuite, as constraints should
* always hold by construction. Here we are just interested to see if any
* mutation does break the constraints
*
* Note: checks here are lightweight (ie not 100% precise), and only
* check what is in the test, not what should had been there
*
* Created by Andrea Arcuri on 06/06/15.
*/
public class ConstraintVerifier {
private static final Logger logger = LoggerFactory.getLogger(ConstraintVerifier.class);
/**
* During the search, assertions have not been generated yet.
* Here, do check if any method, that is only for assertions, was
* included in the test
*
* @param tc
* @return
*/
public static boolean hasAnyOnlyForAssertionMethod(TestCase tc){
for(int i=0; i dependentPositions(TestCase tc, int pos) throws IllegalArgumentException{
Inputs.checkNull(tc);
Set dep = new LinkedHashSet<>();
Statement st = tc.getStatement(pos);
if(! canStatementHaveConstraints(st)){
/*
the statement itself has no constraints.
however, others might have dependencies on it.
An example is methods with "noNullInput" using it
*/
VariableReference ret = st.getReturnValue();
for (int i = pos + 1; i < tc.size(); i++) {
Statement toCheck = tc.getStatement(i);
Constraints constraint = ConstraintHelper.getConstraints(toCheck);
if(constraint==null || !constraint.noNullInputs()){
continue;
}
if(! (toCheck instanceof EntityWithParametersStatement)){
continue;
}
EntityWithParametersStatement entity = (EntityWithParametersStatement) toCheck;
for(VariableReference input : entity.getParameterReferences()){
if(input.same(ret)){
//var is used as input in a method that accepts no null input, so cannot be deleted
dep.add(i);
}
}
}
return dep;
}
//first look at bounded variables
for(Annotation[] array : getParameterAnnotations(st)){
for(int i=0; i obj, TestCase tc, int pos)
throws IllegalArgumentException{
Inputs.checkNull(obj,tc);
/*
if the given 'obj' (a method/constructor) belongs to a class for which there is an instance
before "pos" which is bounded after "pos", then we cannot add it, as could break bounding
constraints if such instance is chosen as callee for "obj". Note: we could force to never
use such instance (ie use another one if exists, or create it), but that would complicate
a lot all the algorithms in the test factory :(
*/
List possibleCallees = tc.getObjects(obj.getOwnerType(), pos);
for(VariableReference ref : possibleCallees){
int boundPos = ConstraintHelper.getLastPositionOfBounded(ref, tc);
if(boundPos >= pos){
return false;
}
}
Constraints constraints = obj.getAccessibleObject().getAnnotation(Constraints.class);
if(constraints == null){
return true;
}
if(! canBeInsertedRegardlessOfPosition(obj, tc)){
return false;
}
int minPos = getMinPosForAfter(obj,tc,tc.size());
if(minPos < 0 || pos < minPos){
return false;
}
return true;
}
/**
*
* @param obj
* @param tc
* @param lastValid
* @return position where the object can be inserted, otherwise a negative value if no insertion is possible
* @throws IllegalArgumentException
*/
public static int getAValidPositionForInsertion(GenericAccessibleObject> obj, TestCase tc, int lastValid) throws IllegalArgumentException{
Inputs.checkNull(obj,tc);
Constraints constraints = obj.getAccessibleObject().getAnnotation(Constraints.class);
if(constraints == null){
if(lastValid <= 0){
return 0;
}
return Randomness.nextInt(0,lastValid);
}
if(! canBeInsertedRegardlessOfPosition(obj, tc)){
return -1;
}
//TODO
//bounded
int minPos = getMinPosForAfter(obj, tc, lastValid);
if(minPos < 0){
return -1;
} else if(minPos > 0) {
return minPos; //try to add immediately 'after' the constraining method
} else {
assert minPos==0;
if(lastValid<=0){
return 0;
}
return Randomness.nextInt(0,lastValid);
}
}
private static int getMinPosForAfter(GenericAccessibleObject> obj, TestCase tc, int lastValid){
Constraints constraints = obj.getAccessibleObject().getAnnotation(Constraints.class);
Class> declaringClass = obj.getDeclaringClass();
//after
int minPos = 0;
String after = constraints.after();
if(after!=null && !after.isEmpty()){
String[] pair = ConstraintHelper.getClassAndMethod(after,declaringClass);
int afterPos = ConstraintHelper.getLastPositionOfMethodCall(tc,pair[0],pair[1],lastValid);
if(afterPos < 0){
/*
The current method cannot be inserted, because it has to be 'after' X, but X is not in the test
*/
return -1;
}
minPos = afterPos+1;
}
return minPos;
}
private static boolean canBeInsertedRegardlessOfPosition(GenericAccessibleObject> obj, TestCase tc){
Constraints constraints = obj.getAccessibleObject().getAnnotation(Constraints.class);
if(constraints == null){
return true;
}
if(constraints.noDirectInsertion()){
return false;
}
Class> declaringClass = obj.getDeclaringClass();
String declaringClassName = declaringClass.getCanonicalName();
String name = obj.getName();
//check atMostOnce
if(constraints.atMostOnce()){
int counter = ConstraintHelper.countNumberOfMethodCalls(tc,declaringClass,name);
if(counter == 1){
//cannot insert it again
return false;
} else if(counter > 1){
throw new RuntimeException("Violated 'atMostOnce' constraint for "+obj.getName());
}
}
//excludeOthers
List othersExcluded = ConstraintHelper.getExcludedMethods(tc);
if(othersExcluded != null && othersExcluded.size() > 0){
for(String[] pair : othersExcluded){
if(pair[0].equals(declaringClassName) && pair[1].equals(name)){
//this method/constructor cannot be added
return false;
}
}
}
//dependOnProperties
String[] properties = constraints.dependOnProperties();
if(properties!=null && properties.length>0){
for(String property : properties){
if(! tc.getAccessedEnvironment().hasProperty(property)){
return false;
}
}
}
return true;
}
public static boolean verifyTest(TestChromosome tc){
return verifyTest(tc.getTestCase());
}
/**
*
* @param tc
* @return true if the test case does satisfy all the constraints
*/
public static boolean verifyTest(TestCase tc) throws IllegalArgumentException{
Inputs.checkNull(tc);
Set