package fr.inria.diversify.diversification; import fr.inria.diversify.bytecode.CompareBytecode; import fr.inria.diversify.persistence.json.output.JsonTransformationWriter; import fr.inria.diversify.sosie.logger.Instru; import fr.inria.diversify.statistic.AbstractSessionResults; import fr.inria.diversify.buildSystem.AbstractBuilder; import fr.inria.diversify.transformation.SingleTransformation; import fr.inria.diversify.transformation.Transformation; import fr.inria.diversify.transformation.TransformationsWriter; import fr.inria.diversify.transformation.query.TransformationQuery; import fr.inria.diversify.util.GitUtils; import fr.inria.diversify.util.Log; import org.apache.commons.io.FileUtils; import org.eclipse.jgit.api.errors.GitAPIException; import org.json.JSONException; import spoon.reflect.declaration.CtType; import java.io.*; import java.util.List; /** * User: Simon * Date: 9/2/13 * Time: 3:05 PM */ public abstract class AbstractDiversify { /** * Number of trials performed */ protected int trial = 0; /** * Session report */ protected AbstractSessionResults sessionResults; /** * Input configuration */ protected InputConfiguration inputConfiguration; /** * The original temporal directory. This is a patch. Sometimes we cannot delete the tmpDir */ protected String originalTmpDir; /** * Directory project */ protected String projectDir; /** * Temporal directory where the diversificated programs are going to be temporary stored */ protected String tmpDir; /** * Source directory of the project */ protected String sourceDir; /** * Directory to store result information, i.e number of trials, number of sosies, transformations, etc. */ private String resultDir; /** * Directory to copy sosies programs source code to. */ private String sosieSourcesDir = null; private boolean android = false; /** * The original temporal directory. This is a patch. Sometimes we cannot delete the tmpDir */ public String getOriginalTmpDir() { return originalTmpDir; } public void setOriginalTmpDir(String originalTmpDir) { this.originalTmpDir = originalTmpDir; } public String getTmpDir() { return tmpDir; } public void setTransformationQuery(TransformationQuery transQuery) { this.transQuery = transQuery; } public List<Transformation> getTransformations() { return transformations; } public AbstractBuilder getBuilder() { return builder; } public void setBuilder(AbstractBuilder builder) { this.builder = builder; } /** * Result directory */ public String getResultDir() { return resultDir; } public void setResultDir(String resultDir) { this.resultDir = resultDir; } /** * Directory to copy sosies programs source code to. */ public String getSosieSourcesDir() { return sosieSourcesDir; } public void setSosieSourcesDir(String sosieSourcesDir) { this.sosieSourcesDir = sosieSourcesDir; } protected List<Transformation> transformations; /** * Query to find transformations */ protected TransformationQuery transQuery; /** * Build system to perform the locate lifecycle */ protected AbstractBuilder builder; /** * Runs the diversificator. * @param n Number of times the diversification process will run, i.e trials * @throws Exception */ public abstract void run(int n) throws Exception; protected boolean compareByteCode(Transformation transformation) throws Exception { if(transformation instanceof SingleTransformation) { SingleTransformation singleTransformation = (SingleTransformation) transformation; CtType mainType = singleTransformation.getPosition().getCompilationUnit().getMainType(); InputProgram inputProgram = inputConfiguration.getInputProgram(); String originalClass = inputProgram.getProgramDir() + "/" + inputProgram.getClassesDir() + "/" + mainType.getQualifiedName().replace(".", "/") + ".class"; String sosieClass = tmpDir + "/" + inputProgram.getClassesDir() + "/" + mainType.getQualifiedName().replace(".", "/") + ".class"; CompareBytecode compareBytecode = new CompareBytecode(originalClass, sosieClass); return compareBytecode.equals(singleTransformation.methodLocationName()); } return true; } /** * * @param output * @return */ public String printResult(String output) { Log.info("session result: {}", sessionResults); mkDirResult(output); String prefix = output + System.currentTimeMillis(); String fileName = ""; try { fileName = writeTransformations(prefix); Log.info("write result in {}", fileName); } catch (Exception e) { Log.error("error in Builder.printResult", e); } return fileName; } public void printResultInGitRepo(String output, String git) { String absoluteFileName = printResult(git + "/" + output); String fileName = absoluteFileName.substring(git.length() + 1, absoluteFileName.length()); Log.info("add dir {} in git", fileName); try { GitUtils gitUtils = new GitUtils(git); gitUtils.pull(); gitUtils.add(fileName); gitUtils.commit("update"); gitUtils.push(); } catch (IOException e) { e.printStackTrace(); } catch (GitAPIException e) { e.printStackTrace(); } } /** * Write found transformations to file. * * TODO: A sugestion, we should move this to AbstractQuery * * @param fileName File name where the transformations are going to be stored. * @throws IOException * @throws JSONException */ public String writeTransformations(String fileName) throws IOException, JSONException { if (transformations.isEmpty()) return ""; JsonTransformationWriter writer = new JsonTransformationWriter(); writer.write(transformations, fileName + ".json", inputConfiguration.getInputProgram().getProgramDir() + "/pom.xml"); return fileName + ".json"; } protected void mkDirResult(String output) { String dirs = ""; if(output.endsWith("/")) { dirs = output; } else { String[] tmp = output.split("/"); for (int i = 0; i < tmp.length - 1; i++) { dirs = dirs + tmp[i] + "/"; } } new File(dirs).mkdirs(); Log.debug("mkdir: {}", dirs); } /** * Initializes de temporal directory to copy the sources of the sosiefied program * * @param dirProject Directory where the original program is. Is going to be copy to dirTarget * @param dirTarget Directory where the diversified program is going to be after the transformation are run. * Transformations are applied to the source in dirTarget * @return * @throws IOException * @throws InterruptedException */ public String init(String dirProject, String dirTarget) throws IOException, InterruptedException { originalTmpDir = dirTarget; tmpDir = dirTarget + "/tmp_" + System.currentTimeMillis(); File dir = new File(tmpDir); dir.mkdirs(); FileUtils.copyDirectory(new File(dirProject), dir); return tmpDir; } /** * Delete the temporal files that we have created */ public void deleteTmpFiles() { try { FileUtils.cleanDirectory(new File(tmpDir)); FileUtils.forceDelete(new File(tmpDir)); } catch (IOException e) { try { init(projectDir, originalTmpDir); } catch (Exception e1) { throw new RuntimeException(e1); } } } protected Integer runTest(String directory) throws InterruptedException { int status; Log.debug("run test in directory: {}", directory); builder.setDirectory(directory); builder.runBuilder(); Log.info("status: " + builder.getStatus() + ", compile error: " + builder.getCompileError() + ", run all test: " + builder.allTestRun() + ", nb error: " + builder.getTestFail().size()); status = builder.getStatus(); return status; } protected String copySosieProgram() throws IOException, JSONException { //Store the whole sosie program. try { if (getSosieSourcesDir() != null && getSosieSourcesDir().length() > 0) { File f = new File(getSosieSourcesDir()); if (!(f.exists())) { f.mkdirs(); } String destPath = getSosieDestinationPath(); boolean intruMethodCall = Boolean.parseBoolean(inputConfiguration.getProperty("intruMethodCall","false")); boolean intruVariable = Boolean.parseBoolean(inputConfiguration.getProperty("intruVariable","false")); boolean intruError = Boolean.parseBoolean(inputConfiguration.getProperty("intruError","false")); boolean intruNewTest = Boolean.parseBoolean(inputConfiguration.getProperty("intruNewTest","false")); int javaVersion = Integer.parseInt(inputConfiguration.getProperty("javaVersion")); if (intruMethodCall || intruVariable || intruError || intruNewTest) { Instru instru = new Instru(tmpDir, sourceDir, inputConfiguration.getProperty("testSrc"), javaVersion, destPath, transformations); instru.setMethodCall(intruMethodCall); instru.setVariable(intruVariable); instru.setError(intruError); instru.setNewTest(intruNewTest); instru.instru(); } else { File dest = new File(destPath); if (!(dest.exists())) { dest.mkdirs(); } FileUtils.copyDirectory(new File(tmpDir), dest); } FileWriter writer = new FileWriter(destPath + "/trans.json"); for (Transformation t : transformations) { writer.write(t.toJSONObject().toString() + "\n"); } writer.close(); return destPath; } return null; } catch (IOException e) { //We may also don't want to recover from here. If no instrumentation possible... now what? throw new RuntimeException(e); } } protected String getSosieDestinationPath() { return getSosieSourcesDir() + "/" + sessionResults.getBeginTime() + "_trial_" + trial; } protected void tryRestore(Transformation trans, Exception e) throws Exception { try { trans.restore(tmpDir + "/" + sourceDir); } catch (Exception restore) { e.printStackTrace(); Log.debug(""); } // try { // trans.printJavaFile(tmpDir + "/" + sourceDir); // } catch (Exception print) {} int status = runTest(tmpDir); if (status != 0) { throw new Exception(e); } } public void setAndroid(boolean android) { this.android = android; } }