org.evosuite.TestSuiteGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.evosuite.TestSuiteGenerator.java

Source

/**
 * Copyright (C) 2010-2016 Gordon Fraser, Andrea Arcuri and EvoSuite
 * contributors
 *
 * This file is part of EvoSuite.
 *
 * EvoSuite is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3.0 of the License, or
 * (at your option) any later version.
 *
 * EvoSuite is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>.
 */
package org.evosuite;

import org.evosuite.Properties.AssertionStrategy;
import org.evosuite.Properties.Criterion;
import org.evosuite.Properties.TestFactory;
import org.evosuite.assertion.AssertionGenerator;
import org.evosuite.assertion.CompleteAssertionGenerator;
import org.evosuite.assertion.SimpleMutationAssertionGenerator;
import org.evosuite.assertion.UnitAssertionGenerator;
import org.evosuite.classpath.ClassPathHandler;
import org.evosuite.classpath.ResourceList;
import org.evosuite.contracts.ContractChecker;
import org.evosuite.contracts.FailingTestSet;
import org.evosuite.coverage.CoverageCriteriaAnalyzer;
import org.evosuite.coverage.FitnessFunctions;
import org.evosuite.coverage.TestFitnessFactory;
import org.evosuite.coverage.branch.Branch;
import org.evosuite.coverage.branch.BranchPool;
import org.evosuite.coverage.dataflow.DefUseCoverageSuiteFitness;
import org.evosuite.ga.metaheuristics.GeneticAlgorithm;
import org.evosuite.ga.stoppingconditions.StoppingCondition;
import org.evosuite.junit.JUnitAnalyzer;
import org.evosuite.junit.writer.TestSuiteWriter;
import org.evosuite.regression.RegressionClassDiff;
import org.evosuite.regression.RegressionSearchListener;
import org.evosuite.regression.RegressionSuiteMinimizer;
import org.evosuite.result.TestGenerationResult;
import org.evosuite.result.TestGenerationResultBuilder;
import org.evosuite.rmi.ClientServices;
import org.evosuite.rmi.service.ClientState;
import org.evosuite.runtime.LoopCounter;
import org.evosuite.runtime.sandbox.PermissionStatistics;
import org.evosuite.runtime.sandbox.Sandbox;
import org.evosuite.seeding.ObjectPool;
import org.evosuite.seeding.ObjectPoolManager;
import org.evosuite.setup.DependencyAnalysis;
import org.evosuite.setup.TestCluster;
import org.evosuite.statistics.RuntimeVariable;
import org.evosuite.statistics.StatisticsSender;
import org.evosuite.strategy.*;
import org.evosuite.symbolic.DSEStats;
import org.evosuite.symbolic.DSEStrategy;
import org.evosuite.testcase.ConstantInliner;
import org.evosuite.testcase.DefaultTestCase;
import org.evosuite.testcase.TestCase;
import org.evosuite.testcase.TestChromosome;
import org.evosuite.testcase.TestFitnessFunction;
import org.evosuite.testcase.execution.EvosuiteError;
import org.evosuite.testcase.execution.ExecutionResult;
import org.evosuite.testcase.execution.ExecutionTrace;
import org.evosuite.testcase.execution.ExecutionTraceImpl;
import org.evosuite.testcase.execution.ExecutionTracer;
import org.evosuite.testcase.execution.TestCaseExecutor;
import org.evosuite.testcase.execution.reset.ClassReInitializer;
import org.evosuite.testcase.statements.MethodStatement;
import org.evosuite.testcase.statements.Statement;
import org.evosuite.testcase.statements.StringPrimitiveStatement;
import org.evosuite.testcase.statements.numeric.BooleanPrimitiveStatement;
import org.evosuite.testcase.variable.VariableReference;
import org.evosuite.testsuite.*;
import org.evosuite.utils.ArrayUtil;
import org.evosuite.utils.LoggingUtils;
import org.evosuite.utils.generic.GenericMethod;
import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.util.*;

/**
 * Main entry point. Does all the static analysis, invokes a test generation
 * strategy, and then applies postprocessing.
 * 
 * @author Gordon Fraser
 */
public class TestSuiteGenerator {

    private static final String FOR_NAME = "forName";
    private static Logger logger = LoggerFactory.getLogger(TestSuiteGenerator.class);

    /**
     * Generate a test suite for the target class
     * 
     * @return a {@link java.lang.String} object.
     */
    public TestGenerationResult generateTestSuite() {

        LoggingUtils.getEvoLogger().info("* Analyzing classpath: ");

        ClientServices.getInstance().getClientNode().changeState(ClientState.INITIALIZATION);

        // Deactivate loop counter to make sure classes initialize properly
        LoopCounter.getInstance().setActive(false);

        TestCaseExecutor.initExecutor();
        try {
            String cp = ClassPathHandler.getInstance().getTargetProjectClasspath();
            // Here is where the <clinit> code should be invoked for the first time
            DefaultTestCase test = buildLoadTargetClassTestCase(Properties.TARGET_CLASS);
            ExecutionResult execResult = TestCaseExecutor.getInstance().execute(test, Integer.MAX_VALUE);

            if (hasThrownInitializerError(execResult)) {
                // create single test suite with Class.forName()
                writeJUnitTestSuiteForFailedInitialization();
                ExceptionInInitializerError ex = getInitializerError(execResult);
                throw ex;
            } else if (!execResult.getAllThrownExceptions().isEmpty()) {
                // some other exception has been thrown during initialization
                Throwable t = execResult.getAllThrownExceptions().iterator().next();
                throw t;
            }

            DependencyAnalysis.analyzeClass(Properties.TARGET_CLASS, Arrays.asList(cp.split(File.pathSeparator)));
            LoggingUtils.getEvoLogger().info("* Finished analyzing classpath");

        } catch (Throwable e) {

            LoggingUtils.getEvoLogger().error("* Error while initializing target class: "
                    + (e.getMessage() != null ? e.getMessage() : e.toString()));
            logger.error("Problem for " + Properties.TARGET_CLASS + ". Full stack:", e);
            return TestGenerationResultBuilder
                    .buildErrorResult(e.getMessage() != null ? e.getMessage() : e.toString());
        } finally {
            if (Properties.RESET_STATIC_FIELDS) {
                configureClassReInitializer();

            }
            // Once class loading is complete we can start checking loops
            // without risking to interfere with class initialisation
            LoopCounter.getInstance().setActive(true);
        }

        /*
         * Initialises the object pool with objects carved from SELECTED_JUNIT
         * classes
         */
        // TODO: Do parts of this need to be wrapped into sandbox statements?
        ObjectPoolManager.getInstance();

        LoggingUtils.getEvoLogger().info("* Generating tests for class " + Properties.TARGET_CLASS);
        printTestCriterion();

        if (!Properties.hasTargetClassBeenLoaded()) {
            // initialization failed, then build error message
            return TestGenerationResultBuilder.buildErrorResult("Could not load target class");
        }

        if (Properties.isRegression() && Properties.REGRESSION_SKIP_SIMILAR) {
            // Sanity checks
            if (Properties.getTargetClassRegression(true) == null) {
                Properties.IGNORE_MISSING_STATISTICS = false;
                logger.error("class {} was not on the regression projectCP", Properties.TARGET_CLASS);
                return TestGenerationResultBuilder.buildErrorResult("Could not load target regression class");
            }
            if (!ResourceList.getInstance(TestGenerationContext.getInstance().getRegressionClassLoaderForSUT())
                    .hasClass(Properties.TARGET_CLASS)) {
                Properties.IGNORE_MISSING_STATISTICS = false;
                logger.error("class {} was not on the regression_cp", Properties.TARGET_CLASS);
                return TestGenerationResultBuilder.buildErrorResult(
                        "Class " + Properties.TARGET_CLASS + " did not exist on regression classpath");

            }

            boolean areDifferent = RegressionClassDiff.differentAcrossClassloaders(Properties.TARGET_CLASS);

            // If classes are different, no point in continuing.
            // TODO: report it to master to create a nice regression report
            if (!areDifferent) {
                Properties.IGNORE_MISSING_STATISTICS = false;
                logger.error("class {} was equal on both versions", Properties.TARGET_CLASS);
                return TestGenerationResultBuilder.buildErrorResult(
                        "Class " + Properties.TARGET_CLASS + " was not changed between the two versions");
            }
        }

        if (Properties.isRegression() && Properties.REGRESSION_SKIP_DIFFERENT_CFG) {
            // Does the class have the same CFG across the two versions of the program?
            boolean sameBranches = RegressionClassDiff.sameCFG();

            if (!sameBranches) {
                Properties.IGNORE_MISSING_STATISTICS = false;
                logger.error("Could not match the branches across the two versions.");
                return TestGenerationResultBuilder
                        .buildErrorResult("Could not match the branches across the two versions.");
            }
        }

        TestSuiteChromosome testCases = generateTests();

        postProcessTests(testCases);
        ClientServices.getInstance().getClientNode().publishPermissionStatistics();
        PermissionStatistics.getInstance().printStatistics(LoggingUtils.getEvoLogger());

        // progressMonitor.setCurrentPhase("Writing JUnit test cases");
        TestGenerationResult result = writeJUnitTestsAndCreateResult(testCases);

        TestCaseExecutor.pullDown();
        /*
         * TODO: when we will have several processes running in parallel, we ll
         * need to handle the gathering of the statistics.
         */
        ClientServices.getInstance().getClientNode().changeState(ClientState.WRITING_STATISTICS);

        LoggingUtils.getEvoLogger().info("* Done!");
        LoggingUtils.getEvoLogger().info("");

        return result;
    }

    /**
     * Returns true iif the test case execution has thrown an instance of ExceptionInInitializerError
     * 
     * @param execResult of the test case execution
     * @return true if the test case has thrown an ExceptionInInitializerError
     */
    private static boolean hasThrownInitializerError(ExecutionResult execResult) {
        for (Throwable t : execResult.getAllThrownExceptions()) {
            if (t instanceof ExceptionInInitializerError) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the initialized  error from the test case execution
     * 
     * @param execResult of the test case execution
     * @return null if there were no thrown instances of ExceptionInInitializerError
     */
    private static ExceptionInInitializerError getInitializerError(ExecutionResult execResult) {
        for (Throwable t : execResult.getAllThrownExceptions()) {
            if (t instanceof ExceptionInInitializerError) {
                ExceptionInInitializerError exceptionInInitializerError = (ExceptionInInitializerError) t;
                return exceptionInInitializerError;
            }
        }
        return null;
    }

    /**
     * Reports the initialized classes during class initialization to the
     * ClassReInitializater and configures the ClassReInitializer accordingly
     */
    private void configureClassReInitializer() {
        // add loaded classes during building of dependency graph
        ExecutionTrace execTrace = ExecutionTracer.getExecutionTracer().getTrace();
        final List<String> initializedClasses = execTrace.getInitializedClasses();
        ClassReInitializer.getInstance().addInitializedClasses(initializedClasses);
        // set the behaviour of the ClassReInitializer
        final boolean reset_all_classes = Properties.RESET_ALL_CLASSES_DURING_TEST_GENERATION;
        ClassReInitializer.getInstance().setReInitializeAllClasses(reset_all_classes);
    }

    private static void writeJUnitTestSuiteForFailedInitialization() throws EvosuiteError {
        TestSuiteChromosome suite = new TestSuiteChromosome();
        DefaultTestCase test = buildLoadTargetClassTestCase(Properties.TARGET_CLASS);
        suite.addTest(test);
        writeJUnitTestsAndCreateResult(suite);
    }

    /**
     * Creates a single Test Case that only loads the target class. 
     * <code>
     * Thread currentThread = Thread.currentThread();
     * ClassLoader classLoader = currentThread.getClassLoader();
     * classLoader.load(className);
     * </code>
     * @param className the class to be loaded
     * @return
     * @throws EvosuiteError if a reflection error happens while creating the test case
     */
    private static DefaultTestCase buildLoadTargetClassTestCase(String className) throws EvosuiteError {
        DefaultTestCase test = new DefaultTestCase();

        StringPrimitiveStatement stmt0 = new StringPrimitiveStatement(test, className);
        VariableReference string0 = test.addStatement(stmt0);
        try {
            Method currentThreadMethod = Thread.class.getMethod("currentThread");
            Statement currentThreadStmt = new MethodStatement(test,
                    new GenericMethod(currentThreadMethod, currentThreadMethod.getDeclaringClass()), null,
                    Collections.emptyList());
            VariableReference currentThreadVar = test.addStatement(currentThreadStmt);

            Method getContextClassLoaderMethod = Thread.class.getMethod("getContextClassLoader");
            Statement getContextClassLoaderStmt = new MethodStatement(test,
                    new GenericMethod(getContextClassLoaderMethod, getContextClassLoaderMethod.getDeclaringClass()),
                    currentThreadVar, Collections.emptyList());
            VariableReference contextClassLoaderVar = test.addStatement(getContextClassLoaderStmt);

            //         Method loadClassMethod = ClassLoader.class.getMethod("loadClass", String.class);
            //         Statement loadClassStmt = new MethodStatement(test,
            //               new GenericMethod(loadClassMethod, loadClassMethod.getDeclaringClass()), contextClassLoaderVar,
            //               Collections.singletonList(string0));
            //         test.addStatement(loadClassStmt);

            BooleanPrimitiveStatement stmt1 = new BooleanPrimitiveStatement(test, true);
            VariableReference boolean0 = test.addStatement(stmt1);

            Method forNameMethod = Class.class.getMethod("forName", String.class, boolean.class, ClassLoader.class);
            Statement forNameStmt = new MethodStatement(test,
                    new GenericMethod(forNameMethod, forNameMethod.getDeclaringClass()), null,
                    Arrays.<VariableReference>asList(string0, boolean0, contextClassLoaderVar));
            test.addStatement(forNameStmt);

            return test;
        } catch (NoSuchMethodException | SecurityException e) {
            throw new EvosuiteError("Unexpected exception while creating Class Initializer Test Case");
        }
    }

    /**
     * Apply any readability optimizations and other techniques that should use
     * or modify the generated tests
     * 
     * @param testSuite
     */
    protected void postProcessTests(TestSuiteChromosome testSuite) {

        // If overall time is short, the search might not have had enough time
        // to come up with a suite without timeouts. However, they will slow
        // down
        // the rest of the process, and may lead to invalid tests
        testSuite.getTestChromosomes().removeIf(t -> t.getLastExecutionResult() != null
                && (t.getLastExecutionResult().hasTimeout() || t.getLastExecutionResult().hasTestException()));

        if (Properties.CTG_SEEDS_FILE_OUT != null) {
            TestSuiteSerialization.saveTests(testSuite, new File(Properties.CTG_SEEDS_FILE_OUT));
        } else if (Properties.TEST_FACTORY == TestFactory.SERIALIZATION) {
            TestSuiteSerialization.saveTests(testSuite,
                    new File(Properties.SEED_DIR + File.separator + Properties.TARGET_CLASS));
        }

        /*
         * Remove covered goals that are not part of the minimization targets,
         * as they might screw up coverage analysis when a minimization timeout
         * occurs. This may happen e.g. when MutationSuiteFitness calls
         * BranchCoverageSuiteFitness which adds branch goals.
         */
        // TODO: This creates an inconsistency between
        // suite.getCoveredGoals().size() and suite.getNumCoveredGoals()
        // but it is not clear how to update numcoveredgoals
        List<TestFitnessFunction> goals = new ArrayList<>();
        for (TestFitnessFactory<?> ff : getFitnessFactories()) {
            goals.addAll(ff.getCoverageGoals());
        }
        for (TestFitnessFunction f : testSuite.getCoveredGoals()) {
            if (!goals.contains(f)) {
                testSuite.removeCoveredGoal(f);
            }
        }

        if (Properties.INLINE) {
            ClientServices.getInstance().getClientNode().changeState(ClientState.INLINING);
            ConstantInliner inliner = new ConstantInliner();
            // progressMonitor.setCurrentPhase("Inlining constants");

            // Map<FitnessFunction<? extends TestSuite<?>>, Double> fitnesses =
            // testSuite.getFitnesses();

            inliner.inline(testSuite);
        }

        if (Properties.MINIMIZE) {
            ClientServices.getInstance().getClientNode().changeState(ClientState.MINIMIZATION);
            // progressMonitor.setCurrentPhase("Minimizing test cases");
            if (!TimeController.getInstance().hasTimeToExecuteATestCase()) {
                LoggingUtils.getEvoLogger().info("* Skipping minimization because not enough time is left");
                ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Result_Size,
                        testSuite.size());
                ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Minimized_Size,
                        testSuite.size());
                ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Result_Length,
                        testSuite.totalLengthOfTestCases());
                ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Minimized_Length,
                        testSuite.totalLengthOfTestCases());
            } else if (Properties.isRegression()) {
                RegressionSuiteMinimizer minimizer = new RegressionSuiteMinimizer();
                minimizer.minimize(testSuite);
            } else {

                double before = testSuite.getFitness();

                TestSuiteMinimizer minimizer = new TestSuiteMinimizer(getFitnessFactories());

                LoggingUtils.getEvoLogger().info("* Minimizing test suite");
                minimizer.minimize(testSuite, true);

                double after = testSuite.getFitness();
                if (after > before + 0.01d) { // assume minimization
                    throw new Error("EvoSuite bug: minimization lead fitness from " + before + " to " + after);
                }
            }
        } else {
            if (!TimeController.getInstance().hasTimeToExecuteATestCase()) {
                LoggingUtils.getEvoLogger().info("* Skipping minimization because not enough time is left");
            }

            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Result_Size,
                    testSuite.size());
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Minimized_Size,
                    testSuite.size());
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Result_Length,
                    testSuite.totalLengthOfTestCases());
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Minimized_Length,
                    testSuite.totalLengthOfTestCases());
        }

        if (Properties.COVERAGE) {
            ClientServices.getInstance().getClientNode().changeState(ClientState.COVERAGE_ANALYSIS);
            CoverageCriteriaAnalyzer.analyzeCoverage(testSuite);
        }

        double coverage = testSuite.getCoverage();

        if (ArrayUtil.contains(Properties.CRITERION, Criterion.MUTATION)
                || ArrayUtil.contains(Properties.CRITERION, Criterion.STRONGMUTATION)) {
            // SearchStatistics.getInstance().mutationScore(coverage);
        }

        StatisticsSender.executedAndThenSendIndividualToMaster(testSuite);
        LoggingUtils.getEvoLogger().info("* Generated " + testSuite.size() + " tests with total length "
                + testSuite.totalLengthOfTestCases());

        // TODO: In the end we will only need one analysis technique
        if (!Properties.ANALYSIS_CRITERIA.isEmpty()) {
            // SearchStatistics.getInstance().addCoverage(Properties.CRITERION.toString(),
            // coverage);
            CoverageCriteriaAnalyzer.analyzeCriteria(testSuite, Properties.ANALYSIS_CRITERIA); // FIXME:
            // can
            // we
            // send
            // all
            // bestSuites?
        }
        if (Properties.CRITERION.length > 1)
            LoggingUtils.getEvoLogger()
                    .info("* Resulting test suite's coverage: " + NumberFormat.getPercentInstance().format(coverage)
                            + " (average coverage for all fitness functions)");
        else
            LoggingUtils.getEvoLogger().info(
                    "* Resulting test suite's coverage: " + NumberFormat.getPercentInstance().format(coverage));

        // printBudget(ga); // TODO - need to move this somewhere else
        if (ArrayUtil.contains(Properties.CRITERION, Criterion.DEFUSE) && Properties.ANALYSIS_CRITERIA.isEmpty())
            DefUseCoverageSuiteFitness.printCoverage();

        DSEStats.getInstance().trackConstraintTypes();

        DSEStats.getInstance().trackSolverStatistics();

        if (Properties.DSE_PROBABILITY > 0.0 && Properties.LOCAL_SEARCH_RATE > 0
                && Properties.LOCAL_SEARCH_PROBABILITY > 0.0) {
            DSEStats.getInstance().logStatistics();
        }

        if (Properties.FILTER_SANDBOX_TESTS) {
            for (TestChromosome test : testSuite.getTestChromosomes()) {
                // delete all statements leading to security exceptions
                ExecutionResult result = test.getLastExecutionResult();
                if (result == null) {
                    result = TestCaseExecutor.runTest(test.getTestCase());
                }
                if (result.hasSecurityException()) {
                    int position = result.getFirstPositionOfThrownException();
                    if (position > 0) {
                        test.getTestCase().chop(position);
                        result = TestCaseExecutor.runTest(test.getTestCase());
                        test.setLastExecutionResult(result);
                    }
                }
            }
        }

        if (Properties.ASSERTIONS && !Properties.isRegression()) {
            LoggingUtils.getEvoLogger().info("* Generating assertions");
            // progressMonitor.setCurrentPhase("Generating assertions");
            ClientServices.getInstance().getClientNode().changeState(ClientState.ASSERTION_GENERATION);
            if (!TimeController.getInstance().hasTimeToExecuteATestCase()) {
                LoggingUtils.getEvoLogger().info("* Skipping assertion generation because not enough time is left");
            } else {
                addAssertions(testSuite);
            }
            StatisticsSender.sendIndividualToMaster(testSuite); // FIXME: can we
            // pass the list
            // of
            // testsuitechromosomes?
        }

        if (Properties.CHECK_CONTRACTS) {
            for (TestCase failing_test : FailingTestSet.getFailingTests()) {
                testSuite.addTest(failing_test);
            }
            FailingTestSet.sendStatistics();
        }

        if (Properties.JUNIT_TESTS && Properties.JUNIT_CHECK) {
            compileAndCheckTests(testSuite);
        }

        if (Properties.SERIALIZE_REGRESSION_TEST_SUITE) {
            RegressionTestSuiteSerialization.performRegressionAnalysis(testSuite);
        }
    }

    /**
     * Compile and run the given tests. Remove from input list all tests that do
     * not compile, and handle the cases of instability (either remove tests or
     * comment out failing assertions)
     *
     * @param chromosome
     */
    private void compileAndCheckTests(TestSuiteChromosome chromosome) {
        LoggingUtils.getEvoLogger().info("* Compiling and checking tests");

        if (!JUnitAnalyzer.isJavaCompilerAvailable()) {
            String msg = "No Java compiler is available. Make sure to run EvoSuite with the JDK and not the JRE."
                    + "You can try to setup the JAVA_HOME system variable to point to it, as well as to make sure that the PATH "
                    + "variable points to the JDK before any JRE.";
            logger.error(msg);
            throw new RuntimeException(msg);
        }

        ClientServices.getInstance().getClientNode().changeState(ClientState.JUNIT_CHECK);

        // Store this value; if this option is true then the JUnit check
        // would not succeed, as the JUnit classloader wouldn't find the class
        boolean junitSeparateClassLoader = Properties.USE_SEPARATE_CLASSLOADER;
        Properties.USE_SEPARATE_CLASSLOADER = false;

        int numUnstable = 0;

        // note: compiling and running JUnit tests can be very time consuming
        if (!TimeController.getInstance().isThereStillTimeInThisPhase()) {
            Properties.USE_SEPARATE_CLASSLOADER = junitSeparateClassLoader;
            return;
        }

        List<TestCase> testCases = chromosome.getTests(); // make copy of
        // current tests

        // first, let's just get rid of all the tests that do not compile
        JUnitAnalyzer.removeTestsThatDoNotCompile(testCases);

        // compile and run each test one at a time. and keep track of total time
        long start = java.lang.System.currentTimeMillis();
        Iterator<TestCase> iter = testCases.iterator();
        while (iter.hasNext()) {
            if (!TimeController.getInstance().hasTimeToExecuteATestCase()) {
                break;
            }
            TestCase tc = iter.next();
            List<TestCase> list = new ArrayList<>();
            list.add(tc);
            numUnstable += JUnitAnalyzer.handleTestsThatAreUnstable(list);
            if (list.isEmpty()) {
                // if the test was unstable and deleted, need to remove it from
                // final testSuite
                iter.remove();
            }
        }
        /*
         * compiling and running each single test individually will take more
         * than compiling/running everything in on single suite. so it can be
         * used as an upper bound
         */
        long delta = java.lang.System.currentTimeMillis() - start;

        numUnstable += checkAllTestsIfTime(testCases, delta);

        // second passage on reverse order, this is to spot dependencies among
        // tests
        if (testCases.size() > 1) {
            Collections.reverse(testCases);
            numUnstable += checkAllTestsIfTime(testCases, delta);
        }

        chromosome.clearTests(); // remove all tests
        for (TestCase testCase : testCases) {
            chromosome.addTest(testCase); // add back the filtered tests
        }

        boolean unstable = (numUnstable > 0);

        if (!TimeController.getInstance().isThereStillTimeInThisPhase()) {
            logger.warn("JUnit checking timed out");
        }

        ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.HadUnstableTests,
                unstable);
        ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.NumUnstableTests,
                numUnstable);
        Properties.USE_SEPARATE_CLASSLOADER = junitSeparateClassLoader;

    }

    private int checkAllTestsIfTime(List<TestCase> testCases, long delta) {
        if (TimeController.getInstance().hasTimeToExecuteATestCase()
                && TimeController.getInstance().isThereStillTimeInThisPhase(delta)) {
            return JUnitAnalyzer.handleTestsThatAreUnstable(testCases);
        }
        return 0;
    }

    private int getBytecodeCount(RuntimeVariable v, Map<RuntimeVariable, Set<Integer>> m) {
        Set<Integer> branchSet = m.get(v);
        return (branchSet == null) ? 0 : branchSet.size();
    }

    private TestSuiteChromosome generateTests() {
        // Make sure target class is loaded at this point
        TestCluster.getInstance();

        ContractChecker checker = null;
        if (Properties.CHECK_CONTRACTS) {
            checker = new ContractChecker();
            TestCaseExecutor.getInstance().addObserver(checker);
        }

        TestGenerationStrategy strategy = getTestGenerationStrategy();
        TestSuiteChromosome testSuite = strategy.generateTests();

        if (Properties.CHECK_CONTRACTS) {
            TestCaseExecutor.getInstance().removeObserver(checker);
        }

        StatisticsSender.executedAndThenSendIndividualToMaster(testSuite);
        getBytecodeStatistics();

        ClientServices.getInstance().getClientNode().publishPermissionStatistics();

        writeObjectPool(testSuite);

        /*
         * PUTGeneralizer generalizer = new PUTGeneralizer(); for (TestCase test
         * : tests) { generalizer.generalize(test); // ParameterizedTestCase put
         * = new ParameterizedTestCase(test); }
         */

        return testSuite;
    }

    private TestGenerationStrategy getTestGenerationStrategy() {
        switch (Properties.STRATEGY) {
        case EVOSUITE:
            return new WholeTestSuiteStrategy();
        case RANDOM:
            return new RandomTestStrategy();
        case RANDOM_FIXED:
            return new FixedNumRandomTestStrategy();
        case ONEBRANCH:
            return new IndividualTestStrategy();
        case REGRESSION:
            return new RegressionSuiteStrategy();
        case ENTBUG:
            return new EntBugTestStrategy();
        case MOSUITE:
            return new MOSuiteStrategy();
        case DSE:
            return new DSEStrategy();
        default:
            throw new RuntimeException("Unsupported strategy: " + Properties.STRATEGY);
        }
    }

    /**
     * <p>
     * If Properties.JUNIT_TESTS is set, this method writes the given test cases
     * to the default directory Properties.TEST_DIR.
     * 
     * <p>
     * The name of the test will be equal to the SUT followed by the given
     * suffix
     * 
     * @param testSuite
     *            a test suite.
     */
    public static TestGenerationResult writeJUnitTestsAndCreateResult(TestSuiteChromosome testSuite,
            String suffix) {
        List<TestCase> tests = testSuite.getTests();
        if (Properties.JUNIT_TESTS) {
            ClientServices.getInstance().getClientNode().changeState(ClientState.WRITING_TESTS);

            TestSuiteWriter suiteWriter = new TestSuiteWriter();
            suiteWriter.insertTests(tests);

            if (Properties.CHECK_CONTRACTS) {
                LoggingUtils.getEvoLogger().info("* Writing failing test cases");
                // suite.insertAllTests(FailingTestSet.getFailingTests());
                FailingTestSet.writeJUnitTestSuite(suiteWriter);
            }

            String name = Properties.TARGET_CLASS.substring(Properties.TARGET_CLASS.lastIndexOf(".") + 1);
            String testDir = Properties.TEST_DIR;

            LoggingUtils.getEvoLogger().info("* Writing JUnit test case '" + (name + suffix) + "' to " + testDir);
            suiteWriter.writeTestSuite(name + suffix, testDir, testSuite.getLastExecutionResults());

            // If in regression mode, create a separate copy of the tests
            if (!RegressionSearchListener.statsID.equals("") && Properties.REGRESSION_STATISTICS) {
                File evosuiterTestDir = new File("evosuiter-stats");

                boolean madeDir = false;
                if (!evosuiterTestDir.exists() || !evosuiterTestDir.isDirectory()) {
                    madeDir = evosuiterTestDir.mkdirs();
                }
                if (madeDir) {
                    String regressionTestName = "T" + RegressionSearchListener.statsID + "Test";

                    LoggingUtils.getEvoLogger().info(
                            "* Writing JUnit test case '" + (regressionTestName) + "' to " + evosuiterTestDir);

                    suiteWriter.writeTestSuite(regressionTestName, evosuiterTestDir.getName(),
                            Collections.EMPTY_LIST);
                }
            }
        }
        return TestGenerationResultBuilder.buildSuccessResult();
    }

    /**
     * 
     * @param testSuite
     *            the test cases which should be written to file
     */
    public static TestGenerationResult writeJUnitTestsAndCreateResult(TestSuiteChromosome testSuite) {
        return writeJUnitTestsAndCreateResult(testSuite, Properties.JUNIT_SUFFIX);
    }

    private void addAssertions(TestSuiteChromosome tests) {
        AssertionGenerator asserter;
        ContractChecker.setActive(false);

        if (Properties.ASSERTION_STRATEGY == AssertionStrategy.MUTATION) {
            asserter = new SimpleMutationAssertionGenerator();
        } else if (Properties.ASSERTION_STRATEGY == AssertionStrategy.ALL) {
            asserter = new CompleteAssertionGenerator();
        } else
            asserter = new UnitAssertionGenerator();

        asserter.addAssertions(tests);

        if (Properties.FILTER_ASSERTIONS)
            asserter.filterFailingAssertions(tests);
    }

    private void writeObjectPool(TestSuiteChromosome suite) {
        if (!Properties.WRITE_POOL.isEmpty()) {
            LoggingUtils.getEvoLogger().info("* Writing sequences to pool");
            ObjectPool pool = ObjectPool.getPoolFromTestSuite(suite);
            pool.writePool(Properties.WRITE_POOL);
        }
    }

    private void getBytecodeStatistics() {
        if (Properties.TRACK_BOOLEAN_BRANCHES) {
            int gradientBranchCount = ExecutionTraceImpl.gradientBranches.size() * 2;
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Gradient_Branches,
                    gradientBranchCount);
        }
        if (Properties.TRACK_COVERED_GRADIENT_BRANCHES) {
            int coveredGradientBranchCount = ExecutionTraceImpl.gradientBranchesCoveredTrue.size()
                    + ExecutionTraceImpl.gradientBranchesCoveredFalse.size();
            ClientServices.getInstance().getClientNode()
                    .trackOutputVariable(RuntimeVariable.Gradient_Branches_Covered, coveredGradientBranchCount);
        }
        if (Properties.BRANCH_COMPARISON_TYPES) {
            int cmp_intzero = 0, cmp_intint = 0, cmp_refref = 0, cmp_refnull = 0;
            int bc_lcmp = 0, bc_fcmpl = 0, bc_fcmpg = 0, bc_dcmpl = 0, bc_dcmpg = 0;
            for (Branch b : BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                    .getAllBranches()) {
                int branchOpCode = b.getInstruction().getASMNode().getOpcode();
                int previousOpcode = -2;
                if (b.getInstruction().getASMNode().getPrevious() != null)
                    previousOpcode = b.getInstruction().getASMNode().getPrevious().getOpcode();
                switch (previousOpcode) {
                case Opcodes.LCMP:
                    bc_lcmp++;
                    break;
                case Opcodes.FCMPL:
                    bc_fcmpl++;
                    break;
                case Opcodes.FCMPG:
                    bc_fcmpg++;
                    break;
                case Opcodes.DCMPL:
                    bc_dcmpl++;
                    break;
                case Opcodes.DCMPG:
                    bc_dcmpg++;
                    break;
                }
                switch (branchOpCode) {
                // copmpare int with zero
                case Opcodes.IFEQ:
                case Opcodes.IFNE:
                case Opcodes.IFLT:
                case Opcodes.IFGE:
                case Opcodes.IFGT:
                case Opcodes.IFLE:
                    cmp_intzero++;
                    break;
                // copmpare int with int
                case Opcodes.IF_ICMPEQ:
                case Opcodes.IF_ICMPNE:
                case Opcodes.IF_ICMPLT:
                case Opcodes.IF_ICMPGE:
                case Opcodes.IF_ICMPGT:
                case Opcodes.IF_ICMPLE:
                    cmp_intint++;
                    break;
                // copmpare reference with reference
                case Opcodes.IF_ACMPEQ:
                case Opcodes.IF_ACMPNE:
                    cmp_refref++;
                    break;
                // compare reference with null
                case Opcodes.IFNULL:
                case Opcodes.IFNONNULL:
                    cmp_refnull++;
                    break;

                }
            }
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Cmp_IntZero,
                    cmp_intzero);
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Cmp_IntInt,
                    cmp_intint);
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Cmp_RefRef,
                    cmp_refref);
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Cmp_RefNull,
                    cmp_refnull);

            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.BC_lcmp, bc_lcmp);
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.BC_fcmpl, bc_fcmpl);
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.BC_fcmpg, bc_fcmpg);
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.BC_dcmpl, bc_dcmpl);
            ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.BC_dcmpg, bc_dcmpg);

            RuntimeVariable[] bytecodeVarsCovered = new RuntimeVariable[] { RuntimeVariable.Covered_lcmp,
                    RuntimeVariable.Covered_fcmpl, RuntimeVariable.Covered_fcmpg, RuntimeVariable.Covered_dcmpl,
                    RuntimeVariable.Covered_dcmpg, RuntimeVariable.Covered_IntInt, RuntimeVariable.Covered_IntInt,
                    RuntimeVariable.Covered_IntZero, RuntimeVariable.Covered_RefRef,
                    RuntimeVariable.Covered_RefNull };

            for (RuntimeVariable bcvar : bytecodeVarsCovered) {
                ClientServices.getInstance().getClientNode().trackOutputVariable(bcvar,
                        getBytecodeCount(bcvar, ExecutionTraceImpl.bytecodeInstructionCoveredFalse)
                                + getBytecodeCount(bcvar, ExecutionTraceImpl.bytecodeInstructionCoveredTrue));
            }

            RuntimeVariable[] bytecodeVarsReached = new RuntimeVariable[] { RuntimeVariable.Reached_lcmp,
                    RuntimeVariable.Reached_fcmpl, RuntimeVariable.Reached_fcmpg, RuntimeVariable.Reached_dcmpl,
                    RuntimeVariable.Reached_dcmpg, RuntimeVariable.Reached_IntInt, RuntimeVariable.Reached_IntInt,
                    RuntimeVariable.Reached_IntZero, RuntimeVariable.Reached_RefRef,
                    RuntimeVariable.Reached_RefNull };

            for (RuntimeVariable bcvar : bytecodeVarsReached) {
                ClientServices.getInstance().getClientNode().trackOutputVariable(bcvar,
                        getBytecodeCount(bcvar, ExecutionTraceImpl.bytecodeInstructionReached) * 2);
            }

        }

    }

    private void printTestCriterion() {
        if (Properties.CRITERION.length > 1)
            LoggingUtils.getEvoLogger().info("* Test criteria:");
        else
            LoggingUtils.getEvoLogger().info("* Test criterion:");
        for (int i = 0; i < Properties.CRITERION.length; i++)
            printTestCriterion(Properties.CRITERION[i]);
    }

    private void printTestCriterion(Criterion criterion) {
        switch (criterion) {
        case WEAKMUTATION:
            LoggingUtils.getEvoLogger().info("  - Mutation testing (weak)");
            break;
        case ONLYMUTATION:
            LoggingUtils.getEvoLogger().info("  - Only Mutation testing (weak)");
            break;
        case STRONGMUTATION:
        case MUTATION:
            LoggingUtils.getEvoLogger().info("  - Mutation testing (strong)");
            break;
        case DEFUSE:
            LoggingUtils.getEvoLogger().info("  - All DU Pairs");
            break;
        case STATEMENT:
            LoggingUtils.getEvoLogger().info("  - Statement Coverage");
            break;
        case RHO:
            LoggingUtils.getEvoLogger().info("  - Rho Coverage");
            break;
        case AMBIGUITY:
            LoggingUtils.getEvoLogger().info("  - Ambiguity Coverage");
            break;
        case ALLDEFS:
            LoggingUtils.getEvoLogger().info("  - All Definitions");
            break;
        case EXCEPTION:
            LoggingUtils.getEvoLogger().info("  - Exception");
            break;
        case ONLYBRANCH:
            LoggingUtils.getEvoLogger().info("  - Only-Branch Coverage");
            break;
        case METHODTRACE:
            LoggingUtils.getEvoLogger().info("  - Method Coverage");
            break;
        case METHOD:
            LoggingUtils.getEvoLogger().info("  - Top-Level Method Coverage");
            break;
        case METHODNOEXCEPTION:
            LoggingUtils.getEvoLogger().info("  - No-Exception Top-Level Method Coverage");
            break;
        case LINE:
            LoggingUtils.getEvoLogger().info("  - Line Coverage");
            break;
        case ONLYLINE:
            LoggingUtils.getEvoLogger().info("  - Only-Line Coverage");
            break;
        case OUTPUT:
            LoggingUtils.getEvoLogger().info("  - Method-Output Coverage");
            break;
        case INPUT:
            LoggingUtils.getEvoLogger().info("  - Method-Input Coverage");
            break;
        case BRANCH:
            LoggingUtils.getEvoLogger().info("  - Branch Coverage");
            break;
        case CBRANCH:
            LoggingUtils.getEvoLogger().info("  - Context Branch Coverage");
            break;
        case IBRANCH:
            LoggingUtils.getEvoLogger().info("  - Interprocedural Context Branch Coverage");
            break;
        case TRYCATCH:
            LoggingUtils.getEvoLogger().info("  - Try-Catch Branch Coverage");
            break;
        case REGRESSION:
            LoggingUtils.getEvoLogger().info("  - Regression");
            break;
        default:
            throw new IllegalArgumentException("Unrecognized criterion: " + criterion);
        }
    }

    /**
     * <p>
     * getFitnessFunctions
     * </p>
     * 
     * @return a list of {@link org.evosuite.testsuite.TestSuiteFitnessFunction}
     *         objects.
     */
    public static List<TestSuiteFitnessFunction> getFitnessFunctions() {
        List<TestSuiteFitnessFunction> ffs = new ArrayList<TestSuiteFitnessFunction>();
        for (int i = 0; i < Properties.CRITERION.length; i++) {
            ffs.add(FitnessFunctions.getFitnessFunction(Properties.CRITERION[i]));
        }

        return ffs;
    }

    /**
     * Prints out all information regarding this GAs stopping conditions
     * 
     * So far only used for testing purposes in TestSuiteGenerator
     */
    public void printBudget(GeneticAlgorithm<?> algorithm) {
        LoggingUtils.getEvoLogger().info("* Search Budget:");
        for (StoppingCondition sc : algorithm.getStoppingConditions())
            LoggingUtils.getEvoLogger().info("\t- " + sc.toString());
    }

    /**
     * <p>
     * getBudgetString
     * </p>
     * 
     * @return a {@link java.lang.String} object.
     */
    public String getBudgetString(GeneticAlgorithm<?> algorithm) {
        String r = "";
        for (StoppingCondition sc : algorithm.getStoppingConditions())
            r += sc.toString() + " ";

        return r;
    }

    /**
     * <p>
     * getFitnessFactories
     * </p>
     * 
     * @return a list of {@link org.evosuite.coverage.TestFitnessFactory}
     *         objects.
     */
    public static List<TestFitnessFactory<? extends TestFitnessFunction>> getFitnessFactories() {
        List<TestFitnessFactory<? extends TestFitnessFunction>> goalsFactory = new ArrayList<TestFitnessFactory<? extends TestFitnessFunction>>();
        for (int i = 0; i < Properties.CRITERION.length; i++) {
            goalsFactory.add(FitnessFunctions.getFitnessFactory(Properties.CRITERION[i]));
        }

        return goalsFactory;
    }

    /**
     * <p>
     * main
     * </p>
     * 
     * @param args
     *            an array of {@link java.lang.String} objects.
     */
    public static void main(String[] args) {
        TestSuiteGenerator generator = new TestSuiteGenerator();
        generator.generateTestSuite();
        System.exit(0);
    }

}