package fr.inria.diversify.info;
import fr.inria.diversify.Profiling;
import fr.inria.diversify.buildSystem.android.InvalidSdkException;
import fr.inria.diversify.buildSystem.maven.MavenBuilder;
import fr.inria.diversify.codeFragment.CodeFragment;
import fr.inria.diversify.diversification.InputConfiguration;
import fr.inria.diversify.diversification.InputProgram;
import fr.inria.diversify.logger.Diff;
import fr.inria.diversify.logger.JsonDiffInput;
import fr.inria.diversify.logger.JsonDiffOutput;
import fr.inria.diversify.logger.branch.*;
import fr.inria.diversify.logger.exception.ExceptionDiff;
import fr.inria.diversify.logger.graph.GraphsDiff;
import fr.inria.diversify.logger.transformationUsed.StaticDiff;
import fr.inria.diversify.logger.variable.VariableDiff;
import fr.inria.diversify.persistence.json.input.JsonTransformationLoader;
import fr.inria.diversify.persistence.json.output.JsonTransformationWriter;
import fr.inria.diversify.processor.main.BranchPositionProcessor;
import fr.inria.diversify.processor.test.CountProcessor;
import fr.inria.diversify.transformation.Transformation;
import fr.inria.diversify.transformation.ast.ASTAdd;
import fr.inria.diversify.transformation.ast.ASTReplace;
import fr.inria.diversify.transformation.ast.ASTTransformation;
import fr.inria.diversify.util.InitUtils;
import fr.inria.diversify.util.Log;
import fr.inria.diversify.util.LoggerUtils;
import org.apache.commons.io.FileUtils;
import spoon.reflect.cu.SourcePosition;
import spoon.reflect.declaration.*;
import spoon.reflect.factory.Factory;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
/**
* User: Simon
* Date: 07/07/15
* Time: 10:33
*/
public class StatementsInfo {
protected final InputConfiguration inputConfiguration;
protected final InputProgram inputProgram;
protected Map<Transformation, Set<Diff>> transToDiffs;
protected Collection<Transformation> transformations;
protected Map<String, SourcePosition> branchPosition;
protected Map<String, Set<String>> testsByBranch;
protected Coverage globalCoverage;
protected Map<String, Integer> assertPerTest;
protected StaticDiff staticDiff;
protected Set<String> transformationWithStaticDiff;
public StatementsInfo(String propertiesFile) throws Exception, InvalidSdkException {
inputConfiguration = new InputConfiguration(propertiesFile);
InitUtils.initLogLevel(inputConfiguration);
InitUtils.initDependency(inputConfiguration);
inputProgram = InitUtils.initInputProgram(inputConfiguration);
InitUtils.initSpoon(inputProgram, false);
String transDir = inputConfiguration.getProperty("transformation.directory");
String out = inputConfiguration.getProperty("result");
init();
if(transDir != null) {
computeDiversifyStat(transDir, out);
staticDiff = new StaticDiff(branchPosition, testsByBranch);
transformationWithStaticDiff = new HashSet<>();
writeCSV(out + ".csv");
}
writeCSVStatement(out + "_stmt.csv");
}
protected void computeDiversifyStat(String transDir, String output) throws Exception {
JsonTransformationLoader loader = new JsonTransformationLoader(inputProgram);
JsonDiffInput jsonDiffInput = new JsonDiffInput();
loader.addSection(jsonDiffInput.getClass(), jsonDiffInput);
transformations = loader.load(transDir, true).stream()
.collect(Collectors.toList());
transToDiffs = jsonDiffInput.getTransToDiffs();
JsonTransformationWriter writer = new JsonTransformationWriter();
writer.addSection(JsonDiffOutput.class, new JsonDiffOutput(transToDiffs));
Set<Transformation> sosies = transformations.stream()
.filter(t -> t.getStatus() >= 0)
.collect(Collectors.toSet());
writer.write(sosies, output + "_sosie.json", inputProgram.getProgramDir() + "/pom.xml");
}
protected void writeCSV(String out) throws IOException {
FileWriter writer = new FileWriter(new File(out));
writer.append("uuid;type;name;position;status;diff;graphDiff;branchDiff;variableDiff;exceptionDiff;nbTest;maxDeep;meanDeep;medianDeep;minDeep;nodeTransplantationPoint;nodeTransplant\n");
for(Transformation transformation: transformations) {
writer.append(transformation.getIndex() + ";");
writer.append(transformation.getType() + ";");
writer.append(transformation.getName() + ";");
writer.append(formatPosition(transformation.getPosition())+";");
writer.append(transformation.getStatus() + ";");
if(transToDiffs.containsKey(transformation)) {
writer.append(transToDiffs.get(transformation).size() + ";");
writer.append(transToDiffs.get(transformation).stream()
.filter(d -> d instanceof GraphsDiff)
.mapToInt(d -> d.size())
.sum()+ ";");
writer.append(transToDiffs.get(transformation).stream()
.filter(d -> d instanceof BranchDiff)
.mapToInt(d -> d.size())
.sum()+ ";");
writer.append(transToDiffs.get(transformation).stream()
.filter(d -> d instanceof VariableDiff)
.mapToInt(d -> d.size())
.sum()+ ";");
writer.append(transToDiffs.get(transformation).stream()
.filter(d -> d instanceof ExceptionDiff)
.mapToInt(d -> d.size())
.sum()+ ";");
} else {
writer.append("0;0;0;0;0;");
}
writer.append(coveredTests(transformation.getPosition()).size() + ";");
writer.append(deepMax(transformation.getPosition()) + ";");
writer.append(deepMean(transformation.getPosition()) + ";");
writer.append(deepMedian(transformation.getPosition()) + ";");
writer.append(deepMin(transformation.getPosition()) + ";");
writer.append(nodeTransplantationPoint(transformation) + ";");
writer.append(nodeTransplant(transformation) + "\n");
}
writer.close();
}
protected String nodeTransplantationPoint(Transformation trans) {
return ((ASTTransformation) trans).getTransplantationPoint().getCodeFragmentTypeSimpleName();
}
protected String nodeTransplant(Transformation trans) {
if(trans instanceof ASTReplace) {
return ((ASTReplace) trans).getTransplant().getCodeFragmentTypeSimpleName();
}
if(trans instanceof ASTAdd) {
return ((ASTAdd) trans).getTransplant().getCodeFragmentTypeSimpleName();
}
return "NA";
}
protected void writeCSVStatement(String out) throws IOException {
StatementInfo stmtInfo = new StatementInfo(inputProgram);
FileWriter writer = new FileWriter(new File(out));
InitUtils.initSpoon(inputProgram, false);
writer.append("position;isCandidate;isCandidateDelete;nbTrial;nbCompile;nbSosie;nbTest;nbAssert;maxDeep;meanDeep;medianDeep;minDeep\n");
for(CodeFragment stmt: inputProgram.getCodeFragments()) {
SourcePosition position = stmt.getCtCodeFragment().getPosition();
List<Transformation> transInThisStmt = transformations.stream()
.filter(trans -> include(position, trans.getPosition()))
.collect(Collectors.toList());
writer.append( stmt.positionString() + ";");
writer.append(stmtInfo.isTransformable(stmt) + ";");
writer.append(stmtInfo.delete(stmt) + ";");
writer.append(transInThisStmt.size() + ";");
writer.append(transInThisStmt.stream()
.filter(trans -> trans.getStatus() >= -1)
.count() + ";");
writer.append(transInThisStmt.stream()
.filter(trans -> trans.getStatus() >= 0)
.count() + ";");
Collection<String> coveredTests = coveredTests(position);
writer.append(coveredTests(position).size() + ";");
writer.append(nbAssertFor(coveredTests) + ";");
writer.append(deepMax(position) + ";");
writer.append(deepMean(position) + ";");
writer.append(deepMedian(position) + ";");
writer.append(deepMin(position) + "\n");
}
writer.close();
}
public void init() throws Exception {
Log.debug("init BranchComparator");
String tmpDir = inputConfiguration.getProperty("tmpDir") + "/tmp_" + System.currentTimeMillis();
copyDir(inputProgram.getProgramDir(), tmpDir);
instru(tmpDir);
MavenBuilder builder = new MavenBuilder(tmpDir);
builder.runGoals(new String[]{"clean", "test"}, true);
initTestByBranch(builder.getDirectory() + "/log");
intBranch();
globalCoverage = loadGlobalCoverage(builder.getDirectory() + "/log");
initAssertCount();
transformations = new LinkedList<>();
}
protected String formatPosition(SourcePosition position) {
return position.getCompilationUnit().getMainType().getQualifiedName() + ":" + position.getLine();
}
protected void copyDir(String src, String dest) throws IOException {
File dir = new File(dest);
if(dir.exists()) {
FileUtils.forceDelete(dir);
}
dir.mkdirs();
FileUtils.copyDirectory(new File(src), dir);
}
protected void intBranch() {
BranchPositionProcessor processor = new BranchPositionProcessor(inputProgram);
LoggerUtils.applyProcessor(inputProgram.getFactory(), processor);
branchPosition = processor.getBranchPosition();
}
protected List<TestCoverage> loadTestCoverage(String logDir) throws IOException {
CoverageReader reader = new CoverageReader(logDir);
List<TestCoverage> result = reader.loadTest();
return result;
}
protected Coverage loadGlobalCoverage(String logDir) throws IOException {
CoverageReader reader = new CoverageReader(logDir);
return reader.load();
}
protected void instru(String outputDirectory) throws Exception {
Properties properties = new Properties();
properties.put("profiling.main.branch", "true");
properties.put("profiling.main.branch.addBodyBranch", "true");
properties.put("profiling.test.logTest", "true");
Profiling profiling = new Profiling(inputProgram, outputDirectory, "fr.inria.diversify.logger.logger", properties);
profiling.apply();
}
protected void initTestByBranch(String logDir) throws InterruptedException, IOException {
testsByBranch = new HashMap<>();
List<TestCoverage> testCoverage = loadTestCoverage(logDir);
for(TestCoverage tc : testCoverage) {
for(MethodCoverage mth : tc.getCoverage().getMethodCoverages()) {
for(Branch branch : mth.getCoveredBranchs()) {
String key = mth.getMethodId() + "." + branch.getId();
if (!testsByBranch.containsKey(key)) {
testsByBranch.put(key, new HashSet<>());
}
String testName = tc.getTestName();
int ind = testName.lastIndexOf(".");
testName = new StringBuilder(testName).replace(ind, ind + 1, "#").toString();
testsByBranch.get(key).add(testName);
}
}
}
}
protected void initAssertCount() {
Factory factory = InitUtils.initSpoon(inputProgram, true);
CountProcessor m = new CountProcessor();
LoggerUtils.applyProcessor(factory, m);
assertPerTest = m.getAssertPerTest();
}
protected String smallBranchContaining(SourcePosition sourcePosition) {
List<String> branches = branchPosition.keySet().stream()
.filter(branch -> include(branchPosition.get(branch), sourcePosition))
.collect(Collectors.toList());
if(branches.isEmpty()) {
return "";
}
int minBranchSize = 10000;
String minBranch = "";
for (String branch : branches) {
int size = branchPosition.get(branch).getEndLine() - branchPosition.get(branch).getLine();
if(size < minBranchSize) {
minBranchSize = size;
minBranch = branch;
}
}
return minBranch;
}
protected int deepMax(SourcePosition sourcePosition) {
String branchName = smallBranchContaining(sourcePosition);
if(branchName.isEmpty()) {
return 0;
}
Branch branch = globalCoverage.getBranch(branchName);
if(branch == null) {
return 0;
}
return branch.getDeeps().stream()
.mapToInt(i -> i)
.max()
.orElse(0);
}
protected double deepMean(SourcePosition sourcePosition) {
String branchName = smallBranchContaining(sourcePosition);
if(branchName.isEmpty()) {
return 0;
}
Branch branch = globalCoverage.getBranch(branchName);
if(branch == null) {
return 0;
}
return branch.getDeeps().stream()
.mapToDouble(i -> i)
.sum() / branch.getDeeps().size();
}
protected double deepMin(SourcePosition sourcePosition) {
String branchName = smallBranchContaining(sourcePosition);
if(branchName.isEmpty()) {
return 0;
}
Branch branch = globalCoverage.getBranch(branchName);
if(branch == null) {
return 0;
}
return branch.getDeeps().stream()
.mapToInt(i -> i)
.min()
.orElse(0);
}
protected double deepMedian(SourcePosition sourcePosition) {
String branchName = smallBranchContaining(sourcePosition);
if(branchName.isEmpty()) {
return 0;
}
Branch branch = globalCoverage.getBranch(branchName);
if(branch == null) {
return 0;
}
List<Integer> sorted = branch.getDeeps().stream()
.mapToInt(i -> i)
.sorted()
.boxed()
.collect(Collectors.toList());
return sorted.get(sorted.size()/2);
}
protected int nbAssertFor(Collection<String> tests) {
return tests.stream()
.filter(test -> assertPerTest.containsKey(test))
.mapToInt(test -> assertPerTest.get(test))
.sum();
}
protected Collection<String> coveredTests(SourcePosition sourcePosition) {
String branch = smallBranchContaining(sourcePosition);
if(testsByBranch.containsKey(branch)) {
return testsByBranch.get(branch);
} else {
return new LinkedList<>();
}
}
protected double methodNameContainedInTests(CtElement element, Collection<String> tests) {
CtExecutable exe = getMethodOrConstructorContaining(element);
if(exe == null) {
return 0;
} else {
String name = exe.getSimpleName().toLowerCase();
return tests.stream()
.map(test -> test.split("#")[1].toLowerCase())
.filter(test -> test.contains(name))
.count() / (double)tests.size();
}
}
protected double methodClassTargetByTests(CtElement element, Collection<String> tests) {
CtExecutable exe = getMethodOrConstructorContaining(element);
if(exe == null) {
return 0;
} else {
String className = exe.getReference().getDeclaringType().getQualifiedName();
return tests.stream()
.map(test -> test.split("#")[0])
.map(test -> test.substring(0, test.length() - 4))
.filter(testClass -> getSuperClasses(testClass).contains(className))
.count() / (double)tests.size();
}
}
protected Collection<String> getSuperClasses(String className) {
Collection<String> classes = new HashSet<>();
classes.add(className);
try {
Class cl = Class.forName(className);
while (cl.getSuperclass() != null) {
cl = cl.getSuperclass();
classes.add(cl.getCanonicalName());
}
} catch (Exception e) {}
return classes;
}
protected CtExecutable getMethodOrConstructorContaining(CtElement element) {
CtExecutable exe = element.getParent(CtMethod.class);
if(exe == null) {
exe = element.getParent(CtConstructor.class);
}
return exe;
}
//true if oThis include in oOther
protected boolean include(SourcePosition oThis, SourcePosition oOther) {
return oThis.getCompilationUnit().getMainType().getQualifiedName().equals(oOther.getCompilationUnit().getMainType().getQualifiedName())
&& oThis.getLine() <= oOther.getLine()
&& oThis.getEndLine() >= oOther.getEndLine();
}
public static void main(String args[]) throws InvalidSdkException, Exception {
new StatementsInfo(args[0]);
}
}