diff --git a/starts-plugin/src/main/java/edu/illinois/starts/jdeps/HybridMojo.java b/starts-plugin/src/main/java/edu/illinois/starts/jdeps/HybridMojo.java new file mode 100644 index 00000000..24156f14 --- /dev/null +++ b/starts-plugin/src/main/java/edu/illinois/starts/jdeps/HybridMojo.java @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2015 - Present. The STARTS Team. All Rights Reserved. + */ + +package edu.illinois.starts.jdeps; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; + +import edu.illinois.starts.helpers.ZLCHelperMethods; +import edu.illinois.starts.smethods.MethodLevelStaticDepsBuilder; +import edu.illinois.starts.util.ChecksumUtil; +import edu.illinois.starts.util.Logger; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Execute; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.surefire.booter.Classpath; + +@Mojo(name = "hybrid", requiresDirectInvocation = true, requiresDependencyResolution = ResolutionScope.TEST) +@Execute(phase = LifecyclePhase.TEST_COMPILE) +public class HybridMojo extends DiffMojo { + + private Logger logger; + private Set changedMethods; + private Set newMethods; + private Set impactedMethods; + private Set newClasses; + private Set oldClasses; + private Set affectedTestClasses; + private Set nonAffectedMethods; + private Map methodsCheckSum; + private Map> classesChecksum; + private Map> methodToTestClasses; + private ClassLoader loader; + private Map> classDependencyGraph; + private Map> classToTestClassGraph; + private Set deletedClasses; + private Set changedClassesWithChangedHeaders; + private Set changedClassesWithoutChangedHeaders; + private Set impactedClasses; + + @Parameter(property = "computeImpactedMethods", defaultValue = TRUE) + private boolean computeImpactedMethods; + + @Parameter(property = "updateMethodsChecksums", defaultValue = TRUE) + private boolean updateMethodsChecksums; + + @Parameter(property = "includeVariables", defaultValue = FALSE) + private boolean includeVariables; + + @Parameter(property = "debug", defaultValue = TRUE) + private boolean debug; + + public void setComputeImpactedMethods(boolean computeImpactedMethods) { + this.computeImpactedMethods = computeImpactedMethods; + } + + public void setUpdateMethodsChecksums(boolean updateChecksums) { + this.updateMethodsChecksums = updateChecksums; + } + + public Set getAffectedMethods() { + Set affectedMethods = new HashSet<>(); + affectedMethods.addAll(changedMethods); + affectedMethods.addAll(newMethods); + return Collections.unmodifiableSet(affectedMethods); + } + + public Set getAffectedClasses() { + Set affectedClasses = new HashSet<>(); + affectedClasses.addAll(changedClassesWithChangedHeaders); + affectedClasses.addAll(newClasses); + return Collections.unmodifiableSet(affectedClasses); + } + + public Set getImpactedMethods() { + return Collections.unmodifiableSet(impactedMethods); + } + + public Set getImpactedClasses() { + return Collections.unmodifiableSet(impactedClasses); + } + + public Set getNewClasses() { + return Collections.unmodifiableSet(newClasses); + } + + public Set getOldClasses() { + return Collections.unmodifiableSet(oldClasses); + } + + public Set getChangedClassesWithChangedHeaders() throws MojoExecutionException { + Set changedC = new HashSet<>(); + for (String c : changedClassesWithChangedHeaders) { + URL url = loader.getResource(ChecksumUtil.toClassOrJavaName(c, false)); + String extForm = url.toExternalForm(); + changedC.add(extForm); + } + return Collections.unmodifiableSet(changedC); + } + + public Set getChangedClassesWithoutChangedHeaders() throws MojoExecutionException { + Set changedC = new HashSet<>(); + for (String c : changedClassesWithoutChangedHeaders) { + URL url = loader.getResource(ChecksumUtil.toClassOrJavaName(c, false)); + String extForm = url.toExternalForm(); + changedC.add(extForm); + } + return Collections.unmodifiableSet(changedC); + } + + public Set getChangedClasses() throws MojoExecutionException { + Set changedC = new HashSet<>(); + for (String c : changedClassesWithoutChangedHeaders) { + URL url = loader.getResource(ChecksumUtil.toClassOrJavaName(c, false)); + String extForm = url.toExternalForm(); + changedC.add(extForm); + } + for (String c : changedClassesWithChangedHeaders) { + URL url = loader.getResource(ChecksumUtil.toClassOrJavaName(c, false)); + String extForm = url.toExternalForm(); + changedC.add(extForm); + } + + return Collections.unmodifiableSet(changedC); + } + + public Set getNonAffectedMethods() { + return Collections.unmodifiableSet(nonAffectedMethods); + } + + /** + * This method first builds the method-level static dependencies by calling + * MethodLevelStaticDepsBuilder.buildMethodsGraph(). + * Then, it computes and retrieves the classes' checksums and the mapping + * between methods and test classes by calling + * MethodLevelStaticDepsBuilder.computeMethodsChecksum(ClassLoader) and + * MethodLevelStaticDepsBuilder.computeMethod2testClasses() respectively. + * Finally, it computes the changed (and impacted) methods through changed + * classes by calling runMethods(boolean). + */ + public void execute() throws MojoExecutionException { + Logger.getGlobal().setLoggingLevel(Level.parse(loggingLevel)); + logger = Logger.getGlobal(); + Classpath sfClassPath = getSureFireClassPath(); + loader = createClassLoader(sfClassPath); + + // Build method level static dependencies + try { + MethodLevelStaticDepsBuilder.buildMethodsGraph(includeVariables); + methodToTestClasses = MethodLevelStaticDepsBuilder.computeMethodToTestClasses(); + classesChecksum = MethodLevelStaticDepsBuilder.computeClassesChecksums(loader, cleanBytes); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + + runHybrid(computeImpactedMethods); + } + + /** + * This method handles the main logic of the mojo for hybrid analysis. + * It checks if the file of dependencies exists and sets the changed + * methods accordingly. (First run doesn't have the file of dependencies) + * If the file does not exist, it sets the changed methods, new methods, + * impacted test classes, old classes, changed classes, new classes and + * non-affected methods. + * If the file exists, it sets the changed methods and computes the impacted + * methods and impacted test classes if impacted is true. + * It also updates the methods checksums in the dependency file if + * updateMethodsChecksums is true. + * + * @param impacted a boolean value indicating whether to compute impacted + * methods and impacted test classes + * @throws MojoExecutionException if an exception occurs while setting changed + * methods + */ + protected void runHybrid(boolean impacted) throws MojoExecutionException { + // Checking if the file of dependencies exists (first run) + if (!Files.exists(Paths.get(getArtifactsDir() + METHODS_CHECKSUMS_SERIALIZED_FILE)) + && !Files.exists(Paths.get(getArtifactsDir() + CLASSES_CHECKSUM_SERIALIZED_FILE))) { + // In the first run we compute all method checksums and save them. + // In later runs we just compute new method checksums for changed classes + MethodLevelStaticDepsBuilder.computeMethodsChecksum(loader); + methodsCheckSum = MethodLevelStaticDepsBuilder.getMethodsCheckSum(); + changedMethods = new HashSet<>(); + newMethods = MethodLevelStaticDepsBuilder.computeMethods(); + affectedTestClasses = MethodLevelStaticDepsBuilder.computeTestClasses(); + newClasses = MethodLevelStaticDepsBuilder.getClasses(); + oldClasses = new HashSet<>(); + deletedClasses = new HashSet<>(); + changedClassesWithChangedHeaders = new HashSet<>(); + changedClassesWithoutChangedHeaders = new HashSet<>(); + nonAffectedMethods = new HashSet<>(); + + if (impacted) { + impactedMethods = newMethods; + impactedClasses = newClasses; + } + + if (updateMethodsChecksums) { + try { + // Save class-to-checksums mapping + ZLCHelperMethods.serializeMapping(classesChecksum, getArtifactsDir(), + CLASSES_CHECKSUM_SERIALIZED_FILE); + // The method-to-checksum mapping has been updated in + // ZLCHelperMethods.getChangedDataHybrid() + ZLCHelperMethods.serializeMapping(methodsCheckSum, getArtifactsDir(), + METHODS_CHECKSUMS_SERIALIZED_FILE); + } catch (IOException exception) { + exception.printStackTrace(); + } + } + } else { + + classDependencyGraph = MethodLevelStaticDepsBuilder.constructClassesDependencyGraph(); + MethodLevelStaticDepsBuilder.constuctTestClassesToClassesGraph(); + classToTestClassGraph = MethodLevelStaticDepsBuilder.constructClassesToTestClassesGraph(); + methodToTestClasses = MethodLevelStaticDepsBuilder.computeMethodToTestClasses(); + affectedTestClasses = new HashSet<>(); + + setChangedAndNonaffectedMethods(); + + if (impacted) { + computeImpactedMethods(); + computeImpactedClasses(); + } + + if (updateMethodsChecksums) { + try { + // Save class-to-checksums mapping + ZLCHelperMethods.serializeMapping(classesChecksum, getArtifactsDir(), + CLASSES_CHECKSUM_SERIALIZED_FILE); + // Save method-to-checksum mapping + ZLCHelperMethods.serializeMapping(methodsCheckSum, getArtifactsDir(), + METHODS_CHECKSUMS_SERIALIZED_FILE); + } catch (IOException exception) { + exception.printStackTrace(); + } + } + + } + + logInfo(impacted); + } + + /** + * This method logs information statements about changed methods, new methods, + * impacted test classes, new classes, old classes and changed classes. + * If impacted is true, it also logs information about impacted methods. + * + * @param impacted a boolean value indicating whether to log information about + * impacted methods + */ + private void logInfo(boolean impacted) { + logger.log(Level.INFO, "ChangedMethods: " + changedMethods.size()); + logger.log(Level.INFO, "NewMethods: " + newMethods.size()); + + if (impacted) { + logger.log(Level.INFO, "ImpactedMethods: " + impactedMethods.size()); + } + + logger.log(Level.INFO, "NewClasses: " + newClasses.size()); + logger.log(Level.INFO, "OldClasses: " + oldClasses.size()); + logger.log(Level.INFO, "DeletedClasses: " + deletedClasses.size()); + logger.log(Level.INFO, "ChangedClassesWithChangedHeaders: " + changedClassesWithChangedHeaders.size()); + logger.log(Level.INFO, "ChangedClassesWithoutChangedHeaders: " + changedClassesWithoutChangedHeaders.size()); + logger.log(Level.INFO, "AffectedTestClasses: " + affectedTestClasses.size()); + + if (impacted) { + logger.log(Level.INFO, "ImpactedClasses: " + impactedClasses.size()); + } + + // DEBUG PRINTS + if (debug) { + logger.log(Level.INFO, "ImpactedMethods: " + impactedMethods); + logger.log(Level.INFO, "ImpactedClasses: " + impactedClasses); + logger.log(Level.INFO, "AffectedTestClasses: " + affectedTestClasses); + logger.log(Level.INFO, "ClassDependencyGraph: " + classDependencyGraph); + logger.log(Level.INFO, "ClassToTestClassGraph: " + classToTestClassGraph); + logger.log(Level.INFO, "ChangedClassesWithChangedHeaders: " + changedClassesWithChangedHeaders); + logger.log(Level.INFO, "ChangedClassesWithoutChangedHeaders: " + changedClassesWithoutChangedHeaders); + } + } + + /** + * Sets the changed and non-affected methods by retrieving changed data using + * the ZLCHelperMethods class and updating the relevant fields. + * This method also updates the impacted test classes by adding test classes + * associated with new methods. + */ + protected void setChangedAndNonaffectedMethods() throws MojoExecutionException { + List> classesData = ZLCHelperMethods.getChangedDataHybridClassLevel(classesChecksum, + getArtifactsDir(), CLASSES_CHECKSUM_SERIALIZED_FILE); + + newClasses = classesData == null ? new HashSet() : classesData.get(0); + deletedClasses = classesData == null ? new HashSet() : classesData.get(1); + changedClassesWithChangedHeaders = classesData == null ? new HashSet() : classesData.get(2); + changedClassesWithoutChangedHeaders = classesData == null ? new HashSet() : classesData.get(3); + oldClasses = classesData == null ? new HashSet() : classesData.get(4); + + List> methodsData = ZLCHelperMethods.getChangedDataHybridMethodLevel(newClasses, deletedClasses, + changedClassesWithChangedHeaders, + changedClassesWithoutChangedHeaders, MethodLevelStaticDepsBuilder.getMethodsCheckSum(), loader, + getArtifactsDir(), METHODS_CHECKSUMS_SERIALIZED_FILE); + + methodsCheckSum = MethodLevelStaticDepsBuilder.getMethodsCheckSum(); + + changedMethods = methodsData == null ? new HashSet() : methodsData.get(0); + newMethods = methodsData == null ? new HashSet() : methodsData.get(1); + + for (String newMethod : newMethods) { + affectedTestClasses.addAll(methodToTestClasses.getOrDefault(newMethod, new HashSet<>())); + } + + for (String changedMethod : changedMethods) { + affectedTestClasses.addAll(methodToTestClasses.getOrDefault(changedMethod, new HashSet<>())); + } + + for (String addedClass : newClasses) { + affectedTestClasses.addAll(classToTestClassGraph.getOrDefault(addedClass, new HashSet<>())); + } + + for (String changedClassesWithChangedHeader : changedClassesWithChangedHeaders) { + affectedTestClasses + .addAll(classToTestClassGraph.getOrDefault(changedClassesWithChangedHeader, new HashSet<>())); + } + + } + + /** + * Computes the impacted methods by finding impacted methods for changed and new + * methods (called affected methods), and updating the impacted test classes by + * adding test classes found from impacted methods. + */ + private void computeImpactedMethods() { + impactedMethods = new HashSet<>(); + impactedMethods.addAll(findImpactedMethods(changedMethods)); + impactedMethods.addAll(findImpactedMethods(newMethods)); + for (String impactedMethod : impactedMethods) { + affectedTestClasses.addAll(methodToTestClasses.getOrDefault(impactedMethod, new HashSet())); + } + } + + /** + * Computes the impacted classes by finding impacted classes for new and + * changedClassesWithChangedHeaders + * methods (called affected methods), and updating the impacted test classes by + * adding test classes found from impacted methods. + */ + private void computeImpactedClasses() { + impactedClasses = new HashSet<>(); + impactedClasses.addAll(findImpactedClasses(newClasses)); + impactedClasses.addAll(findImpactedClasses(changedClassesWithChangedHeaders)); + for (String impactedClass : impactedClasses) { + affectedTestClasses.addAll(classToTestClassGraph.getOrDefault(impactedClass, new HashSet())); + } + } + + private Set findImpactedClasses(Set affectedClasses) { + Set classes = new HashSet<>(affectedClasses); + for (String clazz : affectedClasses) { + classes.addAll(MethodLevelStaticDepsBuilder.getClassDeps(clazz)); + } + return classes; + } + + /** + * This method finds all impacted methods associated with a set of affected + * methods (new methods and changed methods). + * It adds all method dependencies of each affected method to the set of + * impacted methods. + * This is the method that finds the transitive closure of the affected methods. + * + * @param affectedMethods a set of affected methods + * @return a set of impacted methods found from the affected methods + */ + private Set findImpactedMethods(Set affectedMethods) { + Set methods = new HashSet<>(affectedMethods); + for (String method : affectedMethods) { + methods.addAll(MethodLevelStaticDepsBuilder.getMethodDeps(method)); + + } + return methods; + } + +} diff --git a/starts-plugin/src/main/java/edu/illinois/starts/jdeps/MethodsMojo.java b/starts-plugin/src/main/java/edu/illinois/starts/jdeps/MethodsMojo.java new file mode 100644 index 00000000..df7b4d96 --- /dev/null +++ b/starts-plugin/src/main/java/edu/illinois/starts/jdeps/MethodsMojo.java @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2015 - Present. The STARTS Team. All Rights Reserved. + */ + +package edu.illinois.starts.jdeps; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; + +import edu.illinois.starts.helpers.ZLCHelperMethods; +import edu.illinois.starts.smethods.MethodLevelStaticDepsBuilder; +import edu.illinois.starts.util.ChecksumUtil; +import edu.illinois.starts.util.Logger; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Execute; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.surefire.booter.Classpath; + +@Mojo(name = "methods", requiresDirectInvocation = true, requiresDependencyResolution = ResolutionScope.TEST) +@Execute(phase = LifecyclePhase.TEST_COMPILE) +public class MethodsMojo extends DiffMojo { + + private Logger logger; + private Set changedMethods; + private Set newMethods; + private Set impactedMethods; + private Set newClasses; + private Set oldClasses; + private Set changedClasses; + private Set affectedTestClasses; + private Set nonAffectedMethods; // This may not be needed at all + private Map methodsCheckSum; + private Map> methodToTestClasses; + private ClassLoader loader; + + /** + * Set this to "true" to compute impacted methods as well. False indicates only + * changed methods will be computed. + */ + @Parameter(property = "computeImpactedMethods", defaultValue = TRUE) + private boolean computeImpactedMethods; + + /** + * Set this to "true" to save the new checksums of changed methods in the zlc + * file. + */ + @Parameter(property = "updateMethodsChecksums", defaultValue = TRUE) + private boolean updateMethodsChecksums; + + /** + * Set this to "true" to include variables in the method-level static + * dependencies. + */ + @Parameter(property = "includeVariables", defaultValue = FALSE) + private boolean includeVariables; + + /** + * Set this to "true" to print debug statements. + */ + @Parameter(property = "debug", defaultValue = FALSE) + private boolean debug; + + public void setDebug(boolean debug) { + this.debug = debug; + } + + public void setUpdateMethodsChecksums(boolean updateChecksums) { + this.updateMethodsChecksums = updateChecksums; + } + + public void setComputeImpactedMethods(boolean computeImpactedMethods) { + this.computeImpactedMethods = computeImpactedMethods; + } + + public void setIncludeVariables(boolean includeVariables) { + this.includeVariables = includeVariables; + } + + public Set getAffectedMethods() { + Set affectedMethods = new HashSet<>(); + affectedMethods.addAll(changedMethods); + affectedMethods.addAll(newMethods); + return Collections.unmodifiableSet(affectedMethods); + } + + public Set getImpactedMethods() { + return Collections.unmodifiableSet(impactedMethods); + } + + public Set getNewClasses() { + return Collections.unmodifiableSet(newClasses); + } + + public Set getOldClasses() { + return Collections.unmodifiableSet(oldClasses); + } + + public Set getChangedClasses() throws MojoExecutionException { + Set changedC = new HashSet<>(); + for (String c : changedClasses) { + + URL url = loader.getResource(ChecksumUtil.toClassOrJavaName(c, false)); + String extForm = url.toExternalForm(); + changedC.add(extForm); + } + return Collections.unmodifiableSet(changedC); + } + + public Set getNonAffectedMethods() { + return Collections.unmodifiableSet(nonAffectedMethods); + } + + /** + * This method first builds the method-level static dependencies by calling + * MethodLevelStaticDepsBuilder.buildMethodsGraph(). + * Then, it computes and retrieves the methods' checksums and the mapping + * between methods and test classes by calling + * MethodLevelStaticDepsBuilder.computeMethodsChecksum(ClassLoader) and + * MethodLevelStaticDepsBuilder.computeMethod2testClasses() respectively. + * Finally, it computes the changed (and impacted) methods by calling + * runMethods(boolean). + */ + public void execute() throws MojoExecutionException { + Logger.getGlobal().setLoggingLevel(Level.parse(loggingLevel)); + logger = Logger.getGlobal(); + Classpath sfClassPath = getSureFireClassPath(); + loader = createClassLoader(sfClassPath); + + // Build method level static dependencies + try { + MethodLevelStaticDepsBuilder.buildMethodsGraph(includeVariables); + methodToTestClasses = MethodLevelStaticDepsBuilder.computeMethodToTestClasses(); + methodsCheckSum = MethodLevelStaticDepsBuilder.computeMethodsChecksum(loader); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + + runMethods(computeImpactedMethods); + } + + /** + * This method handles the main logic of the mojo for method-level analysis. + * It checks if the file of dependencies exists and sets the changed + * methods accordingly. (First run doesn't have the file of dependencies) + * If the file does not exist, it sets the changed methods, new methods, + * impacted test classes, old classes, changed classes, new classes and + * non-affected methods. + * If the file exists, it sets the changed methods and computes the impacted + * methods and impacted test classes if impacted is true. + * It also updates the methods checksums in the dependency file if + * updateMethodsChecksums is true. + * + * @param impacted a boolean value indicating whether to compute impacted + * methods and impacted test classes + * @throws MojoExecutionException if an exception occurs while setting changed + * methods + */ + protected void runMethods(boolean impacted) throws MojoExecutionException { + + // Checking if the file of depedencies exists (first run or not) + if (!Files.exists(Paths.get(getArtifactsDir() + METHODS_CHECKSUMS_SERIALIZED_FILE))) { + changedMethods = new HashSet<>(); + newMethods = MethodLevelStaticDepsBuilder.computeMethods(); + affectedTestClasses = MethodLevelStaticDepsBuilder.computeTestClasses(); + oldClasses = new HashSet<>(); + changedClasses = new HashSet<>(); + newClasses = MethodLevelStaticDepsBuilder.getClasses(); + nonAffectedMethods = new HashSet<>(); + + if (impacted) { + impactedMethods = newMethods; + } + + // Always save the checksums in the first run + try { + ZLCHelperMethods.serializeMapping(methodsCheckSum, getArtifactsDir(), + METHODS_CHECKSUMS_SERIALIZED_FILE); + } catch (IOException exception) { + exception.printStackTrace(); + } + + } else { + // First run has saved the old revision's checksums. Time to find changes. + computeChangedMethods(); + + if (impacted) { + computeImpactedMethods(); + computeAffectedTestClasses(); + } + + if (updateMethodsChecksums) { + try { + ZLCHelperMethods.serializeMapping(methodsCheckSum, getArtifactsDir(), + METHODS_CHECKSUMS_SERIALIZED_FILE); + } catch (IOException exception) { + exception.printStackTrace(); + } + } + } + + logInfoStatements(impacted); + } + + /** + * This method logs information statements about changed methods, new methods, + * impacted test classes, new classes, old classes and changed classes. + * If impacted is true, it also logs information about impacted methods. + * + * @param impacted a boolean value indicating whether to log information about + * impacted methods + */ + private void logInfoStatements(boolean impacted) { + logger.log(Level.INFO, "ChangedMethods: " + changedMethods.size()); + logger.log(Level.INFO, "NewMethods: " + newMethods.size()); + + if (impacted) { + logger.log(Level.INFO, "ImpactedMethods: " + impactedMethods.size()); + } + + logger.log(Level.INFO, "NewClasses: " + newClasses.size()); + logger.log(Level.INFO, "OldClasses: " + oldClasses.size()); + logger.log(Level.INFO, "ChangedClasses: " + changedClasses.size()); + logger.log(Level.INFO, "AffectedTestClasses: " + affectedTestClasses.size()); + + // DEBUG PRINTS + if (debug) { + logger.log(Level.INFO, "ChangedMethods: " + changedMethods); + logger.log(Level.INFO, "ImpactedMethods: " + impactedMethods); + logger.log(Level.INFO, "AffectedTestClasses: " + affectedTestClasses); + } + } + + /** + * This method sets the changed methods by getting the list of sets for changed + * methods, new methods, impacted test classes, old classes and changed classes + * accordingly. + */ + protected void computeChangedMethods() throws MojoExecutionException { + + List> dataList = ZLCHelperMethods.getChangedDataMethods(methodsCheckSum, + methodToTestClasses, getArtifactsDir(), METHODS_CHECKSUMS_SERIALIZED_FILE); + + changedMethods = dataList == null ? new HashSet() : dataList.get(0); + newMethods = dataList == null ? new HashSet() : dataList.get(1); + + affectedTestClasses = dataList == null ? new HashSet() : dataList.get(2); + for (String newMethod : newMethods) { + affectedTestClasses.addAll(methodToTestClasses.getOrDefault(newMethod, new HashSet<>())); + } + + oldClasses = dataList == null ? new HashSet() : dataList.get(3); + changedClasses = dataList == null ? new HashSet() : dataList.get(4); + newClasses = MethodLevelStaticDepsBuilder.getClasses(); + newClasses.removeAll(oldClasses); + // nonAffectedMethods = MethodLevelStaticDepsBuilder.computeMethods(); + // nonAffectedMethods.removeAll(changedMethods); + // nonAffectedMethods.removeAll(newMethods); + } + + /** + * This method computes the impacted test classes by adding all test classes + * associated with each impacted method to the set of impacted test classes. + */ + private void computeAffectedTestClasses() { + for (String impactedMethod : impactedMethods) { + affectedTestClasses.addAll(methodToTestClasses.getOrDefault(impactedMethod, new HashSet<>())); + + } + } + + /** + * This method computes the impacted methods by finding all impacted methods + * associated with changed methods and new methods and adding them to the set of + * impacted methods. + */ + private void computeImpactedMethods() { + impactedMethods = new HashSet<>(); + impactedMethods.addAll(findImpactedMethods(changedMethods)); + impactedMethods.addAll(findImpactedMethods(newMethods)); + } + + /** + * This method finds all impacted methods associated with a set of affected + * methods (new methods and changed methods). + * It adds all method dependencies of each affected method to the set of + * impacted methods. + * This is the method that finds the transitive closure of the affected methods. + * + * @param affectedMethods a set of affected methods + * @return a set of impacted methods found from the affected methods + */ + private Set findImpactedMethods(Set affectedMethods) { + Set methods = new HashSet<>(affectedMethods); + for (String method : affectedMethods) { + methods.addAll(MethodLevelStaticDepsBuilder.getMethodDeps(method)); + + } + return methods; + } +}