/** * Copyright (C) 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package mujava.cli; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Vector; //import openjava.test.stringPlay.stringPlay; import org.apache.commons.io.FileUtils; import com.beust.jcommander.JCommander; import mujava.MutationSystem; import mujava.TestExecuterCLI; import mujava.test.NoMutantDirException; import mujava.test.NoMutantException; import mujava.test.TestResultCLI; /** * <p> * Description: run mutants API for command line version * </p> * * @author Lin Deng * @version 1.0 */ /* * Three execution modes: \item \textit{$-$default } ``\textit{$-$default}'' defines the default behavior of \textit{runmutes}. Each time it's run, it reads in the result files. For each mutant m, test case t combination, if m is dead or equivalent, t is NOT run on m. If m is live, t is run on m. At the end of execution, the new result will be written out to the same result file with updates. E.G. first run, 200 mutants, 100 killed; second run, 200-100 mutants only, 50 killed; third run, 100-50 mutants, etc. \item \textit{$-$dead } ``\textit{$-$dead}'' defines the dead mode of \textit{runmutes}. Each time it's run, it reads in the result files. For each mutant m, test case t combination, if m is dead, or live, t IS run on m. Not run on equivalent mutants. This lets us find all the tests that kill m. At the end of execution, the new result will be written out to the same result file with updates. E.G. first run, 200 mutants, 100 killed; second run, 200 mutants again; third run, 200, etc. \item \textit{$-$fresh } ``\textit{$-$fresh}'' defines the fresh mode of \textit{runmutes}. Each time it's run, it does NOT read in the result files. After execution, the result files are saved as new files, with the current timestamp. * * * Optional parameter "-p": * random choose mutants. Should compatible with 3 modes. * * -p + default: * if p=0.5, total 100 mutants * first run, 100*0.5=50 get selected to run, 40 killed; * second run, (50-40) *0.5 = 5 get selected to run, 1 killed; * third run, (5-1)*0.5=2 get selected to run, etc. * * * -p + dead: * if p=0.5, total 100 mutants * first run, 100*0.5=50 get selected to run, 40 killed; * second run, another 100*0.5=50 get selected to run, 30 killed; * third run, another 100*0.5=50 get selected to run, 35 killed, etc. * * -p + fresh: * if p=0.5, total 100 mutants * first run, 100*0.5=50 get selected to run, 40 killed; * second run, another 100*0.5=50 get selected to run, 30 killed; * third run, another 100*0.5=50 get selected to run, 35 killed, etc. * */ public class runmutes { public static String mode = "default"; public static String muJavaHomePath = new String(); public static boolean isSingleTestSet = true; public static boolean runEq = false; //default timeout private static int timeout_sec = 3000; /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { runmutesCom jct = new runmutesCom(); // dev only String[] argv = { "-all", "-fresh", "-debug", "testfolder.calTest3", "tool" ,"-timeout", "3" }; JCommander jCommander = new JCommander(jct, args); String targetClassName = null; String testSetName = null; double percentage = 1; String testSessionName = null; muJavaHomePath = Util.loadConfig(); // get all existing session name File muJavaFolder = new File(muJavaHomePath); // check if the config file has defined the correct folder if (!muJavaFolder.isDirectory()) { Util.Error("ERROR: cannot locate the folder specified in mujava.config"); return; } File[] listOfSessions = muJavaFolder.listFiles(); // null checking // check the specified folder has files or not if (listOfSessions==null) { Util.Error("ERROR: no files in the muJava home folder "+muJavaHomePath); return; } List<String> fileNameList = new ArrayList<>(); for (File file : listOfSessions) { fileNameList.add(file.getName()); } // get testSessionName if (jct.getParameters().size() == 1) { // read all test names testSessionName = jct.getParameters().get(0); } else { if (jct.getParameters().size() > 2) { Util.Error("incorrect parameters."); return; } // set names testSetName = jct.getParameters().get(0); testSessionName = jct.getParameters().get(1); } // check if session is already created if (!fileNameList.contains(testSessionName)) { Util.Error("Session does not exist."); return; } // check if debug mode if (jct.isDebug()) { Util.debug = true; } // add support for timeout // Lin 05232015 if (jct.getTimeout() == -1) // -1 means there is no input for timeout // then do nothing, just use the default one { } else { // if there IS an option for timeout timeout_sec = 1000 * jct.getTimeout(); if (timeout_sec <= 0) // if not a valid timeout, make it 3000 timeout_sec = 3000; } // if only one parameter, it must be the session name // then, no testset specified, run all tests in testset folder List<String> testSetList = new ArrayList<>(); if (jct.getParameters().size() == 1) { // read all test names testSessionName = jct.getParameters().get(0); setJMutationStructureAndSession(testSessionName); File folder = new File(MutationSystem.TESTSET_PATH); File[] listOfFiles = folder.listFiles(); for (File file : listOfFiles) { String fileName = file.getName(); if (fileName.contains(".class")) { fileName = fileName.replace(".class", ""); testSetList.add(fileName); } } isSingleTestSet = false; } else { // test set is specified // check the number of parameters if (jct.getParameters().size() > 2) { Util.Error("incorrect parameters."); return; } // set names testSetName = jct.getParameters().get(0); testSessionName = jct.getParameters().get(1); setJMutationStructureAndSession(testSessionName); // make sure test file exists File folder = new File(MutationSystem.TESTSET_PATH); // file all class files (tests) String[] extensions = new String[] { "class" }; List<File> testFiles = (List<File>) FileUtils.listFiles(folder, extensions, true); File[] listOfFiles = new File[testFiles.size()]; for (int i = 0; i < testFiles.size(); i++) { listOfFiles[i] = testFiles.get(i); } if (!hasTestFile(listOfFiles, testSetName)) { Util.Error("can't find test file: " + testSetName); return; } } // get all classes File sessionFolder = new File(MutationSystem.CLASS_PATH); String[] extensions = new String[] { "class" }; List<File> files = (List<File>) FileUtils.listFiles(sessionFolder, extensions, true); if (jct.getP() > 0 && jct.getP() <= 1) percentage = jct.getP(); else if (jct.getP() == 0) percentage = 1; else { Util.Error("Percentage must between 0 and 1"); return; } ArrayList<String> typeList = new ArrayList<String>(); if (jct.isAll()) // all is selected, add all operators { // if all is selected, all mutation operators are added typeList.add("AORB"); typeList.add("AORS"); typeList.add("AOIU"); typeList.add("AOIS"); typeList.add("AODU"); typeList.add("AODS"); typeList.add("ROR"); typeList.add("COR"); typeList.add("COD"); typeList.add("COI"); typeList.add("SOR"); typeList.add("LOR"); typeList.add("LOI"); typeList.add("LOD"); typeList.add("ASRS"); typeList.add("SDL"); typeList.add("VDL"); typeList.add("ODL"); typeList.add("CDL"); } else { // if not all, add selected ops to the list if (jct.isAORB()) { typeList.add("AORB"); } if (jct.isAORS()) { typeList.add("AORS"); } if (jct.isAOIU()) { typeList.add("AOIU"); } if (jct.isAOIS()) { typeList.add("AOIS"); } if (jct.isAODU()) { typeList.add("AODU"); } if (jct.isAODS()) { typeList.add("AODS"); } if (jct.isROR()) { typeList.add("ROR"); } if (jct.isCOR()) { typeList.add("COR"); } if (jct.isCOD()) { typeList.add("COD"); } if (jct.isCOI()) { typeList.add("COI"); } if (jct.isSOR()) { typeList.add("SOR"); } if (jct.isLOR()) { typeList.add("LOR"); } if (jct.isLOI()) { typeList.add("LOI"); } if (jct.isLOD()) { typeList.add("LOD"); } if (jct.isASRS()) { typeList.add("ASRS"); } if (jct.isSDL()) { typeList.add("SDL"); } if (jct.isVDL()) { typeList.add("VDL"); } if (jct.isCDL()) { typeList.add("CDL"); } if (jct.isODL()) { typeList.add("ODL"); } } // default option, all if (typeList.size() == 0) { typeList.add("AORB"); typeList.add("AORS"); typeList.add("AOIU"); typeList.add("AOIS"); typeList.add("AODU"); typeList.add("AODS"); typeList.add("ROR"); typeList.add("COR"); typeList.add("COD"); typeList.add("COI"); typeList.add("SOR"); typeList.add("LOR"); typeList.add("LOI"); typeList.add("LOD"); typeList.add("ASRS"); typeList.add("SDL"); typeList.add("VDL"); typeList.add("CDL"); typeList.add("ODL"); } // setJMutationStructureAndSession(testSessionName); // MutationSystem.recordInheritanceRelation(); // have this line will // mess up things totally, need to check why!!! // decide which mode to run if (jct.isDefaultMode()) { mode = "default"; } else if (jct.isDead()) mode = "dead"; else if (jct.isFresh()) { mode = "fresh"; } if (jct.isDead() && jct.isFresh()) mode = "fresh"; // decide if need to run eq mutants if (jct.isEquiv()) runEq = true; String[] types = new String[typeList.size()]; types = typeList.toArray(types); for (File file : files) { // process file names // need to be testfolder.cal // get an absolute path String fileFullPath = file.getPath(); // trim down system path String intermediatePath = fileFullPath.replace(MutationSystem.CLASS_PATH+"/",""); // trim down .java extension // targetClassName = intermediatePath.substring(0, intermediatePath.length() - ".class".length()); // targetClassName if (!intermediatePath.contains(".class")) { continue; } // need to remove .class extension if (intermediatePath.contains(".class")) { targetClassName = intermediatePath.substring(0, intermediatePath.length() - ".class".length()); } // replace / to . String class_name = ""; // replace symbols for (int j = 0; j < targetClassName.length(); j++) { if ((targetClassName.charAt(j) == '\\') || (targetClassName.charAt(j) == '/')) { class_name = class_name + "."; } else { class_name = class_name + targetClassName.charAt(j); } } if (isSingleTestSet) runTests(class_name, testSetName, types, percentage, mode); else { // NEED TO TEST FOR MULTIPLE TEST FILES for (int i = 0; i < testSetList.size(); i++) { runTests(class_name, testSetList.get(i), types, percentage, mode); } } } // System.exit(0); return; } private static boolean hasTestFile(File[] listOfFiles, String testSetName) throws Exception { if (listOfFiles == null) throw new Exception("invalid test folder"); for (File file : listOfFiles) { String fileName = file.getPath().replace(MutationSystem.TESTSET_PATH+"/", ""); fileName = fileName.replace("/", "."); if (fileName.equals(testSetName + ".class")) return true; } return false; } static void runTests(String targetClassName, String testSetName, String[] mutantTypes, double percentage, String mode) throws NoMutantException, NoMutantDirException, IOException { Util.Print("Class Name: " + targetClassName); Util.Print("Test Name: " + testSetName); Util.Print("-----------------------------------------------"); // read file // get all method names File folder = new File(MutationSystem.MUTANT_HOME + "/" + targetClassName + "/" + MutationSystem.TM_DIR_NAME); File[] listOfMethods = folder.listFiles(); if(listOfMethods==null) return; ArrayList<String> methodNameList = new ArrayList<>(); for (File method : listOfMethods) { if(method.isDirectory()) methodNameList.add(method.getName()); } /* * no result files before, no result to read. fresh mode, no need to * read anything, but need to create time stamp when output files */ if (!methodNameList.contains("mutant_list") || mode.equals("fresh")) { // mode="fresh-default"; // Util.Print("no file mode"); TestExecuterCLI test_engine = new TestExecuterCLI(targetClassName); test_engine.setTimeOut(timeout_sec); // add method list to engine, used for saving result at the end test_engine.methodList = new ArrayList<>(); test_engine.methodList2 = new ArrayList<>(); Util.setUpVectors(); Util.setUpMaps(); for (File method : listOfMethods) { if (method.isDirectory()) { test_engine.methodList.add(method.getName()); test_engine.methodList2.add(method.getName()); } } // First, read (load) test suite class. Util.DebugPrint(targetClassName + " " + testSetName); test_engine.readTestSet(testSetName); TestResultCLI test_result = new TestResultCLI(); test_engine.computeOriginalTestResults(); System.out.print("Running"); test_result = test_engine.runTraditionalMutants("All method", mutantTypes, percentage); return; } if (mode.equals("default")) // run default mode, read result first, then // if m is dead, t not run on it. { // read file TestResultCLI tr = new TestResultCLI(); // tr.path = MutationSystem.MUTANT_HOME + "/" + targetClassName + "/" + MutationSystem.TM_DIR_NAME + "/mutant_list"; tr.getResults(); // need to check if eq option is enabled, if so, need to run eq // mutants if (runEq) { tr.live_mutants.addAll(tr.eq_mutants); tr.eq_mutants = new Vector(); } tr.live_mutants = trimLiveMutants(tr.live_mutants, mutantTypes); // eliminate // mutants // of // other // types // not // listed // run TestExecuterCLI test_engine = new TestExecuterCLI(targetClassName); test_engine.setTimeOut(timeout_sec); // First, read (load) test suite class. Util.DebugPrint(targetClassName + " " + testSetName); test_engine.readTestSet(testSetName); TestResultCLI test_result = new TestResultCLI(); System.out.print("Running"); test_engine.computeOriginalTestResults(); test_result = test_engine.runTraditionalMutants("All method", mutantTypes, percentage, tr.live_mutants); // } } else if (mode.equals("dead")) // dead mode { // // read file // TestResultCLI tr = new TestResultCLI(); // // // tr.path = MutationSystem.MUTANT_HOME + "/" + targetClassName + // "/" + MutationSystem.TM_DIR_NAME // + "/mutant_list"; // // read out all old results in file // tr.getResults(); // dead mode, run live + dead + equiv // Vector newMutants = new Vector<>(); // newMutants.addAll(tr.killed_mutants); // if -equiv is an option, need run equivalent mutants too if (runEq) { System.out.println("eq mode is enabled"); // newMutants.addAll(tr.eq_mutants); } // newMutants.addAll(tr.live_mutants); // newMutants = trimLiveMutants(newMutants, mutantTypes); // trim // mutants // based on // types, // delete // mutants // that are // not in // the type // set // run TestExecuterCLI test_engine = new TestExecuterCLI(targetClassName); test_engine.setTimeOut(timeout_sec); // First, read (load) test suite class. Util.DebugPrint(targetClassName + " " + testSetName); test_engine.readTestSet(testSetName); // TestResultCLI test_result = new TestResultCLI(); System.out.print("Running"); test_engine.computeOriginalTestResults(); test_engine.runTraditionalMutants("All method", mutantTypes, percentage); } } private static Vector trimLiveMutants(Vector live_mutants, String[] mutantTypes) { Vector newLivemutants = new Vector<>(); for (Object str : live_mutants) { for (String type : mutantTypes) { if (((String) str).contains(type)) { newLivemutants.add(str); break; } } } return newLivemutants; } private static void setJMutationStructureAndSession(String sessionName) { muJavaHomePath = muJavaHomePath + "/" + sessionName; MutationSystem.SYSTEM_HOME = muJavaHomePath; MutationSystem.SRC_PATH = muJavaHomePath + "/src"; MutationSystem.CLASS_PATH = muJavaHomePath + "/classes"; MutationSystem.MUTANT_HOME = muJavaHomePath + "/result"; MutationSystem.TESTSET_PATH = muJavaHomePath + "/testset"; } // save csv file public static void saveTestResults(String targetClassName, Map<String, String> finalTestResults, Map<String, String> finalMutantResults, String method) throws IOException { // // results as to how many mutants are killed by each test Map<String, ArrayList<String>> oldResults = new HashMap<String, ArrayList<String>>(); // // results as to how many tests can kill each single mutant // Map<String, String> oldFinalMutantResults = new HashMap<String, // String>(); // ArrayList<String> testList = new ArrayList<String>(); if (mode.equals("fresh")) // fresh mode, need to save time stamp { if (!TestExecuterCLI.methodList2.contains(method)) { System.out.println("ERROR"); return; } TestExecuterCLI.methodList2.remove(method); // merge results mergeMaps(finalTestResults, finalMutantResults); // if no methods left, save file // else continue if (TestExecuterCLI.methodList2.size() == 0) { // get time Calendar nowtime = new GregorianCalendar(); File f = new File(MutationSystem.MUTANT_HOME + "/" + targetClassName + "/" + MutationSystem.TM_DIR_NAME + "/" + "result_list_" + nowtime.get(Calendar.YEAR) + "_" + (nowtime.get(Calendar.MONTH) + 1) + "_" + nowtime.get(Calendar.DATE) + "_" + nowtime.get(Calendar.HOUR) + "_" + nowtime.get(Calendar.MINUTE) + "_" + nowtime.get(Calendar.SECOND) + ".csv"); // System.out.println("can't find the mutant result file"); FileOutputStream fout = new FileOutputStream(f); StringBuffer fileContent = new StringBuffer(); fileContent.append("Mutant,"); // first line, write all tests for (Map.Entry<String, String> entry : finalTestResults.entrySet()) { fileContent.append(entry.getKey() + ","); testList.add(entry.getKey()); } // 2 extra columns fileContent.append("Total,Equiv?\r\n"); for (Map.Entry<String, String> entry : finalMutantResults.entrySet()) { fileContent.append(entry.getKey() + ","); List<String> tempkillingResult = Arrays.asList(entry.getValue().split(",\\s+")); List<String> killingResult = new ArrayList<String>(); // remove spaces for (int i = 0; i < tempkillingResult.size(); i++) { if (!tempkillingResult.get(i).equals("")) killingResult.add(tempkillingResult.get(i)); } for (String test : testList) { if (killingResult.contains(test)) { fileContent.append("1,"); } else { fileContent.append(" ,"); } } fileContent.append(killingResult.size() + ","); // 1 space // for // total fileContent.append(" "); // 1 space for equiv // fileContent.append(entry.getValue()); fileContent.append("\r\n"); } fout.write(fileContent.toString().getBytes("utf-8")); fout.close(); } return; } if (!mode.equals("fresh")) // default mode or dead mode, no time stamp { // read file String s = null; StringBuffer sb = new StringBuffer(); File f = new File(MutationSystem.MUTANT_HOME + "/" + targetClassName + "/" + MutationSystem.TM_DIR_NAME + "/" + "result_list" + ".csv"); // no file exists, means very first run if (!f.exists()) { // System.out.println("can't find the mutant result file"); // default mode, do not create with timestamp File file = new File(MutationSystem.TRADITIONAL_MUTANT_PATH, "result_list" + ".csv"); FileOutputStream fout = new FileOutputStream(file); StringBuffer fileContent = new StringBuffer(); fileContent.append("Mutant,"); // first line, write all tests for (Map.Entry<String, String> entry : finalTestResults.entrySet()) { fileContent.append(entry.getKey() + ","); testList.add(entry.getKey()); } // 2 extra columns fileContent.append("Total,Equiv?\r\n"); for (Map.Entry<String, String> entry : finalMutantResults.entrySet()) { fileContent.append(entry.getKey() + ","); List<String> tempkillingResult = Arrays.asList(entry.getValue().split(",\\s+")); List<String> killingResult = new ArrayList<String>(); // remove spaces for (int i = 0; i < tempkillingResult.size(); i++) { if (!tempkillingResult.get(i).equals("")) killingResult.add(tempkillingResult.get(i)); } for (String test : testList) { if (killingResult.contains(test)) { fileContent.append("1,"); } else { fileContent.append(" ,"); } } fileContent.append(killingResult.size() + ","); // 1 space // for total fileContent.append(" "); // 1 space for equiv // fileContent.append(entry.getValue()); fileContent.append("\r\n"); } fout.write(fileContent.toString().getBytes("utf-8")); fout.close(); return; } // ----------------- // exist result files, need to read old results first oldResults = new HashMap<>(); BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f))); while ((s = br.readLine()) != null) // read lines { String[] temp = s.split(","); ArrayList<String> tempList = new ArrayList<>(); for (int i = 1; i < temp.length; i++) { tempList.add(temp[i]); } oldResults.put(temp[0], tempList); // e.g. will be aois_1 (key), // 1, ,1... (value) } // need to compare test cases, either more or less ArrayList<String> oldTitles = oldResults.get("Mutant"); // initiate new title ArrayList<String> newTitle = new ArrayList<>(); for (int i = 0; i < oldTitles.size(); i++) newTitle.add(oldTitles.get(i)); // if there are new tests added, need to re-write the title line for (Map.Entry<String, String> entry : finalTestResults.entrySet()) { // check // every // test // name testList.add(entry.getKey()); // add test name to it if (!oldTitles.contains(entry.getKey())) { newTitle.add(0, entry.getKey()); // title would be new list // of // tests, add new test // name to the first row } } // update entire title line oldResults.put("Mutant", newTitle); for (Entry<String, ArrayList<String>> entry : oldResults.entrySet()) { // for those not executed old mutants, add spaces at the front if (!finalMutantResults.keySet().contains(entry.getKey()) && !entry.getKey().equals("Mutant")) { ArrayList<String> oldEntryVal = entry.getValue(); for (int i = 0; i < newTitle.size() - 2; i++) { if (!oldTitles.contains(newTitle.get(i))) { oldEntryVal.add(i, " "); } } // update current entry oldResults.put(entry.getKey(), oldEntryVal); } } // for each runned mutants, new sub-set for (Map.Entry<String, String> entry : finalMutantResults.entrySet()) { List<String> tempkillingResult = Arrays.asList(entry.getValue().split(",\\s+")); List<String> killingResult = new ArrayList<String>(); // remove spaces at the end for (int i = 0; i < tempkillingResult.size(); i++) { if (!tempkillingResult.get(i).equals("")) killingResult.add(tempkillingResult.get(i)); // killing result will be which test killed current mutant // e.g. test1 test2 ... } // may have old result here if (oldResults.containsKey(entry.getKey())) // if new result have overlap { // get old results ArrayList<String> oldResult = oldResults.get(entry.getKey()); // oldResult here is 1, , 1, 2... // need to get old test names ArrayList<String> oldKillingTestNames = new ArrayList<>(); for (int i = 0; i < oldResult.size() - 2; i++) { if (oldResult.get(i).equals("1")) { oldKillingTestNames.add(oldTitles.get(i)); } } // for each test, if for (int i = 0; i < newTitle.size() - 2; i++) { if (killingResult.contains(newTitle.get(i)) && !oldTitles.contains(newTitle.get(i))) oldResult.add(i, "1"); else if (!killingResult.contains(newTitle.get(i)) && !oldTitles.contains(newTitle.get(i))) { oldResult.add(i, " "); } else if (killingResult.contains(newTitle.get(i)) && oldTitles.contains(newTitle.get(i))) { // need to reset it oldResult.set(i, "1"); } } // calculate total int sum = 0; for (int i = 0; i < oldResult.size() - 2; i++) // for (String result : oldResult) { if (oldResult.get(i).equals("1")) sum++; } oldResult.set(oldResult.size() - 2, Integer.toString(sum)); // eq mode, need update csv file when necessary if (runEq == true && oldResult.get(oldResult.size() - 1).contains("Y") && sum != 0) { oldResult.set(oldResult.size() - 1, ""); } oldResults.put(entry.getKey(), oldResult); // renew record // set } else { // if this is a new mutant, need to add entirely ArrayList<String> newEntryVal = new ArrayList<>(); for (int i = 0; i < newTitle.size(); i++) { if (killingResult.contains(newTitle.get(i))) newEntryVal.add("1"); else { newEntryVal.add(" "); } } // calculate total int sum = 0; for (int i = 0; i < newEntryVal.size() - 2; i++) // for (String result : oldResult) { if (newEntryVal.get(i).equals("1")) sum++; } newEntryVal.set(newEntryVal.size() - 2, Integer.toString(sum)); oldResults.put(entry.getKey(), newEntryVal); } } br.close(); f.delete(); // update csv file // default mode, do not create with timestamp // File newf = new File(MutationSystem.TRADITIONAL_MUTANT_PATH, // "result_list" + ".csv"); FileOutputStream fout = new FileOutputStream(f); StringBuffer fileContent = new StringBuffer(); // build title fileContent.append("Mutant"); for (String test : newTitle) fileContent.append("," + test); fileContent.append("\r\n"); // build content for (Entry<String, ArrayList<String>> oldEntry : oldResults.entrySet()) { if (oldEntry.getKey().equals("Mutant")) continue; fileContent.append(oldEntry.getKey()); for (String str : oldEntry.getValue()) { fileContent.append("," + str); } fileContent.append("\r\n"); } fout.write(fileContent.toString().getBytes("utf-8")); fout.close(); } } private static void mergeMaps(Map<String, String> finalTestResults, Map<String, String> finalMutantResults) { // do final test results for (Map.Entry<String, String> entry : finalTestResults.entrySet()) // for // each // entry { if (Util.finalTestResultsMap.containsKey(entry.getKey())) // if have // same // key, // need // to // merge { String oldResultString = Util.finalTestResultsMap.get(entry.getKey()); String newResultString = entry.getValue(); String[] oldResultsArr = oldResultString.split(",\\s+"); String[] newResultsArr = newResultString.split(",\\s+"); ArrayList<String> oldResults = new ArrayList<>(Arrays.asList(oldResultsArr)); ArrayList<String> newResults = new ArrayList<>(Arrays.asList(newResultsArr)); for (String str : newResults) { if (!oldResults.contains(str)) oldResults.add(str); } String finalString = new String(); for (String str : oldResults) { finalString = finalString + str + ", "; } finalString = finalString.substring(0, finalString.length() - 2); Util.finalTestResultsMap.put(entry.getKey(), finalString); // finally, // add // it // back } else { // no same key, directly add Util.finalTestResultsMap.put(entry.getKey(), entry.getValue()); } } // do final mutant results for (Map.Entry<String, String> entry : finalMutantResults.entrySet()) // for // each // entry { if (Util.finalMutantResultsMap.containsKey(entry.getKey())) // if // have // same // key, // need // to // merge { String oldResultString = Util.finalMutantResultsMap.get(entry.getKey()); String newResultString = entry.getValue(); String[] oldResultsArr = oldResultString.split(",\\s+"); String[] newResultsArr = newResultString.split(",\\s+"); ArrayList<String> oldResults = new ArrayList<>(Arrays.asList(oldResultsArr)); ArrayList<String> newResults = new ArrayList<>(Arrays.asList(newResultsArr)); for (String str : newResults) { if (!oldResults.contains(str)) oldResults.add(str); } String finalString = new String(); for (String str : oldResults) { finalString = finalString + str + ", "; } finalString = finalString.substring(0, finalString.length() - 2); Util.finalMutantResultsMap.put(entry.getKey(), finalString); // finally, // add // it // back } else { Util.finalMutantResultsMap.put(entry.getKey(), entry.getValue()); } } } }