public static TreeSet<String> findTestsToRun(
            ArrayList<String> implementationDirectories,
            ArrayList<String> testDirectories, ArrayList<String> modifiedClasses)
            throws IOException, ClassNotFoundException {
        // iterate directory to build dependency graph & tests
        HashMap<String, HashSet<String>> dependencies = new HashMap<String, HashSet<String>>();
        HashSet<String> tests = new HashSet<String>();
        for (String directoryPath : implementationDirectories) {
            File directory = new File(directoryPath);
            iterateDirectory(directory, dependencies, tests, false);
        }
        for (String testDirectoryPath : testDirectories) {
            File directory = new File(testDirectoryPath);
            iterateDirectory(directory, dependencies, tests, true);
        }

        // iterate changed classes to find affected tests
        TreeSet<String> testsToRun = new TreeSet<String>();
        HashSet<String> visited = new HashSet<String>();
        HashSet<String> toVisit = new HashSet<String>();
        toVisit.addAll(modifiedClasses);
        while (false == toVisit.isEmpty()) {
            HashSet<String> workingSet = new HashSet<String>(toVisit);
            toVisit.clear();
            for (String candidate : workingSet) {
                boolean isUnvisited = visited.add(candidate);
                if (false == isUnvisited) {
                    continue;
                }
                if (tests.contains(candidate)) {
                    testsToRun.add(candidate);
                }
                HashSet<String> futureVisits = dependencies.get(candidate);
                if (null != futureVisits) {
                    toVisit.addAll(futureVisits);
                }
            }
        }
        return testsToRun;
    }