org.evosuite.TestSuiteGenerator Maven / Gradle / Ivy
/**
* Copyright (C) 2010-2018 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 .
*/
package org.evosuite;
import org.evosuite.Properties.AssertionStrategy;
import org.evosuite.Properties.Criterion;
import org.evosuite.Properties.TestFactory;
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.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.bytecode.RegressionClassDiff;
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.seeding.ObjectPool;
import org.evosuite.seeding.ObjectPoolManager;
import org.evosuite.setup.DependencyAnalysis;
import org.evosuite.setup.ExceptionMapGenerator;
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.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.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.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);
private void initializeTargetClass() throws Throwable {
String cp = ClassPathHandler.getInstance().getTargetProjectClasspath();
// Here is where the 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("* " + ClientProcess.getPrettyPrintIdentifier() + "Finished analyzing classpath");
}
/**
* Generate a test suite for the target class
*
* @return a {@link java.lang.String} object.
*/
public TestGenerationResult generateTestSuite() {
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier() + "Analyzing classpath: ");
ClientServices.getInstance().getClientNode().changeState(ClientState.INITIALIZATION);
// Deactivate loop counter to make sure classes initialize properly
LoopCounter.getInstance().setActive(false);
ExceptionMapGenerator.initializeExceptionMap(Properties.TARGET_CLASS);
TestCaseExecutor.initExecutor();
try {
initializeTargetClass();
} catch (Throwable e) {
// If the bytecode for a method exceeds 64K, Java will complain
// Very often this is due to mutation instrumentation, so this dirty
// hack adds a fallback mode without mutation.
// This currently breaks statistics and assertions, so we have to also set these properties
boolean error = true;
String message = e.getMessage();
if (message != null && (message.contains("Method code too large") || message.contains("Class file too large"))) {
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier()
+ "Instrumentation exceeds Java's 64K limit per method in target class");
Properties.Criterion[] newCriteria = Arrays.stream(Properties.CRITERION).filter(t -> !t.equals(Properties.Criterion.STRONGMUTATION) && !t.equals(Properties.Criterion.WEAKMUTATION) && !t.equals(Properties.Criterion.MUTATION)).toArray(Properties.Criterion[]::new);
if(newCriteria.length < Properties.CRITERION.length) {
TestGenerationContext.getInstance().resetContext();
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier()
+ "Attempting re-instrumentation without mutation");
Properties.CRITERION = newCriteria;
if(Properties.NEW_STATISTICS) {
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier()
+ "Deactivating EvoSuite statistics because of instrumentation problem");
Properties.NEW_STATISTICS = false;
}
try {
initializeTargetClass();
error = false;
} catch(Throwable t) {
// No-op, error handled below
}
if(Properties.ASSERTIONS && Properties.ASSERTION_STRATEGY == AssertionStrategy.MUTATION) {
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier()
+ "Deactivating assertion minimization because mutation instrumentation does not work");
Properties.ASSERTION_STRATEGY = AssertionStrategy.ALL;
}
}
}
if(error) {
LoggingUtils.getEvoLogger().error("* " + ClientProcess.getPrettyPrintIdentifier()
+ "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("* " + ClientProcess.getPrettyPrintIdentifier() + "Generating tests for class "
+ Properties.TARGET_CLASS);
TestSuiteGeneratorHelper.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();
// As post process phases such as minimisation, coverage analysis, etc., may call getFitness()
// of each fitness function, which may try to update the Archive, in here we explicitly disable
// Archive to avoid any problem and at the same time to improve the performance of post process
// phases (as no CPU cycles would be wasted updating the Archive).
Properties.TEST_ARCHIVE = false;
TestGenerationResult result = null;
if (ClientProcess.DEFAULT_CLIENT_NAME.equals(ClientProcess.getIdentifier())) {
postProcessTests(testCases);
ClientServices.getInstance().getClientNode().publishPermissionStatistics();
PermissionStatistics.getInstance().printStatistics(LoggingUtils.getEvoLogger());
// progressMonitor.setCurrentPhase("Writing JUnit test cases");
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier() + "Writing tests to file");
result = writeJUnitTestsAndCreateResult(testCases);
writeJUnitFailingTests();
}
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("* " + ClientProcess.getPrettyPrintIdentifier() + "Done!");
LoggingUtils.getEvoLogger().info("");
return result != null ? result : TestGenerationResultBuilder.buildSuccessResult();
}
/**
* 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 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.
*
* Thread currentThread = Thread.currentThread();
* ClassLoader classLoader = currentThread.getClassLoader();
* classLoader.load(className);
*
* @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.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 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>, 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("* " + ClientProcess.getPrettyPrintIdentifier()
+ "Skipping minimization because not enough time is left");
ClientServices.track(RuntimeVariable.Result_Size, testSuite.size());
ClientServices.track(RuntimeVariable.Minimized_Size, testSuite.size());
ClientServices.track(RuntimeVariable.Result_Length, testSuite.totalLengthOfTestCases());
ClientServices.track(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("* " + ClientProcess.getPrettyPrintIdentifier() + "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("* " + ClientProcess.getPrettyPrintIdentifier()
+ "Skipping minimization because not enough time is left");
}
ClientServices.track(RuntimeVariable.Result_Size, testSuite.size());
ClientServices.track(RuntimeVariable.Minimized_Size, testSuite.size());
ClientServices.track(RuntimeVariable.Result_Length, testSuite.totalLengthOfTestCases());
ClientServices.track(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("* " + ClientProcess.getPrettyPrintIdentifier() + "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("* " + ClientProcess.getPrettyPrintIdentifier() + "Resulting test suite's coverage: "
+ NumberFormat.getPercentInstance().format(coverage)
+ " (average coverage for all fitness functions)");
else
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier() + "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("* " + ClientProcess.getPrettyPrintIdentifier() + "Generating assertions");
// progressMonitor.setCurrentPhase("Generating assertions");
ClientServices.getInstance().getClientNode().changeState(ClientState.ASSERTION_GENERATION);
if (!TimeController.getInstance().hasTimeToExecuteATestCase()) {
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier()
+ "Skipping assertion generation because not enough time is left");
} else {
TestSuiteGeneratorHelper.addAssertions(testSuite);
}
StatisticsSender.sendIndividualToMaster(testSuite); // FIXME: can we
// pass the list
// of
// testsuitechromosomes?
}
if(Properties.NO_RUNTIME_DEPENDENCY) {
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier()
+ "Property NO_RUNTIME_DEPENDENCY is set to true - skipping JUnit compile check");
LoggingUtils.getEvoLogger().info("* " +ClientProcess.getPrettyPrintIdentifier()
+ "WARNING: Not including the runtime dependencies is likely to lead to flaky tests!");
}
else if (Properties.JUNIT_TESTS && Properties.JUNIT_CHECK) {
compileAndCheckTests(testSuite);
}
if (Properties.SERIALIZE_REGRESSION_TEST_SUITE) {
RegressionSuiteSerializer.appendToRegressionTestSuite(testSuite);
}
if(Properties.isRegression() && Properties.KEEP_REGRESSION_ARCHIVE){
RegressionSuiteSerializer.storeRegressionArchive();
}
}
/**
* 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("* " + ClientProcess.getPrettyPrintIdentifier() + "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 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 iter = testCases.iterator();
while (iter.hasNext()) {
if (!TimeController.getInstance().hasTimeToExecuteATestCase()) {
break;
}
TestCase tc = iter.next();
List 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.track(RuntimeVariable.HadUnstableTests, unstable);
ClientServices.track(RuntimeVariable.NumUnstableTests, numUnstable);
Properties.USE_SEPARATE_CLASSLOADER = junitSeparateClassLoader;
}
private static int checkAllTestsIfTime(List testCases, long delta) {
if (TimeController.getInstance().hasTimeToExecuteATestCase()
&& TimeController.getInstance().isThereStillTimeInThisPhase(delta)) {
return JUnitAnalyzer.handleTestsThatAreUnstable(testCases);
}
return 0;
}
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 = TestSuiteGeneratorHelper.getTestGenerationStrategy();
TestSuiteChromosome testSuite = strategy.generateTests();
if (Properties.CHECK_CONTRACTS) {
TestCaseExecutor.getInstance().removeObserver(checker);
}
StatisticsSender.executedAndThenSendIndividualToMaster(testSuite);
TestSuiteGeneratorHelper.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;
}
/**
*
* If Properties.JUNIT_TESTS is set, this method writes the given test cases
* to the default directory Properties.TEST_DIR.
*
*
* 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 tests = testSuite.getTests();
if (Properties.JUNIT_TESTS) {
ClientServices.getInstance().getClientNode().changeState(ClientState.WRITING_TESTS);
TestSuiteWriter suiteWriter = new TestSuiteWriter();
suiteWriter.insertTests(tests);
String name = Properties.TARGET_CLASS.substring(Properties.TARGET_CLASS.lastIndexOf(".") + 1);
String testDir = Properties.TEST_DIR;
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier() + "Writing JUnit test case '"
+ (name + suffix) + "' to " + testDir);
suiteWriter.writeTestSuite(name + suffix, testDir, testSuite.getLastExecutionResults());
}
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);
}
public void writeJUnitFailingTests() {
if (!Properties.CHECK_CONTRACTS)
return;
FailingTestSet.sendStatistics();
if (Properties.JUNIT_TESTS) {
TestSuiteWriter suiteWriter = new TestSuiteWriter();
//suiteWriter.insertTests(FailingTestSet.getFailingTests());
TestSuiteChromosome suite = new TestSuiteChromosome();
for(TestCase test : FailingTestSet.getFailingTests()) {
test.setFailing();
suite.addTest(test);
}
String name = Properties.TARGET_CLASS.substring(Properties.TARGET_CLASS.lastIndexOf(".") + 1);
String testDir = Properties.TEST_DIR;
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier() + "Writing failing test cases '"
+ (name + Properties.JUNIT_SUFFIX) + "' to " + testDir);
suiteWriter.insertAllTests(suite.getTests());
FailingTestSet.writeJUnitTestSuite(suiteWriter);
suiteWriter.writeTestSuite(name + Properties.JUNIT_FAILED_SUFFIX, testDir, suite.getLastExecutionResults());
}
}
private void writeObjectPool(TestSuiteChromosome suite) {
if (!Properties.WRITE_POOL.isEmpty()) {
LoggingUtils.getEvoLogger().info("* " + ClientProcess.getPrettyPrintIdentifier() + "Writing sequences to pool");
ObjectPool pool = ObjectPool.getPoolFromTestSuite(suite);
pool.writePool(Properties.WRITE_POOL);
}
}
/**
*
* getFitnessFunctions
*
*
* @return a list of {@link org.evosuite.testsuite.TestSuiteFitnessFunction}
* objects.
*/
public static List getFitnessFunctions() {
List ffs = new ArrayList();
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("* " + ClientProcess.getPrettyPrintIdentifier() + "Search Budget:");
for (StoppingCondition sc : algorithm.getStoppingConditions())
LoggingUtils.getEvoLogger().info("\t- " + sc.toString());
}
/**
*
* getBudgetString
*
*
* @return a {@link java.lang.String} object.
*/
public String getBudgetString(GeneticAlgorithm> algorithm) {
String r = "";
for (StoppingCondition sc : algorithm.getStoppingConditions())
r += sc.toString() + " ";
return r;
}
/**
*
* getFitnessFactories
*
*
* @return a list of {@link org.evosuite.coverage.TestFitnessFactory}
* objects.
*/
public static List> getFitnessFactories() {
List> goalsFactory = new ArrayList>();
for (int i = 0; i < Properties.CRITERION.length; i++) {
goalsFactory.add(FitnessFunctions.getFitnessFactory(Properties.CRITERION[i]));
}
return goalsFactory;
}
/**
*
* main
*
*
* @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);
}
}