sp-04/test/ch/usi/inf/sp/cfg/ControlFlowGraphBuilderTest...

84 lines
3.7 KiB
Java

package ch.usi.inf.sp.cfg;
import ch.usi.inf.sp.bytecode.Disassembler;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodNode;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* This tests the ControlFlowGraphBuilder by running it on the compiled ExampleClass.
* It loads the ExampleClass.class (as compiled by Java10's javac).
* The different test methods test the CFGB on different inputs
* (different methods in class ExampleClass).
* The test is VERY WEAK.
* That is, there are many bugs it will not discover.
*/
class ControlFlowGraphBuilderTest {
private static ClassNode classNode;
private static MethodNode getMethod(String name, String desc) {
for (MethodNode methodNode : classNode.methods) {
if (methodNode.name.equals(name) && methodNode.desc.equals(desc)) {
return methodNode;
}
}
throw new IllegalArgumentException("Error in test harness: method "+name+" not found!");
}
@BeforeEach
void setUp() throws IOException {
// Use ASM to read a class file (from test-input) and create a ClassNode
File classFile = new File("test-input/java10/ExampleClass.class");
final ClassReader cr = new ClassReader(new FileInputStream(classFile));
// create an empty ClassNode (in-memory representation of a class)
classNode = new ClassNode();
// have the ClassReader read the class file and populate the ClassNode with the corresponding information
cr.accept(classNode, 0);
}
@Test
void createControlFlowGraphOfEmptyMethod() {
MethodNode methodNode = getMethod("emptyMethod", "()V");
ControlFlowGraph g = ControlFlowGraphBuilder.createControlFlowGraph(methodNode);
assertEquals(3, g.getNodes().size(), "CFG should contain three basic blocks: entry, exit, and one with code");
BasicBlock bb = ((ControlFlowEdge)g.getEntry().getOutEdges().get(0)).getTo();
final InsnList asmInstructions = methodNode.instructions;
assertEquals(asmInstructions.size(), bb.getInstructionCount(), "basic block's instruction count differs from number of instructions in method node");
for (int i = 0; i < asmInstructions.size(); i++) {
AbstractInsnNode asmInstruction = asmInstructions.get(i);
String asmInstructionAsString = Disassembler.disassembleInstruction(asmInstruction, i, asmInstructions);
String bbInstruction = bb.getInstruction(i);
assertEquals(asmInstructionAsString, bbInstruction, "basic block's instruction string differs from disassembled ASM instruction");
//System.out.println(asmInstructionAsString+" == "+bbInstruction);
}
}
@Test
void createControlFlowGraphOfIfMethod() {
MethodNode methodNode = getMethod("ifMethod", "(I)I");
ControlFlowGraph g = ControlFlowGraphBuilder.createControlFlowGraph(methodNode);
assertEquals(5, g.getNodes().size(), "CFG should contain five basic blocks: entry, exit, before if, then, after if");
}
@Test
void createControlFlowGraphOfIfElseMethod() {
MethodNode methodNode = getMethod("ifElseMethod", "(I)I");
ControlFlowGraph g = ControlFlowGraphBuilder.createControlFlowGraph(methodNode);
assertEquals(6, g.getNodes().size(), "CFG should contain five basic blocks: entry, exit, before if, then, else, after if");
}
}