diff --git a/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/detection/TestShuffler.java b/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/detection/TestShuffler.java index 44937bb1..7de17441 100644 --- a/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/detection/TestShuffler.java +++ b/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/detection/TestShuffler.java @@ -34,7 +34,6 @@ public static String className(final String testName) { } private final HashMap> classToMethods; - private final String type; private final List tests; private final Set alreadySeenOrders = new HashSet<>(); @@ -203,7 +202,7 @@ public List alphabeticalOrderSelector(int round) { return alphabeticalAndTuscanOrder(round, false); } } - + private List alphabeticalClassMethodOrder() { final List fullTestOrder = new ArrayList<>(); for (String className : classToMethods.keySet()) { @@ -234,4 +233,46 @@ public List alphabeticalAndTuscanOrder(int count, boolean isTuscan) { } return fullTestOrder; } + + public List tuscanIntraClassOrder(int round) { + List classes = new ArrayList<>(classToMethods.keySet()); + HashMap classToPermutations = new HashMap(); + Collections.sort(classes); + final List fullTestOrder = new ArrayList<>(); + int n = classes.size(); // n is number of classes + int[][] classOrdering = Tuscan.generateTuscanPermutations(n); + for (String className : classes) { + int[][] methodPermuation = Tuscan.generateTuscanPermutations(classToMethods.get(className).size()); + classToPermutations.put(className, methodPermuation); + } + HashMap> newClassToMethods = new HashMap>(); + List permClasses = new ArrayList(); + int classRound = round; + while ((classOrdering.length - 1) < classRound) { + // When a intra-class method ordering has covered all pairs but other classes not, + // it will start to loop through the intra-class method ordering again until all rounds are done. + classRound -= classOrdering.length; + } + for (int i = 0; i < classOrdering[classRound].length - 1; i++) { + permClasses.add(classes.get(classOrdering[classRound][i])); + } + for (String className : permClasses) { + List methods = classToMethods.get(className); + List permMethods = new ArrayList(); + int[][] currMethodOrdering = classToPermutations.get(className); + n = methods.size(); + int methodRound = round; + while((currMethodOrdering.length - 1) < methodRound) { + methodRound -= currMethodOrdering.length; + } + for (int i = 0; i < currMethodOrdering[methodRound].length - 1; i++) { + permMethods.add(methods.get(currMethodOrdering[methodRound][i])); + } + newClassToMethods.put(className, permMethods); + } + for (String className : permClasses) { + fullTestOrder.addAll(newClassToMethods.get(className)); + } + return fullTestOrder; + } } diff --git a/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/detection/detectors/DetectorFactory.java b/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/detection/detectors/DetectorFactory.java index ba92b5d5..b99a2452 100644 --- a/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/detection/detectors/DetectorFactory.java +++ b/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/detection/detectors/DetectorFactory.java @@ -1,6 +1,7 @@ package edu.illinois.cs.dt.tools.detection.detectors; import edu.illinois.cs.dt.tools.runner.InstrumentingSmartRunner; +import edu.illinois.cs.dt.tools.utility.Tuscan; import edu.illinois.cs.testrunner.configuration.Configuration; import java.io.File; @@ -24,10 +25,12 @@ public static Detector makeDetector(final InstrumentingSmartRunner runner, final return new OriginalDetector(runner, baseDir, rounds, tests); } else if (detectorType().equals("smart-shuffle")) { return new SmartShuffleDetector(runner, baseDir, rounds, tests, detectorType()); - } else if (detectorType().equals("tuscan")){ - return new TuscanOnlyClassDetector(runner, baseDir, rounds, detectorType(), tests); } else if (detectorType().startsWith("alphabetical")) { return new AlphabeticalDetector(runner, baseDir, rounds, detectorType(), tests); + } else if (detectorType().equals("tuscan")) { + return new TuscanOnlyClassDetector(runner, baseDir, rounds, detectorType(), tests); + } else if (detectorType().equals("tuscan-intra-class")) { + return new TuscanIntraClassDetector(runner, baseDir, rounds, detectorType(), tests); } return new RandomDetector("random", baseDir, runner, rounds, tests); } diff --git a/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/detection/detectors/TuscanIntraClassDetector.java b/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/detection/detectors/TuscanIntraClassDetector.java new file mode 100644 index 00000000..46a4705b --- /dev/null +++ b/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/detection/detectors/TuscanIntraClassDetector.java @@ -0,0 +1,89 @@ +package edu.illinois.cs.dt.tools.detection.detectors; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import edu.illinois.cs.dt.tools.detection.DetectionRound; +import edu.illinois.cs.dt.tools.detection.DetectorUtil; +import edu.illinois.cs.dt.tools.detection.TestShuffler; +import edu.illinois.cs.dt.tools.detection.filters.ConfirmationFilter; +import edu.illinois.cs.dt.tools.detection.filters.UniqueFilter; +import edu.illinois.cs.dt.tools.runner.InstrumentingSmartRunner; +import edu.illinois.cs.testrunner.data.results.TestRunResult; +import edu.illinois.cs.testrunner.runner.Runner; + +public class TuscanIntraClassDetector extends ExecutingDetector { + private final List tests; + private TestRunResult origResult; + + private final HashMap> classToMethods; + private final TestShuffler testShuffler; + + public static int getClassesSize(List tests) { + List classes = new ArrayList(); + for (final String test : tests) { + final String className = TestShuffler.className(test); + if (!classes.contains(className)) { + classes.add(className); + } + } + return classes.size(); + } + + public static int findMaxMethodSize(HashMap> classToMethods) { + int maxMethodSize = 0; + for (String className : classToMethods.keySet()) { + int nn = classToMethods.get(className).size(); + if (maxMethodSize < nn) { + maxMethodSize = nn; + } + } + return maxMethodSize; + } + + public TuscanIntraClassDetector(final Runner runner, final File baseDir, final int rounds, final String type, final List tests) { + super(runner, baseDir, rounds, type); + classToMethods = new HashMap<>(); + for (final String test : tests) { + final String className = TestShuffler.className(test); + if (!classToMethods.containsKey(className)) { + classToMethods.put(className, new ArrayList<>()); + } + classToMethods.get(className).add(test); + } + int classSize = classToMethods.keySet().size(); + if (classSize == 3 || classSize == 5) { + classSize++; + } + int maxMethodSize = findMaxMethodSize(classToMethods); + if (maxMethodSize == 3 || maxMethodSize == 5) { + maxMethodSize++; + } + this.rounds = rounds; + if (classSize > maxMethodSize) { + if (this.rounds > classSize) { + this.rounds = classSize; + } + } else { + if (this.rounds > maxMethodSize) { + this.rounds = maxMethodSize; + } + } + this.tests = tests; + this.testShuffler = new TestShuffler(type, rounds, tests, baseDir); + this.origResult = DetectorUtil.originalResults(tests, runner); + if (runner instanceof InstrumentingSmartRunner) { + addFilter(new ConfirmationFilter(name, tests, (InstrumentingSmartRunner) runner)); + } else { + addFilter(new ConfirmationFilter(name, tests, InstrumentingSmartRunner.fromRunner(runner, baseDir))); + } + addFilter(new UniqueFilter()); + } + + @Override + public DetectionRound results() throws Exception { + return makeDts(origResult, runList(testShuffler.tuscanIntraClassOrder(absoluteRound.get()))); + } +} diff --git a/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/utility/Tuscan.java b/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/utility/Tuscan.java index 569a9d89..0ce926c5 100644 --- a/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/utility/Tuscan.java +++ b/idflakies-core/src/main/java/edu/illinois/cs/dt/tools/utility/Tuscan.java @@ -2,7 +2,9 @@ public class Tuscan { public static int[][] generateTuscanPermutations(int arg) { - if (arg == 3) { + if (arg == 1) { + generateOne(); + } else if (arg == 3) { generateThree(); } else if (arg == 5) { generateFive(); @@ -11,7 +13,7 @@ public static int[][] generateTuscanPermutations(int arg) { } return r; } - + private static int[][] r; private static void helper(int[] a, int i) { @@ -85,7 +87,7 @@ private static void generateTuscanSquare(int n) { while (nn != n) { // https://ww.sciencedirect.com/science/article/pii/0095895680900441 n = n * 2 - 1; - int h = (n + 1) / 2; + int h = (n + 1) / 2; for (int i = 0; i < h; i++) { for (int j = 0; j < h; j++) { r[i][n - j] = r[i][j] + h; @@ -113,6 +115,13 @@ private static void generateTuscanSquare(int n) { } } + private static void generateOne() { + int[][] t = { + { 0, 0 } + }; + r = t; + } + private static void generateThree() { int[][] t = { { 0, 1, 2, 0 }, diff --git a/idflakies-core/src/test/java/edu/illinois/TuscanIntraClassTest.java b/idflakies-core/src/test/java/edu/illinois/TuscanIntraClassTest.java new file mode 100644 index 00000000..2fb88f43 --- /dev/null +++ b/idflakies-core/src/test/java/edu/illinois/TuscanIntraClassTest.java @@ -0,0 +1,142 @@ +package edu.illinois; + +import java.util.*; + +import org.junit.Test; +import org.junit.Assert; + +import edu.illinois.cs.dt.tools.detection.TestShuffler; +import edu.illinois.cs.dt.tools.detection.detectors.TuscanIntraClassDetector; + +public class TuscanIntraClassTest { + @Test + public void test() throws Exception { + String[] testArray = { + "cn.edu.hfut.dmic.webcollector.util.CharsetDetectorTest.testGuessEncodingByMozilla", + "cn.edu.hfut.dmic.webcollector.util.CharsetDetectorTest.testGuessEncoding", + "cn.edu.hfut.dmic.webcollector.util.CrawlDatumTest.testKey", + "cn.edu.hfut.dmic.webcollector.util.DBManagerTest.testBerkeleyDBInjector", + "cn.edu.hfut.dmic.webcollector.util.DBManagerTest.testRocksDBInjector", + "cn.edu.hfut.dmic.webcollector.util.CrawlDatumsTest.testAdd", + "cn.edu.hfut.dmic.webcollector.util.CrawlDatumsTest.testAddAndReturn", + "cn.edu.hfut.dmic.webcollector.util.MetaTest.testMetaSetterAndGetter", + "cn.edu.hfut.dmic.webcollector.util.OkHttpRequesterTest.testHttpCode" + }; + List tests = Arrays.asList(testArray); + TestShuffler testShuffler = new TestShuffler("", 0, tests, null); + HashMap> classToMethods = generateClassToMethods(tests); + + int maxMethodSize = TuscanIntraClassDetector.findMaxMethodSize(classToMethods); + if (maxMethodSize == 3 || maxMethodSize == 5) { + maxMethodSize++; + } + int rounds; + int classSize = classToMethods.keySet().size(); + if (classSize == 3 || classSize == 5) { + classSize++; + } + if (classSize > maxMethodSize) { + rounds = classSize; + } else { + rounds = maxMethodSize; + } + + // Each List inside a set is basically a pair + Set> allClassPairs = generateAllPairs(tests, false); + Set> tuscanCoveredClassPairs = tuscanClassPairs(rounds, tests, testShuffler); + + Assert.assertEquals(allClassPairs, tuscanCoveredClassPairs); + + Set> allMethodPairs = generateAllPairs(tests, true); + Set> tuscanCoveredMethodPairs = tuscanMethodPairs(rounds, tests, testShuffler); + + Assert.assertEquals(allMethodPairs, tuscanCoveredMethodPairs); + } + + public static HashMap> generateClassToMethods(List tests) { + HashMap> classToMethods = new HashMap<>(); + for (final String test : tests) { + final String className = TestShuffler.className(test); + if (!classToMethods.containsKey(className)) { + classToMethods.put(className, new ArrayList<>()); + } + classToMethods.get(className).add(test); + } + return classToMethods; + } + + public static Set> generateAllPairs(List tests) { + Set> allPairs = new LinkedHashSet>(); + for (int i = 0; i < tests.size(); i++) { + for (int j = 0; j < tests.size(); j++) { + if (!tests.get(i).equals(tests.get(j))) { + List newPair = new ArrayList(); + newPair.add(tests.get(i)); + newPair.add(tests.get(j)); + allPairs.add(newPair); + } + } + } + return allPairs; + } + + private static Set> generateAllPairs(List tests, boolean isMethods) { + // Generates all pairs with a naive algorithm for comparison with tuscan-intra-class method + Set> allPairs = new LinkedHashSet>(); + for (int i = 0; i < tests.size(); i++) { + for (int j = 0; j < tests.size(); j++) { + String className1 = TestShuffler.className(tests.get(i)); + String className2 = TestShuffler.className(tests.get(j)); + List newPair = new ArrayList(); + if (!isMethods && !className1.equals(className2)) { + newPair.add(className1); + newPair.add(className2); + allPairs.add(newPair); + } else if (isMethods && className1.equals(className2) && !tests.get(i).equals(tests.get(j))) { + newPair.add(tests.get(i)); + newPair.add(tests.get(j)); + allPairs.add(newPair); + } + } + } + return allPairs; + } + + private static Set> tuscanClassPairs(int rounds, List tests, TestShuffler testShuffler) { + // Generates the class pairs using tuscan-intra-class algorithm + Set> visitedClassPairs = new LinkedHashSet>(); + for (int i = 0; i < rounds; i++) { + List currentOrder = testShuffler.tuscanIntraClassOrder(i); + for (int j = 0; j < currentOrder.size() - 1; j++) { + String className1 = TestShuffler.className(currentOrder.get(j)); + String className2 = TestShuffler.className(currentOrder.get(j + 1)); + List newPair = new ArrayList(); + if (!className1.equals(className2)) { + newPair.add(className1); + newPair.add(className2); + visitedClassPairs.add(newPair); + } + } + } + return visitedClassPairs; + } + + private static Set> tuscanMethodPairs(int rounds, List tests, TestShuffler testShuffler) { + // Generates the intra-class method pairs using tuscan-intra-class algorithm + Set> visitedMethodPairs = new LinkedHashSet>(); + for (int i = 0; i < rounds; i++) { + List currentOrder = testShuffler.tuscanIntraClassOrder(i); + for (int j = 0; j < currentOrder.size() - 1; j++) { + String class1 = TestShuffler.className(currentOrder.get(j)); + String class2 = TestShuffler.className(currentOrder.get(j + 1)); + if (class1.equals(class2)) { + List newPair = new ArrayList(); + newPair.add(currentOrder.get(j)); + newPair.add(currentOrder.get(j + 1)); + visitedMethodPairs.add(newPair); + } + } + } + return visitedMethodPairs; + } +} \ No newline at end of file diff --git a/idflakies-core/src/test/java/edu/illinois/TuscanOnlyClassTest.java b/idflakies-core/src/test/java/edu/illinois/TuscanOnlyClassTest.java new file mode 100644 index 00000000..87ef60fa --- /dev/null +++ b/idflakies-core/src/test/java/edu/illinois/TuscanOnlyClassTest.java @@ -0,0 +1,80 @@ +package edu.illinois; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; + +import edu.emory.mathcs.backport.java.util.Arrays; +import edu.illinois.cs.dt.tools.detection.TestShuffler; +import edu.illinois.cs.dt.tools.detection.detectors.TuscanOnlyClassDetector; + +public class TuscanOnlyClassTest { + @Test + public void test() throws Exception { + String[] testArray = { + "cn.edu.hfut.dmic.webcollector.util.CharsetDetectorTest.testGuessEncodingByMozilla", + "cn.edu.hfut.dmic.webcollector.util.CharsetDetectorTest.testGuessEncoding", + "cn.edu.hfut.dmic.webcollector.util.CrawlDatumTest.testKey", + "cn.edu.hfut.dmic.webcollector.util.DBManagerTest.testBerkeleyDBInjector", + "cn.edu.hfut.dmic.webcollector.util.DBManagerTest.testRocksDBInjector", + "cn.edu.hfut.dmic.webcollector.util.CrawlDatumsTest.testAdd", + "cn.edu.hfut.dmic.webcollector.util.CrawlDatumsTest.testAddAndReturn", + "cn.edu.hfut.dmic.webcollector.util.MetaTest.testMetaSetterAndGetter", + "cn.edu.hfut.dmic.webcollector.util.OkHttpRequesterTest.testHttpCode" + }; + List tests = Arrays.asList(testArray); + TestShuffler testShuffler = new TestShuffler("", 0, tests, null); + int n = TuscanOnlyClassDetector.getClassesSize(tests); + int rounds = n; + if (n == 3 || n == 5) { + rounds++; + } + + // Each List inside a set is basically a pair + Set> allClassPairs = generateAllPairs(tests); + Set> tuscanCoveredClassPairs = tuscanOnlyClassPairs(rounds, tests, testShuffler); + + Assert.assertEquals(allClassPairs, tuscanCoveredClassPairs); + } + + public static Set> generateAllPairs(List tests) { + // Generates all pairs using a naive algorithm for comparison with tuscan-only-class method + Set> allPairs = new LinkedHashSet>(); + for (int i = 0; i < tests.size(); i++) { + for (int j = 0; j < tests.size(); j++) { + String className1 = TestShuffler.className(tests.get(i)); + String className2 = TestShuffler.className(tests.get(j)); + if (!className1.equals(className2)) { + List newPair = new ArrayList(); + newPair.add(className1); + newPair.add(className2); + allPairs.add(newPair); + } + } + } + return allPairs; + } + + private static Set> tuscanOnlyClassPairs(int rounds, List tests, TestShuffler testShuffler) { + // Generates all class pairs using tuscan-only-class method + Set> visitedClassPairs = new LinkedHashSet>(); + for (int i = 0; i < rounds; i++) { + List currentOrder = testShuffler.alphabeticalAndTuscanOrder(i, true); + for (int j = 0; j < currentOrder.size() - 1; j++) { + String className1 = TestShuffler.className(currentOrder.get(j)); + String className2 = TestShuffler.className(currentOrder.get(j + 1)); + List newPair = new ArrayList(); + if (!className1.equals(className2)) { + newPair.add(className1); + newPair.add(className2); + visitedClassPairs.add(newPair); + } + } + } + return visitedClassPairs; + } +} diff --git a/idflakies-core/src/test/java/edu/illinois/TuscanTest.java b/idflakies-core/src/test/java/edu/illinois/TuscanTest.java new file mode 100644 index 00000000..59183799 --- /dev/null +++ b/idflakies-core/src/test/java/edu/illinois/TuscanTest.java @@ -0,0 +1,65 @@ +package edu.illinois; + +import java.util.*; +import java.util.stream.IntStream; + +import org.junit.Assert; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + +import edu.illinois.cs.dt.tools.utility.Tuscan; + +@RunWith(Theories.class) +public class TuscanTest { + + @DataPoints + public static int[] integers() { + int[] range = IntStream.rangeClosed(1, 20).toArray(); + return range; + } + + @Theory + public void test(int n) throws Exception { + Set> allPairs = generateAllPairs(n); + Set> tuscanCoveredPairs = tuscanPairs(n); + + Assert.assertEquals(allPairs, tuscanCoveredPairs); + } + + private static Set> generateAllPairs(int n) { + // Generates all pairs with a naive algorithm to compare with Tuscan Squares + Set> allPairs = new LinkedHashSet>(); + int[] numbersArray = IntStream.range(0, n).toArray(); + for (int i = 0; i < numbersArray.length; i++) { + for (int j = 0; j < numbersArray.length; j++) { + if (numbersArray[i] != numbersArray[j]) { + List newPair = new ArrayList<>(); + newPair.add(numbersArray[i]); + newPair.add(numbersArray[j]); + allPairs.add(newPair); + } + } + } + return allPairs; + } + + private static Set> tuscanPairs(int n) { + // Finding all number pairs generated by Tuscan Squares + Set> visitedPairs = new LinkedHashSet>(); + int[][] matrix = Tuscan.generateTuscanPermutations(n); + int rows = matrix.length; + for (int i = 0; i < rows; i++) { + int[] currentRow = matrix[i]; + for (int j = 0; j < currentRow.length - 2; j++) { + // All rows will have an extra 0 at the end hence we have -2 + List newPair = new ArrayList(); + newPair.add(currentRow[j]); + newPair.add(currentRow[j + 1]); + visitedPairs.add(newPair); + } + } + return visitedPairs; + } +}