org.evosuite.seeding.CastClassManager Maven / Gradle / Ivy
The newest version!
/**
* 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.seeding;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
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.ConstructionFailedException;
import org.evosuite.setup.*;
import org.evosuite.utils.generic.GenericClass;
import org.evosuite.utils.Randomness;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.googlecode.gentyref.GenericTypeReflector;
public class CastClassManager {
private static CastClassManager instance = new CastClassManager();
private static final Logger logger = LoggerFactory.getLogger(CastClassManager.class);
private final Map classMap = new LinkedHashMap();
public static List sortByValue(Map map) {
List> list = new LinkedList>(
map.entrySet());
Collections.sort(list, new Comparator>() {
@Override
public int compare(Map.Entry o1,
Map.Entry o2) {
if (o1.getKey().getNumParameters() == o2.getKey().getNumParameters())
return (o1.getValue()).compareTo(o2.getValue());
else
return o1.getKey().getNumParameters()
- o2.getKey().getNumParameters();
}
});
List result = new LinkedList();
for (Map.Entry entry : list) {
result.add(entry.getKey());
}
return result;
}
public static GenericClass selectClass(List candidates) {
double r = Randomness.nextDouble();
double d = Properties.RANK_BIAS
- Math.sqrt((Properties.RANK_BIAS * Properties.RANK_BIAS)
- (4.0 * (Properties.RANK_BIAS - 1.0) * r));
int length = candidates.size();
d = d / 2.0 / (Properties.RANK_BIAS - 1.0);
int index = (int) (length * d);
return candidates.get(index);
}
private CastClassManager() {
initDefaultClasses();
}
private void initDefaultClasses() {
classMap.put(new GenericClass(Object.class), 0);
classMap.put(new GenericClass(String.class), 1);
classMap.put(new GenericClass(Integer.class), 1);
}
public static CastClassManager getInstance() {
return instance;
}
public void addCastClass(String className, int depth) {
try {
Class> clazz = TestGenerationContext.getInstance().getClassLoaderForSUT().loadClass(className);
GenericClass castClazz = new GenericClass(clazz);
addCastClass(castClazz.getWithWildcardTypes(), depth);
} catch (ClassNotFoundException e) {
// Ignore
logger.debug("Error including cast class " + className + " because: " + e);
}
}
public void addCastClass(Type type, int depth) {
GenericClass castClazz = new GenericClass(type);
addCastClass(castClazz.getWithWildcardTypes(), depth);
}
public void addCastClass(GenericClass clazz, int depth) {
if (clazz.getRawClass() == null) {
logger.warn("ADDING NULL!");
assert (false);
}
if (clazz.isAbstract()) {
for (Class> concreteClass : ConcreteClassAnalyzer.getInstance().getConcreteClasses(clazz.getRawClass(),
TestCluster.getInheritanceTree())) {
GenericClass c = new GenericClass(concreteClass);
if(TestUsageChecker.canUse(c.getRawClass())) {
classMap.put(c, depth);
}
}
if(Properties.P_FUNCTIONAL_MOCKING > 0.0) {
if (TestUsageChecker.canUse(clazz.getRawClass()))
classMap.put(clazz, depth);
}
} else {
if(TestUsageChecker.canUse(clazz.getRawClass()))
classMap.put(clazz, depth);
}
}
private void handleComparable() {
// TODO
}
private void handleComparator() {
// TODO
}
private void handleEnum() {
// TODO
}
private void handleIterable() {
// TODO
}
private final List> specialCases = Arrays.asList(new Class>[] {
Comparable.class, Comparator.class, Iterable.class, Enum.class });
/**
* True if this type variable is one of the java.* special cases
*
* @return
*/
private boolean isSpecialCase(TypeVariable> typeVariable) {
for (Type bound : typeVariable.getBounds()) {
Class> clazz = GenericTypeReflector.erase(bound);
if (specialCases.contains(clazz))
return true;
}
return false;
}
/**
* True if this wildcard type is one of the java.* special cases
*
* @return
*/
private boolean isSpecialCase(WildcardType wildcardType) {
// TODO
return false;
}
private boolean addAssignableClass(WildcardType wildcardType,
Map, Type> typeMap) {
Set> classes = TestCluster.getInstance().getAnalyzedClasses();
Set> assignableClasses = new LinkedHashSet>();
for (Class> clazz : classes) {
if (!TestUsageChecker.canUse(clazz))
continue;
GenericClass genericClass = new GenericClass(clazz).getWithWildcardTypes();
if (!genericClass.satisfiesBoundaries(wildcardType, typeMap)) {
logger.debug("Not assignable: " + clazz);
} else {
logger.debug("Assignable");
assignableClasses.add(clazz);
}
}
for (Type t : typeMap.values()) {
if (t instanceof WildcardType)
continue; // TODO: For now.
Class> clazz = GenericTypeReflector.erase(t);
if (!TestUsageChecker.canUse(GenericTypeReflector.erase(clazz)))
continue;
GenericClass genericClass = new GenericClass(clazz).getWithWildcardTypes();
if (!genericClass.satisfiesBoundaries(wildcardType, typeMap)) {
logger.debug("Not assignable: " + clazz);
} else {
logger.debug("Assignable");
assignableClasses.add(clazz);
}
}
for (Type t : wildcardType.getUpperBounds()) {
if (typeMap.containsKey(t))
t = typeMap.get(t);
Class> clazz = GenericTypeReflector.erase(t);
logger.debug("Checking bound: " + t);
if (!TestUsageChecker.canUse(clazz))
continue;
GenericClass genericClass = new GenericClass(t);
if (genericClass.hasTypeVariables()) {
logger.debug("Has type variables: " + genericClass);
GenericClass wildcardClass = genericClass.getWithWildcardTypes();
if (!wildcardClass.satisfiesBoundaries(wildcardType, typeMap)) {
logger.debug("Not assignable: " + clazz);
} else {
logger.debug("Assignable");
assignableClasses.add(clazz);
}
} else {
logger.debug("Adding directly: " + genericClass);
assignableClasses.add(genericClass.getRawClass());
classMap.put(genericClass, 10);
}
}
logger.debug("Found assignable classes for wildcardtype " + wildcardType + ": "
+ assignableClasses.size());
if (!assignableClasses.isEmpty()) {
Class> clazz = Randomness.choice(assignableClasses);
GenericClass castClass = new GenericClass(clazz);
logger.debug("Adding cast class " + castClass);
classMap.put(castClass, 10);
return true;
}
return false;
}
private boolean addAssignableClass(TypeVariable> typeVariable,
Map, Type> typeMap) {
Set> classes = TestCluster.getInstance().getAnalyzedClasses();
Set> assignableClasses = new LinkedHashSet>();
for (Class> clazz : classes) {
if (!TestUsageChecker.canUse(clazz))
continue;
GenericClass genericClass = new GenericClass(clazz).getWithWildcardTypes();
if (!genericClass.satisfiesBoundaries(typeVariable, typeMap)) {
logger.debug("Not assignable: " + clazz);
} else {
logger.debug("Assignable");
assignableClasses.add(clazz);
}
}
for (Type t : typeMap.values()) {
if (t instanceof WildcardType)
continue; // TODO: For now.
Class> clazz = GenericTypeReflector.erase(t);
if (!TestUsageChecker.canUse(GenericTypeReflector.erase(clazz)))
continue;
GenericClass genericClass = new GenericClass(clazz).getWithWildcardTypes();
if (!genericClass.satisfiesBoundaries(typeVariable, typeMap)) {
logger.debug("Not assignable: " + clazz);
} else {
logger.debug("Assignable");
assignableClasses.add(clazz);
}
}
/*
for (Type t : typeVariable.getBounds()) {
if (typeMap.containsKey(t))
t = typeMap.get(t);
Class> clazz = GenericTypeReflector.erase(t);
logger.debug("Checking bound: " + t);
if (!TestClusterGenerator.canUse(clazz))
continue;
GenericClass genericClass = new GenericClass(t);
//if (genericClass.hasTypeVariables()) {
logger.debug("Has type variables: " + genericClass
+ ", checking wildcard version with type map " + typeMap);
GenericClass wildcardClass = genericClass.getWithWildcardTypes();
//if (!wildcardClass.satisfiesBoundaries(typeVariable, typeMap)) {
// logger.debug("Not assignable: " + clazz);
//} else {
// logger.debug("Assignable");
assignableClasses.add(clazz);
//}
//} else {
// logger.debug("Adding directly: " + genericClass);
// assignableClasses.add(genericClass.getRawClass());
// classMap.put(genericClass, 10);
//}
}
*/
logger.debug("Found assignable classes for type variable " + typeVariable + ": "
+ assignableClasses.size());
if (!assignableClasses.isEmpty()) {
Class> clazz = Randomness.choice(assignableClasses);
GenericClass castClass = new GenericClass(clazz);
logger.debug("Adding cast class " + castClass);
classMap.put(castClass, 10);
return true;
} else {
InheritanceTree inheritanceTree = DependencyAnalysis.getInheritanceTree();
Set> boundCandidates = new LinkedHashSet>();
for (Type bound : typeVariable.getBounds()) {
Class> rawBound = GenericTypeReflector.erase(bound);
boundCandidates.add(rawBound);
logger.debug("Getting concrete classes for " + rawBound);
boundCandidates.addAll(ConcreteClassAnalyzer.getInstance().getConcreteClasses(rawBound,
inheritanceTree));
}
for (Class> clazz : boundCandidates) {
if (!TestUsageChecker.canUse(clazz))
continue;
boolean isAssignable = true;
for (Type bound : typeVariable.getBounds()) {
if (GenericTypeReflector.erase(bound).equals(Enum.class)) {
if (clazz.isEnum())
continue;
}
if (!GenericClass.isAssignable(bound, clazz)) {
isAssignable = false;
logger.debug("Not assignable: " + clazz + " to bound " + bound);
break;
}
if (bound instanceof ParameterizedType) {
if (Arrays.asList(((ParameterizedType) bound).getActualTypeArguments()).contains(typeVariable)) {
isAssignable = false;
break;
}
}
}
if (isAssignable) {
assignableClasses.add(clazz);
}
}
logger.debug("After adding bounds, found "+assignableClasses.size()+" assignable classes for type variable "
+ typeVariable + ": " + assignableClasses);
if (!assignableClasses.isEmpty()) {
// TODO: Add all classes?
// for(Class> clazz : assignableClasses) {
// GenericClass castClass = new GenericClass(clazz);
// logger.debug("Adding cast class " + castClass);
// classMap.put(castClass, 10);
// }
Class> clazz = Randomness.choice(assignableClasses);
GenericClass castClass = new GenericClass(clazz);
logger.debug("Adding cast class " + castClass);
classMap.put(castClass, 10);
return true;
}
}
return false;
}
private List getAssignableClasses(WildcardType wildcardType,
boolean allowRecursion, Map, Type> ownerVariableMap) {
Map assignableClasses = new LinkedHashMap();
logger.debug("Getting assignable classes for wildcard type " + wildcardType);
for (Entry entry : classMap.entrySet()) {
GenericClass key = entry.getKey();
logger.debug("Current class for wildcard " + wildcardType + ": " + key);
if (!key.satisfiesBoundaries(wildcardType, ownerVariableMap)) {
logger.debug("Does not satisfy boundaries");
continue;
}
if (!allowRecursion && key.hasWildcardOrTypeVariables()) {
logger.debug("Stopping because of type recursion");
continue;
}
logger.debug("Is assignable");
assignableClasses.put(entry.getKey(), entry.getValue());
}
return sortByValue(assignableClasses);
}
private List getAssignableClasses(TypeVariable> typeVariable,
boolean allowRecursion, Map, Type> ownerVariableMap) {
Map assignableClasses = new LinkedHashMap();
logger.debug("Getting assignable classes for type variable " + typeVariable);
for (Entry entry : classMap.entrySet()) {
GenericClass key = entry.getKey();
logger.debug("Current class for type variable " + typeVariable + ": " + key);
if (!key.satisfiesBoundaries(typeVariable, ownerVariableMap)) {
logger.debug("Bounds not satisfied");
continue;
}
if (!allowRecursion && key.hasWildcardOrTypeVariables()) {
logger.debug("Recursion not allowed but type has wildcard or type variables");
continue;
}
assignableClasses.put(entry.getKey(), entry.getValue());
}
logger.debug("Found assignable classes: " + assignableClasses.size());
return sortByValue(assignableClasses);
}
public GenericClass selectCastClass() {
List assignableClasses = sortByValue(classMap);
return selectClass(assignableClasses);
}
public GenericClass selectCastClass(TypeVariable> typeVariable,
boolean allowRecursion, Map, Type> ownerVariableMap) {
List assignableClasses = getAssignableClasses(typeVariable,
allowRecursion,
ownerVariableMap);
logger.debug("Assignable classes to " + typeVariable + ": " + assignableClasses);
// FIXME: If we disallow recursion immediately, then we will never actually
// do recursion since we always have Object, Integer, and String as candidates.
// Including recursion may influence performance negatively.
//
// If we were not able to find an assignable class without recursive types
// we try again but allowing recursion
// if(assignableClasses.isEmpty()) {
// assignableClasses = getAssignableClasses(typeVariable,
// allowRecursion,
// ownerVariableMap);
// }
//special case
if (assignableClasses.isEmpty()) {
logger.debug("Trying to add new cast class");
if (addAssignableClass(typeVariable, ownerVariableMap)) {
logger.debug("Now trying again to get a class");
assignableClasses = getAssignableClasses(typeVariable, allowRecursion,
ownerVariableMap);
if (assignableClasses.isEmpty()) {
logger.debug("Nothing is assignable");
return null;
}
} else {
logger.debug("Nothing is assignable");
return null;
}
}
logger.debug("Now we've got assignable classes: " + assignableClasses.size());
return selectClass(assignableClasses);
}
public GenericClass selectCastClass(WildcardType wildcardType,
boolean allowRecursion, Map, Type> ownerVariableMap)
throws ConstructionFailedException {
logger.debug("Getting assignable classes for wildcard");
List assignableClasses = getAssignableClasses(wildcardType,
false,
ownerVariableMap);
logger.debug("Assignable classes to " + wildcardType + ": " + assignableClasses);
// If we were not able to find an assignable class without recursive types
// we try again but allowing recursion
if (assignableClasses.isEmpty()) {
if(allowRecursion) {
assignableClasses.addAll(getAssignableClasses(wildcardType,
allowRecursion,
ownerVariableMap));
}
}
if (assignableClasses.isEmpty()) {
logger.debug("Trying to add new cast class");
if (addAssignableClass(wildcardType, ownerVariableMap)) {
assignableClasses = getAssignableClasses(wildcardType, allowRecursion,
ownerVariableMap);
if (assignableClasses.isEmpty()) {
logger.debug("Nothing is assignable");
throw new ConstructionFailedException("Nothing is assignable to "
+ wildcardType);
}
} else {
logger.debug("Making random choice because nothing is assignable");
throw new ConstructionFailedException("Nothing is assignable to "
+ wildcardType);
}
}
return selectClass(assignableClasses);
}
public boolean hasClass(String className) {
for (GenericClass clazz : classMap.keySet()) {
if (clazz.getClassName().equals(className))
return true;
}
return false;
}
public Set getCastClasses() {
return classMap.keySet();
}
public void clear() {
classMap.clear();
initDefaultClasses();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy