org.evosuite.junit.DetermineSUT 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.junit;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.evosuite.PackageInfo;
import org.evosuite.Properties;
import org.evosuite.TestGenerationContext;
import org.evosuite.annotations.EvoSuiteTest;
import org.evosuite.classpath.ClassPathHacker;
import org.evosuite.classpath.ResourceList;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* For a given JUnit test class, determine which class it seems to be testing.
*
* @author Gordon Fraser
*
*/
public class DetermineSUT {
private final static Logger logger = LoggerFactory.getLogger(DetermineSUT.class);
private String targetName = "";
private Set superClasses = new HashSet();
public static class NoJUnitClassException extends Exception {
private static final long serialVersionUID = -7035856440476749976L;
}
private class TargetClassSorter implements Comparator {
private final String targetClass;
public TargetClassSorter(String target) {
this.targetClass = target;
}
@Override
public int compare(String arg0, String arg1) {
double distance1 = StringUtils.getLevenshteinDistance(targetClass, arg0);
double distance2 = StringUtils.getLevenshteinDistance(targetClass, arg1);
return Double.compare(distance1, distance2);
}
}
public String getSUTName(String fullyQualifiedTargetClass, String targetClassPath)
throws NoJUnitClassException {
this.targetName = fullyQualifiedTargetClass;
try {
ClassPathHacker.addFile(targetClassPath);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Set targetClasses = ResourceList.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).getAllClasses(targetClassPath,false);
Set candidateClasses = new HashSet();
boolean hasJUnit = false;
try {
candidateClasses.addAll(determineCalledClasses(fullyQualifiedTargetClass,
targetClasses));
hasJUnit = true;
} catch (ClassNotFoundException e) {
// Ignore, the set will be empty?
logger.error("Class not found: " + e, e);
return "";
} catch (NoJUnitClassException e) {
}
if (!hasJUnit)
throw new NoJUnitClassException();
if (candidateClasses.isEmpty())
return "";
List sortedNames = new ArrayList(candidateClasses);
Collections.sort(sortedNames, new TargetClassSorter(fullyQualifiedTargetClass));
//System.out.println("Sorted candidate classes: " + sortedNames);
return sortedNames.get(0);
}
public Set determineCalledClasses(String fullyQualifiedTargetClass,
Set targetClasses) throws ClassNotFoundException,
NoJUnitClassException {
Set calledClasses = new HashSet();
String className = fullyQualifiedTargetClass.replace('.', '/');
try {
InputStream is = ClassLoader.getSystemResourceAsStream(className + ".class");
if (is == null) {
throw new ClassNotFoundException("Class '" + className + ".class"
+ "' should be in target project, but could not be found!");
}
ClassReader reader = new ClassReader(is);
ClassNode classNode = new ClassNode();
reader.accept(classNode, ClassReader.SKIP_FRAMES);
superClasses = getSuperClasses(classNode);
if (isJUnitTest(classNode)) {
handleClassNode(calledClasses, classNode, targetClasses);
} else {
throw new NoJUnitClassException();
}
} catch (IOException e) {
e.printStackTrace();
}
calledClasses.remove("java.lang.Object");
calledClasses.remove(fullyQualifiedTargetClass);
return calledClasses;
}
@SuppressWarnings("unchecked")
private void handleClassNode(Set calledClasses, ClassNode cn,
Set targetClasses) throws IOException {
List methods = cn.methods;
for (MethodNode mn : methods) {
handleMethodNode(calledClasses, cn, mn, targetClasses);
}
}
/**
*
* isJavaClass
*
*
* @param classNameWithDots
* a {@link java.lang.String} object.
* @return a boolean.
*/
public static boolean isJavaClass(String classNameWithDots) {
return classNameWithDots.startsWith("java.") //
|| classNameWithDots.startsWith("javax.") //
|| classNameWithDots.startsWith("sun.") //
|| classNameWithDots.startsWith("apple.")
|| classNameWithDots.startsWith("com.apple.");
}
private boolean isValidClass(String name) throws IOException {
if (isJavaClass(name))
return false;
if (name.startsWith("junit"))
return false;
if (name.startsWith("org.junit"))
return false;
if (name.startsWith(targetName))
return false;
if (superClasses.contains(name))
return false;
ClassNode sutNode = loadClassNode(name);
if (isJUnitTest(sutNode)) {
return false;
}
return true;
}
@SuppressWarnings("unchecked")
private void handleMethodNode(Set calledClasses, ClassNode cn, MethodNode mn,
Set targetClasses) throws IOException {
InsnList instructions = mn.instructions;
Iterator iterator = instructions.iterator();
while (iterator.hasNext()) {
AbstractInsnNode insn = iterator.next();
if (insn instanceof MethodInsnNode) {
String name = ResourceList.getClassNameFromResourcePath(((MethodInsnNode) insn).owner);
if (!targetClasses.contains(name))
continue;
if (isValidClass(name))
calledClasses.add(name);
}
}
}
@SuppressWarnings("unchecked")
private boolean isJUnitTest(ClassNode cn) throws IOException {
// We do not consider abstract classes
if ((cn.access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT)
return false;
if (hasJUnitSuperclass(cn))
return true;
List methods = cn.methods;
for (MethodNode mn : methods) {
List annotations = mn.visibleAnnotations;
if (annotations != null) {
for (AnnotationNode an : annotations) {
if (an.desc.equals("Lorg/junit/Test;") ||
an.desc.equals("L"+PackageInfo.getNameWithSlash(EvoSuiteTest.class)+";"))
return true;
}
}
}
return false;
}
private Set getSuperClasses(ClassNode cn) throws IOException {
Set superClasses = new HashSet<>();
String currentSuper = cn.superName;
while (!currentSuper.equals("java/lang/Object")) {
superClasses.add(ResourceList.getClassNameFromResourcePath(currentSuper));
ClassNode superNode = loadClassNode(currentSuper);
currentSuper = superNode.superName;
}
return superClasses;
}
private boolean hasJUnitSuperclass(ClassNode cn) throws IOException {
if (cn.superName.equals("java/lang/Object"))
return false;
if (cn.superName.equals("junit/framework/TestCase"))
return true;
ClassNode superClass = loadClassNode(cn.superName);
return hasJUnitSuperclass(superClass);
}
private ClassNode loadClassNode(String className) throws IOException {
ClassReader reader = new ClassReader(ResourceList.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).getClassAsStream(className));
ClassNode cn = new ClassNode();
reader.accept(cn, ClassReader.SKIP_FRAMES); // | ClassReader.SKIP_DEBUG);
return cn;
}
/**
* @param args
*/
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("Expected parameters: ");
return;
}
Properties.getInstanceSilent();
DetermineSUT det = new DetermineSUT();
try {
System.out.println(det.getSUTName(args[0], args[1]));
} catch (NoJUnitClassException e) {
System.err.println("Found no JUnit test case");
}
}
}