diff --git a/pom.xml b/pom.xml index 0ec6495..a780077 100644 --- a/pom.xml +++ b/pom.xml @@ -85,13 +85,16 @@ idflakies 1.1.0 - + + org.xmlunit + xmlunit-core + 2.8.2 + org.apache.maven.shared maven-invoker 3.0.1 - com.reedoei eunomia diff --git a/src/main/java/edu/illinois/cs/dt/tools/utility/iFixPlusPlugin.java b/src/main/java/edu/illinois/cs/dt/tools/utility/iFixPlusPlugin.java new file mode 100644 index 0000000..761661a --- /dev/null +++ b/src/main/java/edu/illinois/cs/dt/tools/utility/iFixPlusPlugin.java @@ -0,0 +1,769 @@ +package edu.illinois.cs.dt.tools.utility; + +import com.google.gson.Gson; +import com.reedoei.eunomia.io.files.FileUtil; +import edu.illinois.cs.dt.tools.minimizer.MinimizeTestsResult; +import edu.illinois.cs.dt.tools.minimizer.PolluterData; +import edu.illinois.cs.dt.tools.runner.InstrumentingSmartRunner; +import edu.illinois.cs.dt.tools.runner.data.DependentTest; +import edu.illinois.cs.dt.tools.runner.data.DependentTestList; +import edu.illinois.cs.testrunner.configuration.Configuration; +import edu.illinois.cs.testrunner.data.results.TestRunResult; +import edu.illinois.cs.testrunner.mavenplugin.TestPlugin; +import edu.illinois.cs.testrunner.mavenplugin.TestPluginPlugin; +import edu.illinois.cs.testrunner.runner.Runner; +import edu.illinois.cs.testrunner.runner.RunnerFactory; +import org.apache.commons.io.FileUtils; +import org.apache.maven.project.MavenProject; +import org.w3c.dom.Node; +import scala.util.Try; + +import java.io.File; +import java.io.FileReader; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.Set; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashSet; + +import org.xmlunit.builder.DiffBuilder; +import org.xmlunit.diff.Comparison.Detail; +import org.xmlunit.diff.DefaultNodeMatcher; +import org.xmlunit.diff.Diff; +import org.xmlunit.diff.Difference; +import org.xmlunit.diff.ElementSelectors; + +public class iFixPlusPlugin extends TestPlugin { + private Path replayPath; + private Path replayPath2; + private String dtname; + private String xmlDir; + private String rootFile; + private String module; + private String output; + private String diffFieldsFile; + private String subdiffsFold; + private String reflectionFile; + private String eagerloadfile; + + private Set diffFields_filtered = new HashSet (); + + @Override + public void execute(final MavenProject mavenProject) { + Configuration.config().properties().setProperty("statecapture.phase", "initial"); + + long startTime = System.currentTimeMillis(); + + // Currently there could two runners, one for JUnit 4 and one for JUnit 5 + // If the maven project has both JUnit 4 and JUnit 5 tests, two runners will + // be returned + List runners = RunnerFactory.allFrom(mavenProject); + + if (runners.size() != 1) { + // HACK: Always force JUnit 4 + boolean forceJUnit4 = true; + if (forceJUnit4) { + Runner nrunner = null; + for (Runner runner : runners) { + if (runner.framework().toString() == "JUnit") { + nrunner = runner; + break; + } + } + if (nrunner != null) { + runners = new ArrayList<>(Arrays.asList(nrunner)); + } else { + String errorMsg; + if (runners.size() == 0) { + errorMsg = + "Module is not using a supported test framework (probably not JUnit), " + + "or there is no test."; + } else { + errorMsg = "dt.detector.forceJUnit4 is true but no JUnit 4 runners found. Perhaps the project only contains JUnit 5 tests."; + } + TestPluginPlugin.mojo().getLog().info(errorMsg); + return; + } + } else { + String errorMsg; + if (runners.size() == 0) { + errorMsg = + "Module is not using a supported test framework (probably not JUnit), " + + "or there is no test."; + } else { + // more than one runner, currently is not supported. + errorMsg = + "This project contains both JUnit 4 and JUnit 5 tests, which currently" + + " is not supported by iDFlakies"; + } + TestPluginPlugin.mojo().getLog().info(errorMsg); + return; + } + } + final Runner runner = InstrumentingSmartRunner.fromRunner(runners.get(0)); + + replayPath = Paths.get(Configuration.config().getProperty("replay.path")); + replayPath2 = Paths.get(Configuration.config().getProperty("replay.path2")); + dtname= Configuration.config().getProperty("statecapture.testname"); + output= Configuration.config().getProperty("replay.output"); + xmlDir = Configuration.config().getProperty("statecapture.xmlDir"); + rootFile = Configuration.config().getProperty("statecapture.rootFile"); + module = Configuration.config().getProperty("replay.module"); + diffFieldsFile = Configuration.config().getProperty("replay.diffFieldsFile"); + subdiffsFold = Configuration.config().getProperty("replay.subdiffsFold"); + reflectionFile = Configuration.config().getProperty("statecapture.reflectionFile"); + eagerloadfile = Configuration.config().getProperty("statecapture.eagerloadfile"); + + if (runner != null && module.equals(PathManager.modulePath().toString())) { + System.out.println("replyPath: " + replayPath); + System.out.println("module: " + module); + + try { + Files.write(Paths.get(output), + (lastPolluter() + ",").getBytes(), + StandardOpenOption.APPEND); + // phase 0 check json file + System.out.println("~~~~~~ Begin to check if this test is a true order-dependent test."); + Configuration.config().properties().setProperty("statecapture.phase", "check"); + if (testFailOrder()==null) { + timing(startTime); + Files.write(Paths.get(output), + "0,0,0,0,0,0,0,wrongjsonfail,".getBytes(), + StandardOpenOption.APPEND); + System.out.println("## Original json file is wrong!"); + return; + } + + if (testPassOrder_full()==null) { + timing(startTime); + Files.write(Paths.get(output), + "0,0,0,0,0,0,0,wrongjsonpass,".getBytes(), + StandardOpenOption.APPEND); + System.out.println("## Original json file is wrong!"); + return; + } + + for(int i=0; i<10; i++) { + Try phase0ResultFail = null; + try { + phase0ResultFail = runner.runList(testFailOrder()); + } + catch (Exception ex) { + System.out.println("## Encountering error when checking the failing order: " + ex); + } + + System.out.println("Failing order results: " + + phase0ResultFail.get().results().get(dtname).result().toString()); + + if (phase0ResultFail.get().results().get(dtname).result().toString().equals("PASS")) { + System.out.println("## Failing order json file is wrong!"); + timing(startTime); + Files.write(Paths.get(output), + "0,0,0,0,0,0,0,wrongjsonfail2,".getBytes(), + StandardOpenOption.APPEND); + return; + } + } + + for(int i=0; i<10; i++) { + Try phase0ResultPass = null; + try { + phase0ResultPass = runner.runList(testPassOrder_full()); + } + catch (Exception ex) { + System.out.println("## Encountering error when checking the passing order: " + ex); + } + System.out.println("Passing order results: " + + phase0ResultPass.get().results().get(dtname).result().toString()); + + if (!phase0ResultPass.get().results().get(dtname).result().toString().equals("PASS")) { + System.out.println("## Passing order json file is wrong!"); + timing(startTime); + Files.write(Paths.get(output), + "0,0,0,0,0,0,0,wrongjsonpass2,".getBytes(), + StandardOpenOption.APPEND); + return; + } + } + + timing(startTime); + startTime = System.currentTimeMillis(); + System.out.println("~~~~~~ Finish checking if this test is a true order-dependent test."); + + //phase 1: run doublevictim order + Try phase1Result = null; + try { + System.out.println("~~~~~~ Begin running double victim order to check if it is a double victim!"); + + Configuration.config().properties(). + setProperty("testplugin.runner.idempotent.num.runs", "2"); + phase1Result = runner.runList(victim()); + System.out.println(phase1Result.get().results().get(dtname+":1").result()); + Configuration.config().properties(). + setProperty("testplugin.runner.idempotent.num.runs", "-1"); + System.out.println("## Running double victim order results: " + phase1Result.get().results()); + } + catch (Exception e) { + System.out.println("## Encountering error when running in double victim order: " + e); + } + timing(startTime); + startTime = System.currentTimeMillis(); + System.out.println("~~~~~~ Finished running double victim order!"); + + if (phase1Result.get().results().get(dtname+":1").result().toString().equals("PASS")) { + System.out.println("~~~~~~ Begin capturing the state in passing order!"); + // phase 2: run doublevictim order state capture + Configuration.config().properties(). + setProperty("statecapture.phase", "capture_after"); + Configuration.config().properties(). + setProperty("statecapture.rootFile", rootFile.substring(0, rootFile.lastIndexOf("/")) + "/passing_order.txt"); + String allFieldsFold = Configuration.config().getProperty("statecapture.allFieldsFile").substring(0, + Configuration.config().getProperty("statecapture.allFieldsFile").lastIndexOf("/")); + Configuration.config().properties(). + setProperty("statecapture.allFieldsFile", allFieldsFold + "/passing_order.txt"); + Configuration.config().properties(). + setProperty("statecapture.xmlDir", xmlDir + "/passing_order_xml"); + System.out.println(Configuration.config().getProperty("statecapture.rootFile") + ";" + + Configuration.config().getProperty("statecapture.allFieldsFile")); + try { + runner.runList(victim()); + } + catch (Exception e) { + System.out.println("## Encountering error in capturing the state in passing order: " + e); + } + System.out.println("~~~~~~ Finished capturing the state in passing order!"); + timing(startTime); + startTime = System.currentTimeMillis(); + Files.write(Paths.get(output), + "0,".getBytes(), + StandardOpenOption.APPEND); + } + else { + System.out.println("~~~~~~ Begin loading the classes when the test is a double victim!"); + //phase 2tmp: run doublevictim order state capture + Configuration.config().properties(). + setProperty("statecapture.phase", "capture_after"); + Configuration.config().properties(). + setProperty("statecapture.eagerload", "true"); + Configuration.config().properties(). + setProperty("statecapture.eagerloadfile", eagerloadfile); + try { + runner.runList(victim()); + } + catch (Exception e) { + System.out.println("## Encounter error in loading the classes when the test is a double victim: " + e); + } + System.out.println("~~~~~~ Finish loading the classes when the test is a double victim!"); + System.out.println("~~~~~~ Begin capturing the state in passing order(double victim)!!!"); + Configuration.config().properties(). + setProperty("statecapture.eagerload", "false"); + // phase 3: run passorder (indicated in the json) state capture; + Configuration.config().properties(). + setProperty("statecapture.phase", "capture_before"); + Configuration.config().properties(). + setProperty("statecapture.rootFile", rootFile.substring(0, rootFile.lastIndexOf("/")) + "/passing_order.txt"); + String allFieldsFold = Configuration.config().getProperty("statecapture.allFieldsFile").substring(0, + Configuration.config().getProperty("statecapture.allFieldsFile").lastIndexOf("/")); + Configuration.config().properties(). + setProperty("statecapture.allFieldsFile", allFieldsFold + "/passing_order.txt"); + Configuration.config().properties(). + setProperty("statecapture.xmlDir", xmlDir + "/passing_order_xml"); + System.out.println(Configuration.config().getProperty("statecapture.rootFile") + ";" + + Configuration.config().getProperty("statecapture.allFieldsFile")); + try { + runner.runList(testPassOrder_full()); + } + catch (Exception e) { + System.out.println("## Encounter error in capturing the state in passing order(double victim)!"); + } + + Files.write(Paths.get(output), + "0,".getBytes(), + StandardOpenOption.APPEND); + timing(startTime); + startTime = System.currentTimeMillis(); + System.out.println("passOrder: " + testPassOrder_full()); + System.out.println("~~~~~~ Finish phase capturing the state in passing order(double victim)!!"); + } + + // phase 4: failing order before state capture; + System.out.println("~~~~~~ Begin capturing the state in failing order!!"); + Configuration.config().properties(). + setProperty("statecapture.phase", "capture_before"); + Configuration.config().properties(). + setProperty("statecapture.rootFile", rootFile.substring(0, rootFile.lastIndexOf("/")) + "/failing_order.txt"); + String allFieldsFold = Configuration.config().getProperty("statecapture.allFieldsFile").substring(0, + Configuration.config().getProperty("statecapture.allFieldsFile").lastIndexOf("/")); + Configuration.config().properties(). + setProperty("statecapture.allFieldsFile", allFieldsFold + "/failing_order.txt"); + Configuration.config().properties(). + setProperty("statecapture.xmlDir", xmlDir + "/failing_order_xml"); + System.out.println(Configuration.config().getProperty("statecapture.rootFile") + ";" + + Configuration.config().getProperty("statecapture.allFieldsFile")); + try { + runner.runList(testFailOrder()); + } + catch (Exception e) { + System.out.println("## Encounter error in capturing the state in failing order!! " + e); + + } + + timing(startTime); + startTime = System.currentTimeMillis(); + System.out.println("~~~~~~ Finish phase capturing the state in failing order!!"); + + File failingxmlDirer = new File(xmlDir + "/failing_order_xml"); + if (!failingxmlDirer.exists()) { + // phase 4: failing order after state capture; + Configuration.config().properties(). + setProperty("statecapture.phase", "capture_after"); + Configuration.config().properties(). + setProperty("statecapture.rootFile", rootFile.substring(0, rootFile.lastIndexOf("/")) + "/failing_order.txt"); + allFieldsFold = Configuration.config().getProperty("statecapture.allFieldsFile").substring(0, + Configuration.config().getProperty("statecapture.allFieldsFile").lastIndexOf("/")); + Configuration.config().properties(). + setProperty("statecapture.allFieldsFile", allFieldsFold + "/failing_order.txt"); + Configuration.config().properties(). + setProperty("statecapture.xmlDir", xmlDir + "/failing_order_xml"); + System.out.println(Configuration.config().getProperty("statecapture.rootFile") + ";" + + Configuration.config().getProperty("statecapture.allFieldsFile")); + System.out.println("~~~~~~ Begin capturing the state in failing order!!"); + try { + runner.runList(testFailOrder()); + } + catch (Exception e) { + System.out.println("## Encounter error in phase capturing the state in failing order!! " + e); + + } + timing(startTime); + startTime = System.currentTimeMillis(); + System.out.println("~~~~~~ Finish phase capturing the state in failing order!!"); + } else { + Files.write(Paths.get(output), + "0,".getBytes(), + StandardOpenOption.APPEND); + } + + // phase 5: do the diff + System.out.println("~~~~~~ Begin diffing between the passing order and failing order!!!"); + + File passingxmlDirer = new File(xmlDir + "/passing_order_xml"); + if (passingxmlDirer.exists() && failingxmlDirer.exists()) { + try { + diffing(); + } + catch (Exception e) { + System.out.println("## Encounter error in doing diffing: " + e); + } + } + else { + System.out.println("## Cannot do diff, xml files are not complete!"); + } + + timing(startTime); + startTime = System.currentTimeMillis(); + System.out.println("~~~~~~ Finish diffing between the passing order and failing order!!!"); + + // output of phase 5 + String diffFile = diffFieldsFile; + + //create the reflection file + File reflectFile = new File(reflectionFile); + reflectFile.createNewFile(); + + System.out.println("~~~~~~ Begin loading!"); + + // reflect at the after state + Configuration.config().properties(). + setProperty("statecapture.xmlDir", xmlDir + "/passing_order_xml"); + boolean reflectAfterOneSuccess = reflectEachField(diffFile, reflectFile, runner, lastPolluter()); + if (reflectAfterOneSuccess) { + String successfulField = ""; + try (BufferedReader br = new BufferedReader(new FileReader(reflectFile))) { + String line; + while ((line = br.readLine()) != null) { + if (line.contains(" made test success#######")) { + successfulField = line.substring(8, line.lastIndexOf(" made test success#######")); + break; + } + } + } + catch (Exception e) { + return; + } + timing(startTime); + if (!successfulField.equals("")) { + Files.write(Paths.get(output), (successfulField + ",").getBytes(), + StandardOpenOption.APPEND); + } else { + Files.write(Paths.get(output), "FAIL,".getBytes(), + StandardOpenOption.APPEND); + } + } + else { + timing(startTime); + Files.write(Paths.get(output), "FAIL,".getBytes(), + StandardOpenOption.APPEND); + } + + System.out.println("~~~~~~ Finish loading!"); + } catch (Exception e) { + TestPluginPlugin.mojo().getLog().error(e); + } + } else { + TestPluginPlugin.mojo().getLog().info("Module is not using a supported test framework (probably not JUnit)."); + } + } + + private void timing(long startTime) { + long endTime = System.currentTimeMillis(); + double duration = (endTime - startTime)/1000.0; + + String time = duration + ","; + try { + Files.write(Paths.get(output), time.getBytes(), + StandardOpenOption.APPEND); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private boolean reflectEachField(String diffFile, File reflectionFile, Runner runner, String polluter) throws IOException { + boolean reflectSuccess = false; + String header = "*************************do reflection************************\n"; + Files.write(Paths.get(reflectionFile.getAbsolutePath()), header.getBytes(), + StandardOpenOption.APPEND); + try { + try (BufferedReader br = new BufferedReader(new FileReader(diffFile))) { + String diffField; + while ((diffField = br.readLine()) != null) { + Configuration.config().properties(). + setProperty("statecapture.phase", "load"); + Configuration.config().properties(). + setProperty("statecapture.fieldName", diffField); + Configuration.config().properties(). + setProperty("statecapture.testname", polluter); + try { + System.out.println("## doing reflection"); + Try result = runner.runList(testFailOrder()); + if (result.get().results().get(dtname).result().toString().equals("PASS")) { + System.out.println("## reflection on diffField: " + diffField + " is success!!"); + String output = "########" + diffField + " made test success#######\n"; + Files.write(Paths.get(reflectionFile.getAbsolutePath()), output.getBytes(), + StandardOpenOption.APPEND); + reflectSuccess = true; + } else { + String output = "########" + diffField + " made test fail######\n"; + Files.write(Paths.get(reflectionFile.getAbsolutePath()), output.getBytes(), + StandardOpenOption.APPEND); + } + } catch (Exception e) { + System.out.println("## Encounter error in reflection for field: " + + diffField + " " + e); + } + } + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + System.out.println("FileNotFoundException"); + } + + return reflectSuccess; + + } + + private List victim() { + List partialOrder = new ArrayList<>(); + partialOrder.add(dtname); + return partialOrder; + } + + private List testPassOrder_full() throws IOException { + try { + System.out.println("------ testPassOrder_full: " + PathManager.modulePath()); + List dtl = new Gson().fromJson(FileUtil.readFile(replayPath), DependentTestList.class).dts(); + List partialOrder = new ArrayList(); + for(int i = 0; i< dtl.size(); i++ ) { + DependentTest dt = dtl.get(i); + if (dt.name().equals(dtname)) { + for(String s: dt.intended().order()) { + partialOrder.add(s); + if (s.equals(dt.name())) + break; + } + if (!partialOrder.contains(dtname)) { + partialOrder.add(dtname); + } + return partialOrder; + } + } + return null; + + } catch (Exception e) { + System.out.println("## Encounter exception in reading json!"); + return null; + } + } + + private List testFailOrder() throws IOException { + if (replayPath2.toString().equals("")) { + return testFailOrder_full(); + } + else { + return testFailOrder_minimized(); + } + } + + private List testFailOrder_full() throws IOException { + try { + System.out.println("------ testFailOrder_full: " + PathManager.modulePath()); + List dtl = new Gson().fromJson(FileUtil.readFile(replayPath), DependentTestList.class).dts(); + List partialOrder = new ArrayList(); + for(int i = 0; i< dtl.size(); i++ ) { + DependentTest dt = dtl.get(i); + if (dt.name().equals(dtname)) { + for(String s: dt.revealed().order()) { + partialOrder.add(s); + if (s.equals(dt.name())) + break; + } + if (!partialOrder.contains(dtname)) { + partialOrder.add(dtname); + } + return partialOrder; + } + } + return null; + + } catch (Exception e) { + System.out.println("## Encounter exception in reading json!"); + return null; + } + } + + private List testFailOrder_minimized() throws IOException { + List failingTests = new ArrayList(); + try { + List polluters = new Gson().fromJson(FileUtil.readFile(replayPath2), MinimizeTestsResult.class).polluters(); + for(PolluterData pd: polluters) { + if (pd.deps().size() >=1) { + failingTests.addAll(pd.deps()); + failingTests.add(dtname); + return failingTests; + } + } + return null; + } catch (Exception e) { + System.out.println("## Encounter exception in reading json for failing order!"); + return null; + } + } + + private String lastPolluter() { + if (replayPath2.toString().equals("")) { + return lastPolluter_full(); + } + else { + return lastPolluter_minimized(); + } + } + + private String lastPolluter_full() { + try { + List failorder = testFailOrder_full(); + return failorder.get(failorder.size()-2); + } catch (Exception e) { + System.out.println("Encounter exception in lastPolluter_full!"); + return null; + } + } + + private String lastPolluter_minimized() { + try { + List polluters = new Gson().fromJson(FileUtil.readFile(replayPath2), MinimizeTestsResult.class).polluters(); + for(PolluterData pd: polluters) { + if (pd.deps().size() >=1) { + return pd.deps().get(pd.deps().size()-1); + } + } + return null; + } catch (Exception e) { + System.out.println("Encounter exception in reading json for failing order!"); + return null; + } + } + + int countDirNums(String path) { + File [] list = new File(path).listFiles(); + int num = 0; + for (File file : list) { + if (file.isDirectory()) { + num ++; + } + } + return num; + } + + public void diffing() { + try { + diffSub(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + private void recordsubDiff(String beforeState, + String afterState, String fileName) { + try { + // create a string builder + StringBuilder sb = new StringBuilder(); + Diff diff = DiffBuilder.compare(beforeState).withTest(afterState). + withNodeMatcher(new DefaultNodeMatcher( + ElementSelectors.byName + )) + .checkForSimilar() + .build(); + Iterable differences = diff.getDifferences(); + for (Object object : differences) { + Difference difference = (Difference)object; + + sb.append("***********************\n"); + sb.append(difference); + sb.append("\n~~~~\n"); + makeSubDifferenceReport(difference, sb); + sb.append("***********************\n"); + } + writeToFile(fileName, sb.toString(), true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private String readFile(String path) throws IOException { + File file = new File(path); + return FileUtils.readFileToString(file, "UTF-8"); + } + + private Set readFileContentsAsSet(String path) { + File file = new File(path); + Set keys = new HashSet<>(); + try (BufferedReader br = new BufferedReader(new FileReader(file))) { + String line; + while ((line = br.readLine()) != null) { + keys.add(line); + } + } catch (IOException e) { + e.printStackTrace(); + } + return keys; + } + + private void diffSub() throws FileNotFoundException, UnsupportedEncodingException { + String xmlDirPrefix = xmlDir; + String subxml0 = xmlDirPrefix + "/passing_order_xml"; + String subxml1 = xmlDirPrefix + "/failing_order_xml"; + String afterRootPath = Configuration.config().getProperty("statecapture.rootFile"); // *** + Set afterRoots = readFileContentsAsSet(afterRootPath); + + for(String s: afterRoots) { + + String path0 = subxml0 + "/" + s + ".xml"; + String path1 = subxml1 + "/" + s + ".xml"; + String state0 = ""; String state1 = ""; + File file0 = new File(path0); + if (!file0.exists()) { + continue; + } + else { + try { + state0 = readFile(path0); + state1 = readFile(path1); + } + catch (IOException e) { + e.printStackTrace(); + } + + if (!state0.equals(state1)) { + diffFields_filtered.add(s); + String subdiffFile = subdiffsFold + "/" + s + ".txt"; + recordsubDiff(state0, state1, subdiffFile); + } + } + } + + PrintWriter writer = new PrintWriter(diffFieldsFile, "UTF-8"); + + for(String ff: diffFields_filtered) { + + writer.println(ff); + } + writer.close(); + } + + + /** + * Writes content into a file. + * + * @param fn name of the destination file + * @param content string representing the data to be written + * @param append boolean indicating whether to append to destination file or rewrite it + */ + protected void writeToFile(String fn, String content, boolean append) { + try { + File f = new File(fn); + f.createNewFile(); + + FileWriter fw = new FileWriter(f.getAbsoluteFile(), append); + BufferedWriter w = new BufferedWriter(fw); + w.write(content); + w.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void makeSubDifferenceReport(Difference difference, StringBuilder sb) { + Detail controlNode = difference.getComparison().getControlDetails(); + Detail afterNode = difference.getComparison().getTestDetails(); + + String diffXpath = controlNode.getXPath(); + if (diffXpath == null) { + diffXpath = afterNode.getXPath(); + if (diffXpath == null) { + sb.append("NULL xpath\n"); + return; + } + } + sb.append(controlNode.getXPath()); + sb.append("\n"); + sb.append(afterNode.getXPath()); + sb.append("\n"); + + sb.append(difference.getComparison().getType() + "\n"); + sb.append("--------\n"); + + // Deal specifically with if in map + if (controlNode != null) { + Node target = controlNode.getTarget(); + if (target != null && target.getNodeName().equals("entry")) { // Tag name "entry" matches some map structure we want to explore + for (int i = 0; i < target.getChildNodes().getLength(); i++) { + sb.append(target.getChildNodes().item(i).getTextContent()); + sb.append("\n"); + } + } + } + } + +}