Java tutorial
/** * Copyright (C) 2011,2012 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 Public License as published by the Free Software Foundation, * either version 3 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 Public License for more details. * * You should have received a copy of the GNU Public License along with * EvoSuite. If not, see <http://www.gnu.org/licenses/>. */ /** * */ package org.evosuite.setup; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; import java.util.Random; import java.util.Set; import java.util.TreeMap; import java.util.regex.Pattern; import org.apache.commons.lang3.ClassUtils; import org.evosuite.Properties; import org.evosuite.Properties.Criterion; import org.evosuite.TestGenerationContext; import org.evosuite.TimeController; import org.evosuite.annotation.EvoSuiteExclude; import org.evosuite.classpath.ResourceList; import org.evosuite.instrumentation.BooleanTestabilityTransformation; import org.evosuite.reset.ClassResetter; import org.evosuite.rmi.ClientServices; import org.evosuite.runtime.MockList; import org.evosuite.seeding.CastClassAnalyzer; import org.evosuite.seeding.CastClassManager; import org.evosuite.seeding.ConstantPoolManager; import org.evosuite.setup.PutStaticMethodCollector.MethodIdentifier; import org.evosuite.statistics.RuntimeVariable; import org.evosuite.utils.GenericAccessibleObject; import org.evosuite.utils.GenericClass; import org.evosuite.utils.GenericConstructor; import org.evosuite.utils.GenericField; import org.evosuite.utils.GenericMethod; import org.junit.Test; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.InnerClassNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Gordon Fraser * */ public class TestClusterGenerator { private static Logger logger = LoggerFactory.getLogger(TestClusterGenerator.class); private static final List<String> classExceptions = Collections.unmodifiableList( Arrays.asList(new String[] { "com.apple", "apple.", "sun.", "com.sun.", "com.oracle.", "sun.awt." })); /** * Check if we can use the given class * * @param className * a {@link java.lang.String} object. * @return a boolean. */ public static boolean checkIfCanUse(String className) { if (MockList.shouldBeMocked(className)) { return false; } for (String s : classExceptions) { if (className.startsWith(s)) { return false; } } return true; } private final Set<GenericAccessibleObject<?>> dependencyCache = new LinkedHashSet<GenericAccessibleObject<?>>(); private final Set<GenericClass> genericCastClasses = new LinkedHashSet<GenericClass>(); private final Set<Class<?>> concreteCastClasses = new LinkedHashSet<Class<?>>(); private final Set<Class<?>> containerClasses = new LinkedHashSet<Class<?>>(); public void generateCluster(String targetClass, InheritanceTree inheritanceTree, CallTree callTree) throws RuntimeException, ClassNotFoundException { this.inheritanceTree = inheritanceTree; TestCluster.setInheritanceTree(inheritanceTree); if (Properties.INSTRUMENT_CONTEXT || Properties.CRITERION == Criterion.DEFUSE) { for (String callTreeClass : DependencyAnalysis.getCallTree().getClasses()) { try { TestGenerationContext.getInstance().getClassLoaderForSUT().loadClass(callTreeClass); } catch (ClassNotFoundException e) { logger.info("Class not found: {}: {}", callTreeClass, e); } } } Set<Type> parameterClasses = new LinkedHashSet<Type>(); Set<Type> callTreeClasses = new LinkedHashSet<Type>(); // Other classes might have further dependencies which we might need to resolve parameterClasses.removeAll(callTreeClasses); /* * If we fail to load a class, we skip it, and avoid to try * to load it again (which would result in extra unnecessary logging) */ Set<String> blackList = new LinkedHashSet<String>(); initBlackListWithEvoSuitePrimitives(blackList); logger.info("Handling cast classes"); handleCastClasses(); logger.info("Initialising target class"); initializeTargetMethods(); logger.info("Resolving dependencies"); resolveDependencies(blackList); if (logger.isDebugEnabled()) { logger.debug(TestCluster.getInstance().toString()); } dependencyCache.clear(); gatherStatistics(); } private void handleCastClasses() { // If we include type seeding, then we analyze classes to find types in instanceof and cast instructions if (Properties.SEED_TYPES) { Set<String> blackList = new LinkedHashSet<String>(); initBlackListWithPrimitives(blackList); Set<String> classNames = new LinkedHashSet<String>(); //classNames.add("java.lang.Object"); //classNames.add("java.lang.String"); //classNames.add("java.lang.Integer"); CastClassAnalyzer analyzer = new CastClassAnalyzer(); Map<Type, Integer> castMap = analyzer.analyze(Properties.TARGET_CLASS); for (Entry<Type, Integer> castEntry : castMap.entrySet()) { String className = castEntry.getKey().getClassName(); if (blackList.contains(className)) continue; if (addCastClassDependencyIfAccessible(className, blackList)) { CastClassManager.getInstance().addCastClass(className, castEntry.getValue()); classNames.add(castEntry.getKey().getClassName()); } } // If SEED_TYPES is false, only Object is a cast class // logger.info("Handling cast classes"); // addCastClasses(classNames, blackList); logger.debug("Cast classes used: {}", classNames); } } private void gatherStatistics() { ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Analyzed_Classes, analyzedClasses.size()); ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Generators, TestCluster.getInstance().getGenerators().size()); ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Modifiers, TestCluster.getInstance().getModifiers().size()); } private void initBlackListWithEvoSuitePrimitives(Set<String> blackList) throws NullPointerException { blackList.add("int"); blackList.add("short"); blackList.add("float"); blackList.add("double"); blackList.add("byte"); blackList.add("char"); blackList.add("boolean"); blackList.add("long"); blackList.add("java.lang.Enum"); blackList.add("java.lang.String"); blackList.add("java.lang.Class"); } private void initBlackListWithPrimitives(Set<String> blackList) throws NullPointerException { blackList.add("int"); blackList.add("short"); blackList.add("float"); blackList.add("double"); blackList.add("byte"); blackList.add("char"); blackList.add("boolean"); blackList.add("long"); } private boolean addCastClassDependencyIfAccessible(String className, Set<String> blackList) { if (className.equals("java.lang.String")) return true; if (blackList.contains(className)) { logger.info("Cast class in blacklist: {}", className); return false; } try { Class<?> clazz = TestGenerationContext.getInstance().getClassLoaderForSUT().loadClass(className); if (!canUse(clazz)) { logger.debug("Cannot use cast class: {}", className); return false; } // boolean added = addDependency(new GenericClass(clazz), 1); genericCastClasses.add(new GenericClass(clazz)); concreteCastClasses.add(clazz); //if (!added) { blackList.add(className); return true; //} } catch (ClassNotFoundException e) { logger.error("Problem for {}. Class not found", Properties.TARGET_CLASS, e); blackList.add(className); return false; } } /** * Update * * @param clazz */ public void addCastClassForContainer(Class<?> clazz) { if (concreteCastClasses.contains(clazz)) return; concreteCastClasses.add(clazz); // TODO: What if this is generic again? genericCastClasses.add(new GenericClass(clazz)); CastClassManager.getInstance().addCastClass(clazz, 1); TestCluster.getInstance().clearGeneratorCache(new GenericClass(clazz)); } /** * Continue adding generators for classes that are needed */ private void resolveDependencies(Set<String> blackList) { while (!dependencies.isEmpty() && TimeController.getInstance().isThereStillTimeInThisPhase()) { logger.debug("Dependencies left: {}", dependencies.size()); Iterator<Pair> iterator = dependencies.iterator(); Pair dependency = iterator.next(); iterator.remove(); if (analyzedClasses.contains(dependency.getDependencyClass().getRawClass())) { continue; } String className = dependency.getDependencyClass().getClassName(); if (blackList.contains(className)) { continue; } boolean added = false; /* if (dependency.getDependencyClass().isParameterizedType()) { for (List<GenericClass> parameterTypes : getAssignableTypes(dependency.getDependencyClass())) { GenericClass copy = new GenericClass( dependency.getDependencyClass().getType()); copy.setParameterTypes(parameterTypes); boolean success = addDependencyClass(copy, dependency.getRecursion()); if (success) added = true; } } else */ added = addDependencyClass(dependency.getDependencyClass(), dependency.getRecursion()); if (!added) { blackList.add(className); } //} } } public List<List<GenericClass>> getAssignableTypes(GenericClass clazz) { List<List<GenericClass>> tuples = new ArrayList<List<GenericClass>>(); //logger.info("Parameters of " + clazz.getSimpleName() + ": " // + clazz.getNumParameters()); boolean first = true; for (java.lang.reflect.Type parameterType : clazz.getParameterTypes()) { //logger.info("Current parameter: " + parameterType); List<GenericClass> assignableClasses = getAssignableTypes(parameterType); List<List<GenericClass>> newTuples = new ArrayList<List<GenericClass>>(); for (GenericClass concreteClass : assignableClasses) { if (first) { List<GenericClass> tuple = new ArrayList<GenericClass>(); tuple.add(concreteClass); newTuples.add(tuple); } else { for (List<GenericClass> t : tuples) { List<GenericClass> tuple = new ArrayList<GenericClass>(t); tuple.add(concreteClass); newTuples.add(tuple); } } } tuples = newTuples; first = false; } return tuples; } private List<GenericClass> getAssignableTypes(java.lang.reflect.Type type) { List<GenericClass> types = new ArrayList<GenericClass>(); for (GenericClass clazz : genericCastClasses) { if (clazz.isAssignableTo(type)) { logger.debug("{} is assignable to {}", clazz, type); types.add(clazz); } } return types; } private void addDeclaredClasses(Set<Class<?>> targetClasses, Class<?> currentClass) { for (Class<?> c : currentClass.getDeclaredClasses()) { logger.info("Adding declared class {}", c); targetClasses.add(c); addDeclaredClasses(targetClasses, c); } } /** * All public methods defined directly in the SUT should be covered * * TODO: What if we use instrument_parent? * * @param targetClass */ @SuppressWarnings("unchecked") private void initializeTargetMethods() throws RuntimeException, ClassNotFoundException { logger.info("Analyzing target class"); Class<?> targetClass = Properties.getTargetClass(); TestCluster cluster = TestCluster.getInstance(); Set<Class<?>> targetClasses = new LinkedHashSet<Class<?>>(); if (targetClass == null) { throw new RuntimeException("Failed to load " + Properties.TARGET_CLASS); } targetClasses.add(targetClass); addDeclaredClasses(targetClasses, targetClass); if (Modifier.isAbstract(targetClass.getModifiers())) { logger.info("SUT is an abstract class"); Set<Class<?>> subclasses = getConcreteClasses(targetClass, inheritanceTree); logger.info("Found {} concrete subclasses", subclasses.size()); targetClasses.addAll(subclasses); } // To make sure we also have anonymous inner classes double check inner classes using ASM ClassNode targetClassNode = DependencyAnalysis.getClassNode(Properties.TARGET_CLASS); Queue<InnerClassNode> innerClasses = new LinkedList<InnerClassNode>(); innerClasses.addAll(targetClassNode.innerClasses); while (!innerClasses.isEmpty()) { InnerClassNode icn = innerClasses.poll(); try { logger.debug("Loading inner class: {}, {},{}", icn.innerName, icn.name, icn.outerName); String innerClassName = ResourceList.getClassNameFromResourcePath(icn.name); Class<?> innerClass = TestGenerationContext.getInstance().getClassLoaderForSUT() .loadClass(innerClassName); //if (!canUse(innerClass)) // continue; // Sometimes strange things appear such as Map$Entry if (!targetClasses.contains(innerClass)) { // && !innerClassName.matches(".*\\$\\d+(\\$.*)?$")) { logger.info("Adding inner class {}", innerClassName); targetClasses.add(innerClass); ClassNode innerClassNode = DependencyAnalysis.getClassNode(innerClassName); innerClasses.addAll(innerClassNode.innerClasses); } } catch (Throwable t) { logger.error("Problem for {}. Error loading inner class: {}, {},{}: {}", Properties.TARGET_CLASS, icn.innerName, icn.name, icn.outerName, t); } } for (Class<?> clazz : targetClasses) { logger.info("Current SUT class: {}", clazz); if (!canUse(clazz)) { logger.info("Cannot access SUT class: {}", clazz); continue; } // Add all constructors for (Constructor<?> constructor : getConstructors(clazz)) { logger.info("Checking target constructor {}", constructor); String name = "<init>" + org.objectweb.asm.Type.getConstructorDescriptor(constructor); if (Properties.TT) { String orig = name; name = BooleanTestabilityTransformation.getOriginalNameDesc(clazz.getName(), "<init>", org.objectweb.asm.Type.getConstructorDescriptor(constructor)); if (!orig.equals(name)) logger.info("TT name: {} -> {}", orig, name); } if (canUse(constructor)) { GenericConstructor genericConstructor = new GenericConstructor(constructor, clazz); cluster.addTestCall(genericConstructor); // TODO: Add types! cluster.addGenerator(new GenericClass(clazz).getWithWildcardTypes(), genericConstructor); addDependencies(genericConstructor, 1); logger.debug("Keeping track of {}.{}{}", constructor.getDeclaringClass().getName(), constructor.getName(), Type.getConstructorDescriptor(constructor)); } else { logger.debug("Constructor cannot be used: {}", constructor); } } // Add all methods for (Method method : getMethods(clazz)) { logger.info("Checking target method {}", method); String name = method.getName() + org.objectweb.asm.Type.getMethodDescriptor(method); if (Properties.TT) { String orig = name; name = BooleanTestabilityTransformation.getOriginalNameDesc(clazz.getName(), method.getName(), org.objectweb.asm.Type.getMethodDescriptor(method)); if (!orig.equals(name)) logger.info("TT name: {} -> {}", orig, name); } if (canUse(method, clazz)) { logger.debug("Adding method {}.{}{}", clazz.getName(), method.getName(), Type.getMethodDescriptor(method)); GenericMethod genericMethod = new GenericMethod(method, clazz); cluster.addTestCall(genericMethod); cluster.addModifier(new GenericClass(clazz).getWithWildcardTypes(), genericMethod); addDependencies(genericMethod, 1); GenericClass retClass = new GenericClass(method.getReturnType()); if (!retClass.isPrimitive() && !retClass.isVoid() && !retClass.isObject()) cluster.addGenerator(retClass.getWithWildcardTypes(), genericMethod); } else { logger.debug("Method cannot be used: {}", method); } } for (Field field : getFields(clazz)) { logger.info("Checking target field {}", field); if (canUse(field, clazz)) { GenericField genericField = new GenericField(field, clazz); addDependencies(genericField, 1); cluster.addGenerator(new GenericClass(field.getGenericType()).getWithWildcardTypes(), genericField); logger.debug("Adding field {}", field); if (!Modifier.isFinal(field.getModifiers())) { logger.debug("Is not final"); cluster.addTestCall(new GenericField(field, clazz)); } else { logger.debug("Is final"); if (Modifier.isStatic(field.getModifiers()) && !field.getType().isPrimitive()) { logger.debug("Is static non-primitive"); /* * With this we are trying to cover such cases: * public static final DurationField INSTANCE = new MillisDurationField(); private MillisDurationField() { super(); } */ try { Object o = field.get(null); if (o == null) { logger.info("Field is not yet initialized: {}", field); } else { Class<?> actualClass = o.getClass(); logger.debug("Actual class is {}", actualClass); if (!actualClass.isAssignableFrom(genericField.getRawGeneratedType()) && genericField.getRawGeneratedType().isAssignableFrom(actualClass)) { GenericField superClassField = new GenericField(field, clazz); cluster.addGenerator(new GenericClass(actualClass), superClassField); } } } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } else { logger.debug("Can't use field {}", field); } } analyzedClasses.add(clazz); // TODO: Set to generic type rather than class? cluster.getAnalyzedClasses().add(clazz); } if (Properties.INSTRUMENT_PARENT) { for (String superClass : inheritanceTree.getSuperclasses(Properties.TARGET_CLASS)) { try { Class<?> superClazz = TestGenerationContext.getInstance().getClassLoaderForSUT() .loadClass(superClass); dependencies.add(new Pair(0, superClazz)); } catch (ClassNotFoundException e) { logger.error("Problem for {}. Class not found: {}", Properties.TARGET_CLASS, superClass, e); } } } if (Properties.HANDLE_STATIC_FIELDS) { GetStaticGraph getStaticGraph = GetStaticGraphGenerator.generate(Properties.TARGET_CLASS); Map<String, Set<String>> staticFields = getStaticGraph.getStaticFields(); for (String className : staticFields.keySet()) { logger.info("Adding static fields to cluster for class {}", className); Class<?> clazz; try { clazz = getClass(className); } catch (ExceptionInInitializerError ex) { logger.debug("Class class init caused exception {}", className); continue; } if (clazz == null) { logger.debug("Class not found {}", className); continue; } if (!canUse(clazz)) continue; Set<String> fields = staticFields.get(className); for (Field field : getFields(clazz)) { if (!canUse(field, clazz)) continue; if (fields.contains(field.getName())) { if (!Modifier.isFinal(field.getModifiers())) { logger.debug("Is not final"); cluster.addTestCall(new GenericField(field, clazz)); } } } } PutStaticMethodCollector collector = new PutStaticMethodCollector(Properties.TARGET_CLASS, staticFields); Set<MethodIdentifier> methodIdentifiers = collector.collectMethods(); for (MethodIdentifier methodId : methodIdentifiers) { Class<?> clazz = getClass(methodId.getClassName()); if (clazz == null) continue; if (!canUse(clazz)) continue; Method method = getMethod(clazz, methodId.getMethodName(), methodId.getDesc()); if (method == null) continue; GenericMethod genericMethod = new GenericMethod(method, clazz); cluster.addTestCall(genericMethod); } } logger.info("Finished analyzing target class"); } private Method getMethod(Class<?> clazz, String methodName, String desc) { for (Method method : clazz.getMethods()) { if (method.getName().equals(methodName) && Type.getMethodDescriptor(method).equals(desc)) return method; } return null; } private Class<?> getClass(String className) { try { Class<?> clazz = Class.forName(className, true, TestGenerationContext.getInstance().getClassLoaderForSUT()); return clazz; } catch (ClassNotFoundException e) { return null; } catch (NoClassDefFoundError e) { // an ExceptionInInitializationError might have happened during class initialization. return null; } } /** * Get the set of constructors defined in this class and its superclasses * * @param clazz * @return */ public static Set<Constructor<?>> getConstructors(Class<?> clazz) { Map<String, Constructor<?>> helper = new TreeMap<String, Constructor<?>>(); Set<Constructor<?>> constructors = new LinkedHashSet<Constructor<?>>(); try { for (Constructor<?> c : clazz.getDeclaredConstructors()) { helper.put(org.objectweb.asm.Type.getConstructorDescriptor(c), c); } } catch (Throwable t) { logger.info("Error while analyzing class {}: {}", clazz, t); } for (Constructor<?> c : helper.values()) { constructors.add(c); } return constructors; } /** * Get the set of methods defined in this class and its superclasses * * @param clazz * @return */ public static Set<Method> getMethods(Class<?> clazz) { Map<String, Method> helper = new TreeMap<String, Method>(); if (clazz.getSuperclass() != null) { for (Method m : getMethods(clazz.getSuperclass())) { helper.put(m.getName() + org.objectweb.asm.Type.getMethodDescriptor(m), m); } } for (Class<?> in : clazz.getInterfaces()) { for (Method m : getMethods(in)) { helper.put(m.getName() + org.objectweb.asm.Type.getMethodDescriptor(m), m); } } try { for (Method m : clazz.getDeclaredMethods()) { helper.put(m.getName() + org.objectweb.asm.Type.getMethodDescriptor(m), m); } } catch (NoClassDefFoundError e) { // TODO: What shall we do? logger.info("Error while trying to load methods of class {}: {}", clazz.getName(), e); } Set<Method> methods = new LinkedHashSet<Method>(); methods.addAll(helper.values()); return methods; } /** * Get the set of fields defined in this class and its superclasses * * @param clazz * @return */ public static Set<Field> getFields(Class<?> clazz) { // TODO: Helper not necessary here! Map<String, Field> helper = new TreeMap<String, Field>(); Set<Field> fields = new LinkedHashSet<Field>(); if (clazz.getSuperclass() != null) { for (Field f : getFields(clazz.getSuperclass())) { helper.put(f.toGenericString(), f); } } for (Class<?> in : clazz.getInterfaces()) { for (Field f : getFields(in)) { helper.put(f.toGenericString(), f); } } try { for (Field f : clazz.getDeclaredFields()) { helper.put(f.toGenericString(), f); } } catch (NoClassDefFoundError e) { // TODO: What shall we do? logger.info("Error while trying to load fields of class {}: {}", clazz.getName(), e); } fields.addAll(helper.values()); return fields; } /** * Get the set of fields defined in this class and its superclasses * * @param clazz * @return */ public static Set<Field> getAccessibleFields(Class<?> clazz) { Set<Field> fields = new LinkedHashSet<Field>(); try { for (Field f : clazz.getFields()) { if (canUse(f) && !Modifier.isFinal(f.getModifiers())) { fields.add(f); } } } catch (Throwable t) { logger.info("Error while accessing fields of class {} - check allowed permissions: {}", clazz.getName(), t); } return fields; } private static boolean isEvoSuiteClass(Class<?> c) { return c.getName().startsWith("org.evosuite"); //|| c.getName().startsWith("edu.uta.cse.dsc"); // TODO: Why was this here? //|| c.getName().equals("java.lang.String"); // This is now handled in addDependencyClass } protected static void makeAccessible(Field field) { if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) { field.setAccessible(true); } } protected static void makeAccessible(Method method) { if (!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) { method.setAccessible(true); } } protected static void makeAccessible(Constructor<?> constructor) { if (!Modifier.isPublic(constructor.getModifiers()) || !Modifier.isPublic(constructor.getDeclaringClass().getModifiers())) { constructor.setAccessible(true); } } private static final Pattern ANONYMOUS_MATCHER1 = Pattern.compile(".*\\$\\d+.*$"); private static final Pattern ANONYMOUS_MATCHER2 = Pattern.compile(".*\\.\\d+.*$"); public static boolean canUse(Class<?> c) { //if (Throwable.class.isAssignableFrom(c)) // return false; if (Modifier.isPrivate(c.getModifiers())) return false; if (!Properties.USE_DEPRECATED && c.isAnnotationPresent(Deprecated.class)) { logger.debug("Skipping deprecated class {}", c.getName()); return false; } if (c.isAnonymousClass()) { return false; } if (ANONYMOUS_MATCHER1.matcher(c.getName()).matches()) { logger.debug("{} looks like an anonymous class, ignoring it", c); return false; } if (ANONYMOUS_MATCHER2.matcher(c.getName()).matches()) { logger.debug("{} looks like an anonymous class, ignoring it", c); return false; } if (c.getName().startsWith("junit")) return false; if (isEvoSuiteClass(c) && !MockList.isAMockClass(c.getCanonicalName())) { return false; } if (c.getEnclosingClass() != null) { if (!canUse(c.getEnclosingClass())) return false; } if (c.getDeclaringClass() != null) { if (!canUse(c.getDeclaringClass())) return false; } // If the SUT is not in the default package, then // we cannot import classes that are in the default // package if (!c.isArray() && !c.isPrimitive() && !Properties.CLASS_PREFIX.isEmpty() && !c.getName().contains(".")) { return false; } if (Modifier.isPublic(c.getModifiers())) { return true; } // If default access rights, then check if this class is in the same package as the target class if (!Modifier.isPrivate(c.getModifiers())) { // && !Modifier.isProtected(c.getModifiers())) { String packageName = ClassUtils.getPackageName(c); if (packageName.equals(Properties.CLASS_PREFIX)) { return true; } } logger.debug("Not public"); return false; } public static boolean canUse(Field f) { return canUse(f, f.getDeclaringClass()); } public static boolean canUse(Field f, Class<?> ownerClass) { // TODO we could enable some methods from Object, like getClass if (f.getDeclaringClass().equals(java.lang.Object.class)) return false;// handled here to avoid printing reasons if (f.getDeclaringClass().equals(java.lang.Thread.class)) return false;// handled here to avoid printing reasons if (!Properties.USE_DEPRECATED && f.isAnnotationPresent(Deprecated.class)) { logger.debug("Skipping deprecated field {}", f.getName()); return false; } if (f.isSynthetic()) { logger.debug("Skipping synthetic field {}", f.getName()); return false; } if (f.getName().startsWith("ajc$")) { logger.debug("Skipping AspectJ field {}", f.getName()); return false; } if (!f.getType().equals(String.class) && !canUse(f.getType())) { return false; } if (Modifier.isPublic(f.getModifiers())) { // It may still be the case that the field is defined in a non-visible superclass of the class // we already know we can use. In that case, the compiler would be fine with accessing the // field, but reflection would start complaining about IllegalAccess! // Therefore, we set the field accessible to be on the safe side makeAccessible(f); return true; } // If default access rights, then check if this class is in the same package as the target class if (!Modifier.isPrivate(f.getModifiers())) { // && !Modifier.isProtected(f.getModifiers())) { String packageName = ClassUtils.getPackageName(ownerClass); String declaredPackageName = ClassUtils.getPackageName(f.getDeclaringClass()); if (packageName.equals(Properties.CLASS_PREFIX) && packageName.equals(declaredPackageName)) { makeAccessible(f); return true; } } return false; } public static boolean canUse(Method m) { return canUse(m, m.getDeclaringClass()); } public static boolean canUse(Method m, Class<?> ownerClass) { if (m.isBridge()) { logger.debug("Excluding bridge method: {}", m.toString()); return false; } if (m.isSynthetic()) { logger.debug("Excluding synthetic method: {}", m.toString()); return false; } if (!Properties.USE_DEPRECATED && m.isAnnotationPresent(Deprecated.class)) { logger.debug("Excluding deprecated method {}", m.getName()); return false; } if (m.isAnnotationPresent(Test.class)) { logger.debug("Excluding test method {}", m.getName()); return false; } if (m.isAnnotationPresent(EvoSuiteExclude.class)) { logger.debug("Excluding method with exclusion annotation {}", m.getName()); return false; } if (m.getDeclaringClass().equals(java.lang.Object.class)) { return false; } if (!m.getReturnType().equals(String.class) && !canUse(m.getReturnType())) { return false; } if (m.getDeclaringClass().equals(Enum.class)) { return false; /* if (m.getName().equals("valueOf") || m.getName().equals("values") || m.getName().equals("ordinal")) { logger.debug("Excluding valueOf for Enum " + m.toString()); return false; } // Skip compareTo on enums (like Randoop) if (m.getName().equals("compareTo") && m.getParameterTypes().length == 1 && m.getParameterTypes()[0].equals(Enum.class)) return false; */ } if (m.getDeclaringClass().equals(java.lang.Thread.class)) return false; // Hashcode only if we need to cover it if (m.getName().equals("hashCode") && !m.getDeclaringClass().equals(Properties.getTargetClass())) return false; // Randoop special case: just clumps together a bunch of hashCodes, so skip it if (m.getName().equals("deepHashCode") && m.getDeclaringClass().equals(Arrays.class)) return false; // Randoop special case: differs too much between JDK installations if (m.getName().equals("getAvailableLocales")) return false; if (m.getName().equals(ClassResetter.STATIC_RESET)) { logger.debug("Ignoring static reset class"); return false; } if (isForbiddenNonDeterministicCall(m)) { return false; } if (!Properties.CONSIDER_MAIN_METHODS && m.getName().equals("main") && Modifier.isStatic(m.getModifiers()) && Modifier.isPublic(m.getModifiers())) { logger.debug("Ignoring static main method "); return false; } /* if(m.getTypeParameters().length > 0) { logger.debug("Cannot handle generic methods at this point"); if(m.getDeclaringClass().equals(Properties.getTargetClass())) { LoggingUtils.getEvoLogger().info("* Skipping method "+m.getName()+": generic methods are not handled yet"); } return false; } */ // If default or if (Modifier.isPublic(m.getModifiers())) { makeAccessible(m); return true; } // If default access rights, then check if this class is in the same package as the target class if (!Modifier.isPrivate(m.getModifiers())) { // && !Modifier.isProtected(m.getModifiers())) { String packageName = ClassUtils.getPackageName(ownerClass); String declaredPackageName = ClassUtils.getPackageName(m.getDeclaringClass()); if (packageName.equals(Properties.CLASS_PREFIX) && packageName.equals(declaredPackageName)) { makeAccessible(m); return true; } } return false; } private static boolean hasStaticGenerator(Class<?> clazz) { for (Method m : clazz.getMethods()) { if (Modifier.isStatic(m.getModifiers())) { if (clazz.isAssignableFrom(m.getReturnType())) { return true; } } } return false; } /** * If we try to get deterministic tests, we must not include these methods * * @param m * @return */ private static boolean isForbiddenNonDeterministicCall(Method m) { if (!Properties.REPLACE_CALLS) return false; Class<?> declaringClass = m.getDeclaringClass(); // Calendar is initialized with current time if (declaringClass.equals(Calendar.class)) { if (m.getName().equals("getInstance")) return true; } // Locale will return system specific information if (declaringClass.equals(Locale.class)) { if (m.getName().equals("getDefault")) return true; if (m.getName().equals("getAvailableLocales")) return true; } // MessageFormat will return system specific information if (declaringClass.equals(MessageFormat.class)) { if (m.getName().equals("getLocale")) return true; } if (m.getDeclaringClass().equals(Date.class)) { if (m.getName().equals("toLocaleString")) return true; } return false; } /** * If we try to get deterministic tests, we must not include these * constructors * * @param c * @return */ private static boolean isForbiddenNonDeterministicCall(Constructor<?> c) { if (!Properties.REPLACE_CALLS) return false; // Date default constructor uses current time if (c.getDeclaringClass().equals(Date.class)) { if (c.getParameterTypes().length == 0) return true; } // Random without seed parameter is...random if (c.getDeclaringClass().equals(Random.class)) { if (c.getParameterTypes().length == 0) return true; } return false; } public static boolean canUse(Constructor<?> c) { if (c.isSynthetic()) { return false; } // synthetic constructors are OK if (Modifier.isAbstract(c.getDeclaringClass().getModifiers())) return false; // TODO we could enable some methods from Object, like getClass //if (c.getDeclaringClass().equals(java.lang.Object.class)) // return false;// handled here to avoid printing reasons if (c.getDeclaringClass().equals(java.lang.Thread.class)) return false;// handled here to avoid printing reasons if (c.getDeclaringClass().isAnonymousClass()) return false; if (c.getDeclaringClass().isLocalClass()) { logger.debug("Skipping constructor of local class {}", c.getName()); return false; } if (c.getDeclaringClass().isMemberClass() && !canUse(c.getDeclaringClass())) return false; if (!Properties.USE_DEPRECATED && c.getAnnotation(Deprecated.class) != null) { logger.debug("Skipping deprecated constructor {}", c.getName()); return false; } if (isForbiddenNonDeterministicCall(c)) { return false; } if (Modifier.isPublic(c.getModifiers())) { makeAccessible(c); return true; } // If default access rights, then check if this class is in the same package as the target class if (!Modifier.isPrivate(c.getModifiers())) { // && !Modifier.isProtected(c.getModifiers())) { String packageName = ClassUtils.getPackageName(c.getDeclaringClass()); if (packageName.equals(Properties.CLASS_PREFIX)) { makeAccessible(c); return true; } } return false; } private final Set<Class<?>> analyzedClasses = new LinkedHashSet<Class<?>>(); private static class Pair { private final int recursion; private final GenericClass dependencyClass; public Pair(int recursion, java.lang.reflect.Type dependencyClass) { this.recursion = recursion; this.dependencyClass = new GenericClass(dependencyClass); } public int getRecursion() { return recursion; } public GenericClass getDependencyClass() { return dependencyClass; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((dependencyClass == null) ? 0 : dependencyClass.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Pair other = (Pair) obj; if (dependencyClass == null) { if (other.dependencyClass != null) return false; } else if (!dependencyClass.equals(other.dependencyClass)) return false; return true; } }; private final Set<Pair> dependencies = new LinkedHashSet<Pair>(); private InheritanceTree inheritanceTree = null; private void addDependencies(GenericConstructor constructor, int recursionLevel) { if (recursionLevel > Properties.CLUSTER_RECURSION) { logger.debug("Maximum recursion level reached, not adding dependencies of {}", constructor); return; } if (dependencyCache.contains(constructor)) { return; } logger.debug("Analyzing dependencies of {}", constructor); dependencyCache.add(constructor); for (java.lang.reflect.Type parameterClass : constructor.getRawParameterTypes()) { logger.debug("Adding dependency {}", parameterClass); addDependency(new GenericClass(parameterClass), recursionLevel); } } private void addDependencies(GenericMethod method, int recursionLevel) { if (recursionLevel > Properties.CLUSTER_RECURSION) { logger.debug("Maximum recursion level reached, not adding dependencies of {}", method); return; } if (dependencyCache.contains(method)) { return; } logger.debug("Analyzing dependencies of {}", method); dependencyCache.add(method); for (java.lang.reflect.Type parameter : method.getRawParameterTypes()) { logger.debug("Current parameter {}", parameter); GenericClass parameterClass = new GenericClass(parameter); if (parameterClass.isPrimitive() || parameterClass.isString()) continue; logger.debug("Adding dependency {}", parameterClass.getClassName()); addDependency(parameterClass, recursionLevel); } } private void addDependencies(GenericField field, int recursionLevel) { if (recursionLevel > Properties.CLUSTER_RECURSION) { logger.debug("Maximum recursion level reached, not adding dependencies of {}", field); return; } if (dependencyCache.contains(field)) { return; } if (field.getField().getType().isPrimitive() || field.getField().getType().equals(String.class)) return; logger.debug("Analyzing dependencies of {}", field); dependencyCache.add(field); logger.debug("Adding dependency {}", field.getName()); addDependency(new GenericClass(field.getGenericFieldType()), recursionLevel); } private void addDependency(GenericClass clazz, int recursionLevel) { clazz = clazz.getRawGenericClass(); if (analyzedClasses.contains(clazz.getRawClass())) return; if (clazz.isPrimitive()) return; if (clazz.isString()) return; if (clazz.getRawClass().equals(Enum.class)) return; if (clazz.isArray()) { addDependency(new GenericClass(clazz.getComponentType()), recursionLevel); return; } if (!canUse(clazz.getRawClass())) return; Class<?> mock = MockList.getMockClass(clazz.getRawClass().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 {} instead of {}", mock, clazz); clazz = new GenericClass(mock); } else { if (!checkIfCanUse(clazz.getClassName())) { return; } } for (Pair pair : dependencies) { if (pair.getDependencyClass().equals(clazz)) { return; } } logger.debug("Getting concrete classes for {}", clazz.getClassName()); ConstantPoolManager.getInstance().addNonSUTConstant(Type.getType(clazz.getRawClass())); List<Class<?>> actualClasses = new ArrayList<Class<?>>( getConcreteClasses(clazz.getRawClass(), inheritanceTree)); // Randomness.shuffle(actualClasses); logger.debug("Concrete classes for {}: {}", clazz.getClassName(), actualClasses.size()); //dependencies.add(new Pair(recursionLevel, Randomness.choice(actualClasses))); for (Class<?> targetClass : actualClasses) { logger.debug("Adding concrete class: {}", targetClass); dependencies.add(new Pair(recursionLevel, targetClass)); //if(++num >= Properties.NUM_CONCRETE_SUBTYPES) // break; } } private boolean addDependencyClass(GenericClass clazz, int recursionLevel) { if (recursionLevel > Properties.CLUSTER_RECURSION) { logger.debug("Maximum recursion level reached, not adding dependency {}", clazz.getClassName()); return false; } clazz = clazz.getRawGenericClass(); if (analyzedClasses.contains(clazz.getRawClass())) { return true; } analyzedClasses.add(clazz.getRawClass()); // We keep track of generic containers in case we find other concrete generic components during runtime if (clazz.isAssignableTo(Collection.class) || clazz.isAssignableTo(Map.class)) { if (clazz.getNumParameters() > 0) { containerClasses.add(clazz.getRawClass()); } } if (clazz.equals(String.class)) { return false; } try { TestCluster cluster = TestCluster.getInstance(); logger.debug("Adding dependency class {}", clazz.getClassName()); // TODO: Should we include declared classes as well? if (!canUse(clazz.getRawClass())) { logger.info("*** Cannot use class: {}", clazz.getClassName()); return false; } // Add all constructors for (Constructor<?> constructor : getConstructors(clazz.getRawClass())) { String name = "<init>" + org.objectweb.asm.Type.getConstructorDescriptor(constructor); if (Properties.TT) { String orig = name; name = BooleanTestabilityTransformation.getOriginalNameDesc(clazz.getClassName(), "<init>", org.objectweb.asm.Type.getConstructorDescriptor(constructor)); if (!orig.equals(name)) logger.info("TT name: {} -> {}", orig, name); } if (canUse(constructor)) { GenericConstructor genericConstructor = new GenericConstructor(constructor, clazz); try { cluster.addGenerator(clazz.getWithWildcardTypes(), genericConstructor); addDependencies(genericConstructor, recursionLevel + 1); logger.debug("Keeping track of {}.{}{}", constructor.getDeclaringClass().getName(), constructor.getName(), Type.getConstructorDescriptor(constructor)); } catch (Throwable t) { logger.info("Error adding constructor {}: {}", constructor.getName(), t.getMessage()); } } else { logger.debug("Constructor cannot be used: {}", constructor); } } // Add all methods for (Method method : getMethods(clazz.getRawClass())) { String name = method.getName() + org.objectweb.asm.Type.getMethodDescriptor(method); if (Properties.TT) { String orig = name; name = BooleanTestabilityTransformation.getOriginalNameDesc(clazz.getClassName(), method.getName(), org.objectweb.asm.Type.getMethodDescriptor(method)); if (!orig.equals(name)) logger.info("TT name: {} -> {}", orig, name); } if (canUse(method, clazz.getRawClass()) && !method.getName().equals("hashCode")) { logger.debug("Adding method {}.{}{}", clazz.getClassName(), method.getName(), Type.getMethodDescriptor(method)); if (method.getTypeParameters().length > 0) { logger.info("Type parameters in methods are not handled yet, skipping {}", method); continue; } GenericMethod genericMethod = new GenericMethod(method, clazz); try { addDependencies(genericMethod, recursionLevel + 1); cluster.addModifier(clazz.getWithWildcardTypes(), genericMethod); // GenericClass retClass = new GenericClass( // genericMethod.getReturnType(), method.getReturnType()); GenericClass retClass = new GenericClass(method.getReturnType()); if (!retClass.isPrimitive() && !retClass.isVoid() && !retClass.isObject()) { cluster.addGenerator(retClass.getWithWildcardTypes(), genericMethod); } } catch (Throwable t) { logger.info("Error adding method {}: {}", method.getName(), t.getMessage()); } } else { logger.debug("Method cannot be used: {}", method); } } // Add all fields for (Field field : getFields(clazz.getRawClass())) { logger.debug("Checking field {}", field); if (canUse(field, clazz.getRawClass())) { logger.debug("Adding field {} for class {}", field, clazz); try { GenericField genericField = new GenericField(field, clazz); cluster.addGenerator(new GenericClass(field.getGenericType()).getWithWildcardTypes(), genericField); if (!Modifier.isFinal(field.getModifiers())) { cluster.addModifier(clazz.getWithWildcardTypes(), genericField); addDependencies(genericField, recursionLevel + 1); } } catch (Throwable t) { logger.info("Error adding field {}: {}", field.getName(), t.getMessage()); } } else { logger.debug("Field cannot be used: {}", field); } } logger.info("Finished analyzing {} at recursion level {}", clazz.getTypeName(), recursionLevel); cluster.getAnalyzedClasses().add(clazz.getRawClass()); } catch (Throwable t) { /* * NOTE: this is a problem we know it can happen in some cases in SF110, but don't * have a real solution now. As it is bound to happen, we try to minimize the logging (eg no * stack trace), although we still need to log it */ logger.error("Problem for {}. Failed to add dependencies for class {}: {}\n{}", Properties.TARGET_CLASS, clazz.getClassName(), t, Arrays.asList(t.getStackTrace())); return false; } return true; } public static Set<Class<?>> getConcreteClasses(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(java.util.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(java.lang.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)) { Set<String> subClasses = inheritanceTree.getSubclasses(clazz.getName()); 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 = 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 (classDistance.get(subClass) == distance) { try { Class<?> subClazz = Class.forName(subClass, false, TestGenerationContext.getInstance().getClassLoaderForSUT()); if (!canUse(subClazz)) continue; if (subClazz.isInterface()) continue; if (Modifier.isAbstract(subClazz.getModifiers())) { if (!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 {} instead of {}", mock, clazz); subClazz = mock; } else { if (!checkIfCanUse(subClazz.getCanonicalName())) { continue; } } actualClasses.add(subClazz); } catch (ClassNotFoundException e) { logger.error("Problem for {}. Class not found: {}", Properties.TARGET_CLASS, subClass, e); logger.error("Removing class from inheritance tree"); inheritanceTree.removeClass(subClass); } } } distance++; } if (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 static Set<Class<?>> getConcreteClassesMap() { Set<Class<?>> mapClasses = new LinkedHashSet<Class<?>>(); Class<?> mapClazz; try { mapClazz = Class.forName("java.util.HashMap", false, TestGenerationContext.getInstance().getClassLoaderForSUT()); mapClasses.add(mapClazz); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return mapClasses; } private static 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) { // TODO Auto-generated catch block e.printStackTrace(); } return mapClasses; } private static 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) { // TODO Auto-generated catch block e.printStackTrace(); } return mapClasses; } private static 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) { // TODO Auto-generated catch block e.printStackTrace(); } return comparableClasses; } private Set<Class<?>> getConcreteClassesEnum() { Set<Class<?>> enumClasses = new LinkedHashSet<Class<?>>(); for (String className : inheritanceTree.getSubclasses("java.lang.Enum")) { logger.warn("Enum candidate: {}", className); } return enumClasses; } /** * Calculate package distance between two classnames * * @param className1 * @param className2 * @return */ private static int getPackageDistance(String className1, String className2) { String[] package1 = className1.split("\\.|\\$"); String[] package2 = className2.split("\\.|\\$"); int distance = 0; int same = 1; int num = 0; while (num < package1.length && num < package2.length && package1[num].equals(package2[num])) { same++; num++; } if (package1.length > same) distance += package1.length - same; if (package2.length > same) distance += package2.length - same; return distance; } }