/**
* Copyright (C) 2010-2017 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 <http://www.gnu.org/licenses/>.
*/
package org.evosuite.setup;
import org.evosuite.Properties;
import org.evosuite.TestGenerationContext;
import org.evosuite.runtime.mock.MockList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Modifier;
import java.util.*;
public class ConcreteClassAnalyzer {
private static Logger logger = LoggerFactory.getLogger(ConcreteClassAnalyzer.class);
private static ConcreteClassAnalyzer instance;
private ConcreteClassAnalyzer() {
}
public static ConcreteClassAnalyzer getInstance() {
if(instance == null)
instance = new ConcreteClassAnalyzer();
return instance;
}
private Map<Class<?>, Set<Class<?>>> cache = new LinkedHashMap<>();
public void clear() {
cache.clear();
}
public Set<Class<?>> getConcreteClasses(Class<?> clazz,
InheritanceTree inheritanceTree) {
if(cache.containsKey(clazz))
return cache.get(clazz);
Set<Class<?>> classes = getConcreteClassesImpl(clazz, inheritanceTree);
cache.put(clazz, classes);
return classes;
}
private Set<Class<?>> getConcreteClassesImpl(Class<?> clazz,
InheritanceTree inheritanceTree) {
// Some special cases
if (clazz.equals(java.util.Map.class))
return getConcreteClassesMap();
else if (clazz.equals(java.util.List.class))
return getConcreteClassesList();
else if (clazz.equals(Set.class))
return getConcreteClassesSet();
else if (clazz.equals(java.util.Collection.class))
return getConcreteClassesList();
else if (clazz.equals(java.util.Iterator.class))
// We don't want to explicitly create iterators
// This would only pull in java.util.Scanner, the only
// concrete subclass
return new LinkedHashSet<Class<?>>();
else if (clazz.equals(java.util.ListIterator.class))
// We don't want to explicitly create iterators
return new LinkedHashSet<Class<?>>();
else if (clazz.equals(java.io.Serializable.class))
return new LinkedHashSet<Class<?>>();
else if (clazz.equals(Comparable.class))
return getConcreteClassesComparable();
else if (clazz.equals(java.util.Comparator.class))
return new LinkedHashSet<Class<?>>();
Set<Class<?>> actualClasses = new LinkedHashSet<Class<?>>();
if (Modifier.isAbstract(clazz.getModifiers())
|| Modifier.isInterface(clazz.getModifiers()) || clazz.equals(Enum.class)) {
// We have to use getName here and not getCanonicalName
// because getCanonicalname uses . rather than $ for inner classes
// but the InheritanceTree uses $
String className = clazz.getName();
if(MockList.isAMockClass(className))
className = clazz.getSuperclass().getName();
Set<String> subClasses = inheritanceTree.getSubclasses(className);
logger.debug("Subclasses of " + clazz.getName() + ": " + subClasses);
Map<String, Integer> classDistance = new HashMap<String, Integer>();
int maxDistance = -1;
String name = clazz.getName();
if (clazz.equals(Enum.class)) {
name = Properties.TARGET_CLASS;
}
for (String subClass : subClasses) {
int distance = TestClusterUtils.getPackageDistance(subClass, name);
classDistance.put(subClass, distance);
maxDistance = Math.max(distance, maxDistance);
}
int distance = 0;
while (actualClasses.isEmpty() && distance <= maxDistance) {
logger.debug(" Current distance: " + distance);
for (String subClass : subClasses) {
if (TestClusterUtils.isAnonymousClass(subClass)) {
continue;
}
if (classDistance.get(subClass) == distance) {
try {
TestGenerationContext.getInstance().goingToExecuteSUTCode();
Class<?> subClazz = Class.forName(subClass,
false,
TestGenerationContext.getInstance().getClassLoaderForSUT());
if (!TestUsageChecker.canUse(subClazz))
continue;
if (subClazz.isInterface())
continue;
if (Modifier.isAbstract(subClazz.getModifiers())) {
if(!TestClusterUtils.hasStaticGenerator(subClazz))
continue;
}
Class<?> mock = MockList.getMockClass(subClazz.getCanonicalName());
if (mock != null) {
/*
* If we are mocking this class, then such class should not be used
* in the generated JUnit test cases, but rather its mock.
*/
logger.debug("Adding mock " + mock + " instead of "
+ clazz);
subClazz = mock;
} else {
if (!TestClusterUtils.checkIfCanUse(subClazz.getCanonicalName())) {
continue;
}
}
actualClasses.add(subClazz);
} catch (ClassNotFoundException | IncompatibleClassChangeError | NoClassDefFoundError e) {
logger.error("Problem for " + Properties.TARGET_CLASS
+ ". Class not found: " + subClass, e);
logger.error("Removing class from inheritance tree");
inheritanceTree.removeClass(subClass);
} finally {
TestGenerationContext.getInstance().doneWithExecutingSUTCode();
}
}
}
distance++;
}
if(TestClusterUtils.hasStaticGenerator(clazz)) {
actualClasses.add(clazz);
}
if (actualClasses.isEmpty()) {
logger.info("Don't know how to instantiate abstract class {}", clazz.getName());
}
} else {
actualClasses.add(clazz);
}
logger.debug("Subclasses of " + clazz.getName() + ": " + actualClasses);
return actualClasses;
}
private Set<Class<?>> getConcreteClassesMap() {
Set<Class<?>> mapClasses = new LinkedHashSet<>();
Class<?> mapClazz;
try {
mapClazz = Class.forName("java.util.HashMap",
false,
TestGenerationContext.getInstance().getClassLoaderForSUT());
mapClasses.add(mapClazz);
} catch (ClassNotFoundException e) {
logger.error(e.getMessage());
}
return mapClasses;
}
private Set<Class<?>> getConcreteClassesList() {
Set<Class<?>> mapClasses = new LinkedHashSet<Class<?>>();
Class<?> mapClazz;
try {
mapClazz = Class.forName("java.util.LinkedList",
false,
TestGenerationContext.getInstance().getClassLoaderForSUT());
mapClasses.add(mapClazz);
} catch (ClassNotFoundException e) {
logger.error(e.getMessage());
}
return mapClasses;
}
private Set<Class<?>> getConcreteClassesSet() {
Set<Class<?>> mapClasses = new LinkedHashSet<Class<?>>();
Class<?> setClazz;
try {
setClazz = Class.forName("java.util.LinkedHashSet",
false,
TestGenerationContext.getInstance().getClassLoaderForSUT());
mapClasses.add(setClazz);
} catch (ClassNotFoundException e) {
logger.error(e.getMessage());
}
return mapClasses;
}
private Set<Class<?>> getConcreteClassesComparable() {
Set<Class<?>> comparableClasses = new LinkedHashSet<Class<?>>();
Class<?> comparableClazz;
try {
comparableClazz = Class.forName("java.lang.Integer",
false,
TestGenerationContext.getInstance().getClassLoaderForSUT());
comparableClasses.add(comparableClazz);
} catch (ClassNotFoundException e) {
logger.error(e.getMessage());
}
return comparableClasses;
}
}