Commit a1dc71e1 authored by Carlos Galindo's avatar Carlos Galindo
Browse files

Add SlicerTest (regression tester)

parent 9c08cc9b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@
        <dependency>
            <groupId>tfm</groupId>
            <artifactId>sdg-core</artifactId>
            <version>1.0.2</version>
            <version>1.1.0</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
+1 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@

    <groupId>tfm</groupId>
    <artifactId>sdg-core</artifactId>
    <version>1.0.2</version>
    <version>1.1.0</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
+94 −0
Original line number Diff line number Diff line
package tfm;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import tfm.graphs.exceptionsensitive.ESSDG;
import tfm.graphs.sdg.SDG;
import tfm.slicing.FileLineSlicingCriterion;
import tfm.slicing.Slice;
import tfm.slicing.SlicingCriterion;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Scanner;

public class SlicerTest {
    static {
        JavaParser.getStaticConfiguration().setAttributeComments(false);
        CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver();
        combinedTypeSolver.add(new ReflectionTypeSolver(true));
        JavaParser.getStaticConfiguration().setSymbolResolver(new JavaSymbolSolver(combinedTypeSolver));
        JavaParser.getStaticConfiguration().setAttributeComments(false);
    }

    private static final String TEST_FILES = "./sdg-core/src/test/res/dinsa-tests";
    private static final String DOT_JAVA = ".java";

    public static Collection<Arguments> findFiles(File directory, String suffix) throws FileNotFoundException {
        Collection<Arguments> res = new LinkedList<>();
        File[] files = directory.listFiles();
        if (files == null) return Collections.emptyList();
        for (File f : files) {
            if (f.getName().endsWith(suffix))
                res.add(Arguments.of(f, getSliceFile(f), getCriterionLine(f)));
            if (f.isDirectory())
                res.addAll(findFiles(f, suffix));
        }
        return res;
    }

    public static Arguments[] findAllFiles() throws FileNotFoundException {
        Collection<Arguments> args = findFiles(new File(TEST_FILES), DOT_JAVA);
        return args.toArray(Arguments[]::new);
    }

    private static File getSliceFile(File file) {
        return new File(file.getParent(), file.getName() + ".sdg.sliced");
    }

    private static int getCriterionLine(File file) throws FileNotFoundException {
        return new Scanner(new File(file.getParent(), file.getName() + ".sdg.criterion")).nextInt();
    }

    @ParameterizedTest(name = "[{index}] {0}")
    @MethodSource("findAllFiles")
    public void sdgCompare(File source, File target, int criterionLine) throws FileNotFoundException {
        // Build the SDG
        SDG sdg = new ESSDG();
        sdg.build(new NodeList<>(JavaParser.parse(source)));
        SlicingCriterion sc = new FileLineSlicingCriterion(source, criterionLine);
        Slice slice = sdg.slice(sc);

        // Convert the slice to code and output the result to `outputDir`
        NodeList<CompilationUnit> slicedUnits = slice.toAst();
        assert slicedUnits.size() == 1;
        if (!target.exists()) {
            try (PrintWriter pw = new PrintWriter(target)) {
                pw.print(slicedUnits.get(0).toString());
            }
            return;
        }
        String targetSlice;
        {
            StringBuilder builder = new StringBuilder();
            Scanner in = new Scanner(target);
            while (in.hasNextLine())
                builder.append(in.nextLine()).append('\n');
            targetSlice = builder.toString();
        }
        String ourSlice = slicedUnits.get(0).toString();
        boolean equal = targetSlice.equals(ourSlice);
        assert equal;
    }
}
+0 −99
Original line number Diff line number Diff line
package tfm.graphs.pdg;

import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.stmt.ContinueStmt;
import com.github.javaparser.ast.stmt.EmptyStmt;
import com.github.javaparser.ast.stmt.IfStmt;
import com.github.javaparser.ast.stmt.WhileStmt;
import com.github.javaparser.ast.type.VoidType;
import tfm.graphs.augmented.ACFG;
import tfm.graphs.augmented.APDG;
import tfm.graphs.augmented.PPDG;
import tfm.nodes.GraphNode;
import tfm.nodes.TypeNodeFactory;
import tfm.nodes.type.NodeType;

public class HandCraftedGraphs {
    public static APDG problem1WithGotos() {
        // Generate the control flow of a graph
        ACFG cfg = new ACFG();
        cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), TypeNodeFactory.fromType(NodeType.METHOD_ENTER));
        GraphNode<?> wx = cfg.addNode("while (X)", new WhileStmt());
        GraphNode<?> ify = cfg.addNode("L: if (Y)", new IfStmt());
        GraphNode<?> ifz = cfg.addNode("if (Z)", new IfStmt());
        GraphNode<?> a = cfg.addNode("A();", new MethodCallExpr("A"));
        GraphNode<?> b = cfg.addNode("B();", new MethodCallExpr("B"));
        GraphNode<?> c = cfg.addNode("C();", new MethodCallExpr("C"));
        GraphNode<?> d = cfg.addNode("D();", new MethodCallExpr("D"));
        GraphNode<?> g1 = cfg.addNode("goto L;", new ContinueStmt("L"));
        GraphNode<?> g2 = cfg.addNode("goto L;", new ContinueStmt("L"));

        GraphNode<?> end = cfg.addNode("Exit", new EmptyStmt());

        cfg.addControlFlowEdge(cfg.getRootNode().get(), wx);
        cfg.addControlFlowEdge(wx, ify);
        cfg.addControlFlowEdge(wx, d);
        cfg.addControlFlowEdge(ify, ifz);
        cfg.addControlFlowEdge(ify, c);
        cfg.addControlFlowEdge(ifz, a);
        cfg.addControlFlowEdge(ifz, b);
        cfg.addControlFlowEdge(a, g1);
        cfg.addControlFlowEdge(b, g2);
        cfg.addControlFlowEdge(c, wx);
        cfg.addControlFlowEdge(d, end);
        cfg.addNonExecutableControlFlowEdge(g1, b);
        cfg.addControlFlowEdge(g1, ify);
        cfg.addNonExecutableControlFlowEdge(g2, c);
        cfg.addControlFlowEdge(g2, ify);
        cfg.addNonExecutableControlFlowEdge(cfg.getRootNode().get(), end);

        PPDG pdg = new PPDG(cfg);
        ControlDependencyBuilder gen = new ControlDependencyBuilder(cfg, pdg);
        gen.build();
        return pdg;
    }

    public static APDG problem1ContinueWithGotos() {
        // Generate the control flow of a graph
        ACFG cfg = new ACFG();
        cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), TypeNodeFactory.fromType(NodeType.METHOD_ENTER));
        GraphNode<?> wx = cfg.addNode("while (X)", new WhileStmt());
        GraphNode<?> ify = cfg.addNode("L: if (Y)", new IfStmt());
        GraphNode<?> ifz = cfg.addNode("if (Z)", new IfStmt());
        GraphNode<?> a = cfg.addNode("A();", new MethodCallExpr("A"));
        GraphNode<?> b = cfg.addNode("B();", new MethodCallExpr("B"));
        GraphNode<?> c = cfg.addNode("C();", new MethodCallExpr("C"));
        GraphNode<?> d = cfg.addNode("D();", new MethodCallExpr("D"));
        GraphNode<?> g1 = cfg.addNode("goto L1;", new ContinueStmt("L"));
        GraphNode<?> g2 = cfg.addNode("goto L2;", new ContinueStmt("L"));
        GraphNode<?> g3 = cfg.addNode("goto L3;", new ContinueStmt("L"));

        GraphNode<?> end = cfg.addNode("Exit", new EmptyStmt());

        cfg.addControlFlowEdge(cfg.getRootNode().get(), wx);
        cfg.addControlFlowEdge(wx, ify);
        cfg.addControlFlowEdge(wx, d);
        cfg.addControlFlowEdge(ify, ifz);
        cfg.addControlFlowEdge(ify, c);
        cfg.addControlFlowEdge(ifz, a);
        cfg.addControlFlowEdge(ifz, b);
        cfg.addControlFlowEdge(a, g1);
        cfg.addControlFlowEdge(b, g3);
        cfg.addControlFlowEdge(c, wx);
        cfg.addControlFlowEdge(d, end);
        cfg.addNonExecutableControlFlowEdge(g1, b);
        cfg.addControlFlowEdge(g1, ify);
        cfg.addNonExecutableControlFlowEdge(g2, c);
        cfg.addControlFlowEdge(g2, ify);
        cfg.addNonExecutableControlFlowEdge(g3, g2);
        cfg.addControlFlowEdge(g3, ify);
        cfg.addNonExecutableControlFlowEdge(cfg.getRootNode().get(), end);

        PPDG pdg = new PPDG(cfg);
        ControlDependencyBuilder gen = new ControlDependencyBuilder(cfg, pdg);
        gen.build();
        return pdg;
    }
}
+0 −187
Original line number Diff line number Diff line
package tfm.graphs.pdg;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.stmt.ThrowStmt;
import com.github.javaparser.ast.stmt.TryStmt;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import tfm.graphs.augmented.ACFG;
import tfm.graphs.augmented.APDG;
import tfm.graphs.augmented.PPDG;
import tfm.graphs.cfg.CFG;
import tfm.nodes.GraphNode;
import tfm.nodes.TypeNodeFactory;
import tfm.nodes.type.NodeType;
import tfm.slicing.GraphNodeCriterion;
import tfm.slicing.Slice;
import tfm.slicing.SlicingCriterion;
import tfm.utils.Logger;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

public class PDGTests {
    static {
        JavaParser.getStaticConfiguration().setAttributeComments(false);
    }

    private boolean error = false;

    @ParameterizedTest(name = "[{index}] {0} ({1})")
    @MethodSource("tfm.utils.FileFinder#findAllMethodDeclarations")
    public void ppdgTest(File file, String methodName, MethodDeclaration root) throws IOException {
        runPdg(file, methodName, root, new PPDG());
    }

    @ParameterizedTest(name = "[{index}] {0} ({1})")
    @MethodSource("tfm.utils.FileFinder#findAllMethodDeclarations")
    public void apdgTest(File file, String methodName, MethodDeclaration root) throws IOException {
        runPdg(file, methodName, root, new APDG());
    }

    @ParameterizedTest(name = "[{index}] {0} ({1})")
    @MethodSource("tfm.utils.FileFinder#findAllMethodDeclarations")
    public void pdgTest(File file, String methodName, MethodDeclaration root) throws IOException {
        runPdg(file, methodName, root, new PDG());
    }

    private void runPdg(File file, String methodName, MethodDeclaration root, PDG pdg) throws IOException {
        pdg.build(root);
//        GraphLog<?> graphLog = new PDGLog(pdg);
//        graphLog.log();
//        try {
//            graphLog.generateImages(file.getPath() + "-" + methodName);
//        } catch (Exception e) {
//            System.err.println("Could not generate PNG");
//            System.err.println(e.getMessage());
//        }
    }

    @ParameterizedTest(name = "[{index}] {0} ({1})")
    @MethodSource("tfm.utils.FileFinder#findAllMethodDeclarations")
    public void pdgCompare(File file, String methodName, MethodDeclaration root) {
        ControlDependencyBuilder ctrlDepBuilder;

        if (containsUnsupportedStatements(root)) {
            System.err.println("Contains unsupported instructions");
        }

        // Create PDG
        CFG cfg = new CFG();
        cfg.build(root);
        PDG pdg = new PDG(cfg);
        pdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD_ENTER));
        ctrlDepBuilder = new ControlDependencyBuilder(cfg, pdg);
        ctrlDepBuilder.build();

        // Create APDG
        ACFG acfg = new ACFG();
        acfg.build(root);
        APDG apdg = new APDG(acfg);
        apdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD_ENTER));
        ctrlDepBuilder = new ControlDependencyBuilder(acfg, apdg);
        ctrlDepBuilder.build();

        // Create PPDG
        PPDG ppdg = new PPDG(acfg);
        ppdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD_ENTER));
        ctrlDepBuilder = new ControlDependencyBuilder(acfg, ppdg);
        ctrlDepBuilder.build();

        // Print graphs (commented to decrease the test's time)
        String filePathNoExt = file.getPath().substring(0, file.getPath().lastIndexOf('.'));
//        String name = filePathNoExt + "/" + methodName;
//        new PDGLog(pdg).generateImages(name);
//        new PDGLog(apdg).generateImages(name);
//        new PDGLog(ppdg).generateImages(name);

        // Compare
        List<MethodDeclaration> slicedMethods = compareGraphs(pdg, apdg, ppdg);

        // Write sliced methods to a java file.
        ClassOrInterfaceDeclaration clazz = new ClassOrInterfaceDeclaration();
        slicedMethods.forEach(clazz::addMember);
        clazz.setName(methodName);
        clazz.setModifier(Modifier.Keyword.PUBLIC, true);
        try (PrintWriter pw = new PrintWriter(new File("./out/" + filePathNoExt + "/" + methodName + ".java"))) {
            pw.println(clazz);
        } catch (Exception e) {
            Logger.log("Error! Could not write classes to file");
        }

        assert !error;
    }

    public static boolean containsUnsupportedStatements(Node node) {
        return node.findFirst(TryStmt.class).isPresent()
                || node.findFirst(ThrowStmt.class).isPresent();
    }


    /** Slices both graphs on every possible node and compares the result */
    public List<MethodDeclaration> compareGraphs(PDG... pdgs) {
        List<MethodDeclaration> slicedMethods = new LinkedList<>();
        assert pdgs.length > 0;
        for (GraphNode<?> node : pdgs[0].vertexSet().stream()
                .sorted(Comparator.comparingLong(GraphNode::getId))
                .collect(Collectors.toList())) {
            // Skip start of graph
            if (node.getAstNode() instanceof MethodDeclaration)
                continue;

            // Perform slices
            SlicingCriterion sc = new GraphNodeCriterion(node, "x");
//            Slice[] slices = Arrays.stream(pdgs).map(p -> p.slice(sc)).toArray(Slice[]::new);

            // Compare slices
            boolean ok = true;
//            Slice referenceSlice = slices[0];
//            for (Slice slice : slices) {
//                ok = referenceSlice.equals(slice);
//                error |= !ok;
//                if (!ok) break;
//            }

            // Display slice
            Logger.log("Slicing on " + node.getId());
            if (!ok)
                Logger.log("FAILED!");
//            printSlices(pdgs[0], slices);

            // Save slices as MethodDeclaration
            int i = 0;
//            for (Slice s : slices) {
//                i++;
//                try {
//                    MethodDeclaration m = ((MethodDeclaration) s.getAst());
//                    m.setName(m.getName() + "_slice" + node.getId() + "_pdg" + i);
//                    slicedMethods.add(m);
//                } catch (RuntimeException e) {
//                    Logger.log("Error: " + e.getMessage());
//                }
//            }
        }
        return slicedMethods;
    }

    public final void printSlices(PDG pdg, Slice... slices) {
        pdg.vertexSet().stream()
                .sorted(Comparator.comparingLong(GraphNode::getId))
                .forEach(n -> Logger.format("%3d: %s %s",
                        n.getId(),
                        Arrays.stream(slices)
                                .map(s -> s.contains(n) ? "x" : " ")
                                .reduce((a, b) -> a + " " + b).orElse("--error--"),
                        n.getInstruction()));
    }
}
Loading