/**
* 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.junit;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.CheckClassAdapter;
/**
* <p>TracingTestRunner class.</p>
*
* @author Gordon Fraser
*/
public class TracingTestRunner extends ClassLoader {
private static class StatementTracingVisitor extends ClassVisitor {
final String className;
public StatementTracingVisitor(String className, ClassWriter writer) {
super(Opcodes.ASM4, writer);
this.className = className;
}
@Override
public MethodVisitor visitMethod(int access, final String name, String desc, String signature,
String[] exceptions) {
return new MethodVisitor(Opcodes.ASM4, super.visitMethod(access, name, desc, signature, exceptions)) {
@Override
public void visitLineNumber(int line, Label start) {
visitLdcInsn(className + "#" + name + ":" + line);
visitMethodInsn(Opcodes.INVOKESTATIC, "org/evosuite/junit/TracingTestRunner",
"traceStatement", "(Ljava/lang/String;)V");
super.visitLineNumber(line, start);
}
};
}
}
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(TracingTestRunner.class);
/**
* <p>main</p>
*
* @param args a {@link java.lang.String} object.
*/
public static void main(String... args) {
if (args.length < 1) {
System.out.println("Give test class to run as argument.");
}
new TracingTestRunner().traceTest(args[0]);
}
/**
* <p>traceStatement</p>
*
* @param location a {@link java.lang.String} object.
*/
public static void traceStatement(String location) {
String code = readCode(location).trim();
if (code.isEmpty() || code.equals("}")) {
return;
}
System.out.println(location + ":\t\t" + code);
}
private static String readCode(String location) {
String javaFile = location;
BufferedReader reader = null;
try {
javaFile = javaFile.split("\\$")[0];
javaFile = javaFile.split("#")[0];
javaFile = javaFile.replaceAll("\\.", "/");
javaFile = "src/test/java/" + javaFile + ".java";
int lineNr = Integer.parseInt(location.split(":")[1]);
reader = new BufferedReader(new FileReader(javaFile));
String line = null;
for (int lineCnt = 0; lineCnt < lineNr; lineCnt++) {
line = reader.readLine();
}
return line;
} catch (Exception exc) {
logger.error("Encountered exception opening file '{}':", javaFile, exc);
throw new RuntimeException(exc);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException exc) {
// muted
}
}
}
}
/** {@inheritDoc} */
@Override
public Class<?> loadClass(String fullyQualifiedTargetClass) throws ClassNotFoundException {
if (isSystemClass(fullyQualifiedTargetClass)) {
logger.info("Not instrumenting class '{}'.", fullyQualifiedTargetClass);
return super.loadClass(fullyQualifiedTargetClass);
}
logger.info("Instrumenting class '{}'.", fullyQualifiedTargetClass);
String className = fullyQualifiedTargetClass.replace('.', '/');
InputStream is = java.lang.ClassLoader.getSystemResourceAsStream(className + ".class");
if (is == null) {
throw new ClassNotFoundException("Class " + fullyQualifiedTargetClass + "could not be found!");
}
ClassReader reader = null;
try {
reader = new ClassReader(is);
} catch (IOException exc) {
throw new ClassNotFoundException();
}
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new StatementTracingVisitor(fullyQualifiedTargetClass, writer);
CheckClassAdapter checkClassAdapter = new CheckClassAdapter(cv);
reader.accept(checkClassAdapter, ClassReader.SKIP_FRAMES);
byte[] byteBuffer = writer.toByteArray();
Class<?> result = defineClass(fullyQualifiedTargetClass, byteBuffer, 0, byteBuffer.length);
return result;
}
private boolean isSystemClass(String className) {
if (className.startsWith("java.")) {
return true;
}
if (className.startsWith("sun.")) {
return true;
}
if (className.startsWith("org.junit.") || className.startsWith("junit.framework")) {
return true;
}
if (className.equals(this.getClass().getName())) {
return true;
}
return false;
}
private void traceTest(String testCaseName) {
Class<?> testCase = null;
try {
testCase = loadClass(testCaseName);
} catch (ClassNotFoundException exc) {
throw new RuntimeException(exc);
}
Result result = JUnitCore.runClasses(testCase);
if (!result.getFailures().isEmpty()) {
logger.info("{} tests failed: {}", result.getFailureCount(), result.getFailures());
}
}
}