![JAR search and dependency download from the Maven repository](/logo.png)
org.evosuite.regression.RegressionSuiteMinimizer Maven / Gradle / Ivy
The newest version!
/**
* 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.regression;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.evosuite.Properties;
import org.evosuite.TimeController;
import org.evosuite.assertion.Assertion;
import org.evosuite.assertion.InspectorAssertion;
import org.evosuite.assertion.OutputTrace;
import org.evosuite.ga.ConstructionFailedException;
import org.evosuite.rmi.ClientServices;
import org.evosuite.statistics.RuntimeVariable;
import org.evosuite.testcase.TestCaseMinimizer;
import org.evosuite.testcase.TestChromosome;
import org.evosuite.testcase.TestFactory;
import org.evosuite.testcase.execution.ExecutionResult;
import org.evosuite.testcase.statements.MethodStatement;
import org.evosuite.testcase.statements.Statement;
import org.evosuite.testsuite.TestSuiteChromosome;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RegressionSuiteMinimizer {
private transient final static Logger logger = LoggerFactory
.getLogger(RegressionSuiteMinimizer.class);
private RegressionAssertionGenerator
regressionAssertionGenerator = new RegressionAssertionGenerator();
public void minimize(TestSuiteChromosome suite) {
track(RuntimeVariable.Result_Size, suite.size());
track(RuntimeVariable.Result_Length, suite.totalLengthOfTestCases());
track(RuntimeVariable.RSM_OverMinimized, 0);
logger.warn("Going to minimize test suite. Length: {} ", suite.totalLengthOfTestCases());
logger.debug("suite: \n{}", suite);
RegressionTestSuiteChromosome regressionSuite = new RegressionTestSuiteChromosome();
regressionSuite.addTests(suite.clone().getTestChromosomes());
// Seems to be broken:
// removeUnusedVariables(regressionSuite);
executeSuite(regressionSuite);
removeDuplicateAssertions(regressionSuite);
removeDuplicateExceptions(regressionSuite);
removePassingTests(regressionSuite);
int testCount = regressionSuite.size();
minimizeSuite(regressionSuite);
executeSuite(regressionSuite);
removePassingTests(regressionSuite);
sendStats(regressionSuite);
// Sanity check
if (regressionSuite.size() == 0 && testCount > 0) {
track(RuntimeVariable.RSM_OverMinimized, 1);
logger.error("Test suite over-minimized. Returning non-minimized suite.");
} else {
// Adding tests back to the original test suite (if minimization didn't remove all tests)
suite.clearTests();
for (TestChromosome t : regressionSuite.getTestChromosomes()) {
RegressionTestChromosome rtc = (RegressionTestChromosome) t;
suite.addTest(rtc.getTheTest());
}
}
logger.warn("Minimized Length: {} ", suite.totalLengthOfTestCases());
logger.debug("suite: \n{}", suite);
}
private void sendStats(RegressionTestSuiteChromosome regressionSuite) {
int assCount = 0;
assCount = numFailingAssertions(regressionSuite);
track(RuntimeVariable.Generated_Assertions, assCount);
track(RuntimeVariable.Minimized_Size, regressionSuite.size());
track(RuntimeVariable.Minimized_Length, regressionSuite.totalLengthOfTestCases());
}
private void executeSuite(RegressionTestSuiteChromosome regressionSuite) {
for (TestChromosome chromosome : regressionSuite.getTestChromosomes()) {
RegressionTestChromosome c = (RegressionTestChromosome) chromosome;
try {
executeTest(c);
} catch (Throwable t) {
logger.error("Test execution failed. See stack trace.");
t.printStackTrace();
}
}
}
/**
* Execute regression test case on both versions
*
* @param regressionTest regression test chromosome to be executed on both versions
*/
private void executeTest(RegressionTestChromosome regressionTest) {
TestChromosome testChromosome = regressionTest.getTheTest();
TestChromosome otherChromosome = regressionTest.getTheSameTestForTheOtherClassLoader();
ExecutionResult result = regressionAssertionGenerator
.runTest(testChromosome.getTestCase());
ExecutionResult otherResult = regressionAssertionGenerator
.runTest(otherChromosome.getTestCase());
regressionTest.setLastExecutionResult(result);
regressionTest.setLastRegressionExecutionResult(otherResult);
testChromosome.setLastExecutionResult(result);
otherChromosome.setLastExecutionResult(otherResult);
}
private void removeDuplicateAssertions(RegressionTestSuiteChromosome suite) {
Iterator it = suite.getTestChromosomes().iterator();
Map> uniqueAssertions = new HashMap>();
// int i = -1;
while (it.hasNext()) {
// i++;
RegressionTestChromosome test = (RegressionTestChromosome) it.next();
boolean changed = false;
boolean hadAssertion = false;
// keep track of new unique assertions, and if not unique, remove the assertion
for (Assertion a : test.getTheTest().getTestCase().getAssertions()) {
String aClass = a.getClass().getSimpleName();
List aTypes = uniqueAssertions.get(aClass);
if (aTypes == null) {
aTypes = new ArrayList();
}
String aType = "";
if (a instanceof InspectorAssertion) {
InspectorAssertion ia = (InspectorAssertion) a;
try {
aType = ia.getInspector().getMethod().getName();
} catch (NullPointerException e) {
// technically this should not happen
Statement s = ia.getStatement();
if (s instanceof MethodStatement) {
aType = ((MethodStatement) s).getMethod().getName();
}
}
}
if (aTypes.contains(aType)) {
// logger.warn("removing non-unique assertion: {}-{}", aClass, aType);
changed = true;
a.getStatement().getPosition();
test.getTheTest().getTestCase().removeAssertion(a);
continue;
}
aTypes.add(aType);
uniqueAssertions.put(aClass, aTypes);
hadAssertion = true;
}
if (changed) {
test.updateClassloader();
}
}
if (uniqueAssertions.size() > 0) {
logger.warn("unique assertions: {}", uniqueAssertions);
}
}
private void removeDuplicateExceptions(RegressionTestSuiteChromosome suite) {
Set uniqueExceptions = new HashSet<>();
Map exceptionStatementMapping = new HashMap<>();
List chromosomes = suite.getTestChromosomes();
for (int i = 0; i < chromosomes.size(); i++) {
RegressionTestChromosome test = (RegressionTestChromosome) chromosomes.get(i);
boolean changed = false;
boolean hadAssertion = test.getTheTest().getTestCase().getAssertions().size() > 0;
ExecutionResult resultA = test.getLastExecutionResult();
ExecutionResult resultB = test.getLastRegressionExecutionResult();
// If for some reason we haven't executed the test suite yet, or previous execution failed
// re-execute the tests and check
if (resultA == null || resultB == null) {
executeTest(test);
resultA = test.getLastExecutionResult();
resultB = test.getLastRegressionExecutionResult();
if (resultA == null || resultB == null) {
continue;
}
}
Map exceptionMapA = resultA.getCopyOfExceptionMapping();
Map exceptionMapB = resultB.getCopyOfExceptionMapping();
// logger.warn("Test{} - had exceptions? {} {} - {} {}",i,
// resultA.noThrownExceptions(), resultB.noThrownExceptions(),
// exceptionMapA.size(), exceptionMapB.size());
if (!resultA.noThrownExceptions() || !resultB.noThrownExceptions()) {
double exDiff = RegressionExceptionHelper
.compareExceptionDiffs(exceptionMapA, exceptionMapB);
logger.warn("Test{} - Difference in number of exceptions: {}", i, exDiff);
if (exDiff > 0) {
/*
* Three scenarios:
* 1. Same exception, different messages
* 2. Different exception in A
* 3. Different exception in B
*/
for (Entry ex : exceptionMapA.entrySet()) {
Throwable exA = ex.getValue();
// unique statement key over all tests (to avoid removing the same exception twice)
String exKey = i + ":" + ex.getKey();
// exceptionA signatures
String exception =
RegressionExceptionHelper.simpleExceptionName(test, ex.getKey(), exA);
String exSignatureA =
RegressionExceptionHelper.getExceptionSignature(exA, Properties.TARGET_CLASS);
String signaturePair = exSignatureA + ",";
Throwable exB = exceptionMapB.get(ex.getKey());
if (exB != null) { // if the same statement in B also throws an exception
// exceptionB signatures
String exceptionB = RegressionExceptionHelper
.simpleExceptionName(test, ex.getKey(), exB);
String exSignatureB =
RegressionExceptionHelper.getExceptionSignature(exA, Properties.TARGET_CLASS);
signaturePair += exSignatureB;
if (exception.equals(exceptionB) || exSignatureA.equals(exSignatureB)) {
// We will be taking care of removing this exception when checking from A to B
// so there isn't a need to check again from B to A
exceptionMapB.remove(ex.getKey());
}
}
logger.warn("Test{}, uniqueExceptions: {}", i, uniqueExceptions);
logger.warn("checking exception: {} at {}", exception, ex.getKey());
List signatures = Arrays.asList(exception, signaturePair);
for (String sig : signatures) {
// Compare exceptions on the merit of their message or signature
if (uniqueExceptions.contains(sig)
&& exceptionStatementMapping.get(sig) != exKey
&& !hadAssertion) {
TestChromosome originalTestChromosome = (TestChromosome) test
.getTheTest().clone();
try {
TestFactory testFactory = TestFactory.getInstance();
testFactory
.deleteStatementGracefully(test.getTheTest().getTestCase(), ex.getKey());
test.getTheTest().setChanged(true);
logger.warn("Removed exceptionA throwing line {}", ex.getKey());
} catch (ConstructionFailedException e) {
test.getTheTest().setChanged(false);
test.getTheTest().setTestCase(originalTestChromosome.getTestCase());
logger.error("ExceptionA deletion failed");
continue;
}
changed = true;
break;
} else {
uniqueExceptions.add(sig);
exceptionStatementMapping.put(sig, exKey);
}
}
}
// Check from the other way around (B to A)
for (Entry ex : exceptionMapB.entrySet()) {
String exception =
RegressionExceptionHelper.simpleExceptionName(test, ex.getKey(), ex.getValue());
String exKey = i + ":" + ex.getKey();
logger.warn("Test{}, uniqueExceptions: {}", i, uniqueExceptions);
logger.warn("checking exceptionB: {} at {}", exception, ex.getKey());
if (uniqueExceptions.contains(exception)
&& exceptionStatementMapping.get(exception) != exKey
&& !hadAssertion
&& test.getTheTest().getTestCase().hasStatement(ex.getKey())) {
TestChromosome originalTestChromosome = (TestChromosome) test.getTheTest().clone();
try {
TestFactory testFactory = TestFactory.getInstance();
logger.warn("removing statementB: {}",
test.getTheTest().getTestCase().getStatement(ex.getKey()));
testFactory
.deleteStatementGracefully(test.getTheTest().getTestCase(), ex.getKey());
test.getTheTest().setChanged(true);
logger.warn("removed exceptionB throwing line {}", ex.getKey());
} catch (ConstructionFailedException e) {
test.getTheTest().setChanged(false);
test.getTheTest().setTestCase(originalTestChromosome.getTestCase());
logger.error("ExceptionB deletion failed");
continue;
}
changed = true;
} else {
uniqueExceptions.add(exception);
exceptionStatementMapping.put(exception, exKey);
}
}
}
}
if (changed) {
test.updateClassloader();
executeTest(test);
i--;
}
}
if (uniqueExceptions.size() > 0) {
logger.warn("unique exceptions: {}", uniqueExceptions);
}
}
private int numFailingAssertions(RegressionTestSuiteChromosome suite) {
int count = 0;
for (TestChromosome testChromosome : suite.getTestChromosomes()) {
RegressionTestChromosome test = (RegressionTestChromosome) testChromosome;
count += numFailingAssertions(test);
}
return count;
}
private int numFailingAssertions(RegressionTestChromosome test) {
int count = 0;
Set invalidAssertions = new HashSet<>();
ExecutionResult resultA = test.getLastExecutionResult();
ExecutionResult resultB = test.getLastRegressionExecutionResult();
for (Assertion assertion : test.getTheSameTestForTheOtherClassLoader().getTestCase()
.getAssertions()) {
for (OutputTrace> outputTrace : resultA.getTraces()) {
if (outputTrace.isDetectedBy(assertion)) {
logger.error("shouldn't be happening: assertion was failing on original version");
invalidAssertions.add(assertion);
break;
}
}
for (OutputTrace> outputTrace : resultB.getTraces()) {
if (outputTrace.isDetectedBy(assertion) && !invalidAssertions.contains(assertion)) {
count++;
break;
}
}
}
if (invalidAssertions.size() != 0) {
logger.warn("{} invalid assertion(s) to be removed", invalidAssertions);
for (Assertion assertion : invalidAssertions) {
test.getTheTest().getTestCase().removeAssertion(assertion);
test.getTheTest().setChanged(true);
test.updateClassloader();
}
}
int exDiffCount = 0;
if (resultA != null && resultB != null) {
exDiffCount = (int) RegressionExceptionHelper
.compareExceptionDiffs(resultA.getCopyOfExceptionMapping(),
resultB.getCopyOfExceptionMapping());
// logger.warn("exDiffCount: {}: \nmap1:{}\nmap2:{}\n",exDiffCount,resultA.getCopyOfExceptionMapping(),
// resultB.getCopyOfExceptionMapping());
count += exDiffCount;
// logger.warn("adding exception comment for test:\n{}",test.getTheTest());
// logger.wran("test{}")
if (exDiffCount > 0 && !test.exCommentsAdded) {
// logger.warn("Adding Exception Comments for test: \nmap1:{}\nmap2:{}\n",resultA.getCopyOfExceptionMapping(),
// resultB.getCopyOfExceptionMapping());
RegressionExceptionHelper.addExceptionAssertionComments(test,
resultA.getCopyOfExceptionMapping(),
resultB.getCopyOfExceptionMapping());
test.exCommentsAdded = true;
}
} else {
logger.error("resultA: {} | resultB: {}", resultA, resultB);
}
// logger.warn("{} assertions", count);
// test.assertionCount = count;
// test.exAssertionCount = exDiffCount;
return count;
}
private void removePassingTests(RegressionTestSuiteChromosome suite) {
Iterator it = suite.getTestChromosomes().iterator();
int i = 0;
while (it.hasNext()) {
i++;
RegressionTestChromosome test = (RegressionTestChromosome) it.next();
if (numFailingAssertions(test) == 0) {
logger.warn("Removing test {}: no assertions", (i - 1));
it.remove();
}
}
}
private void minimizeSuite(RegressionTestSuiteChromosome suite) {
// logger.warn("minimizeSuite:\n{}", suite);
Iterator it = suite.getTestChromosomes().iterator();
int testCount = 0;
while (it.hasNext()) {
if (isTimeoutReached()) {
logger.warn("minimization timeout reached. skipping minimization");
break;
}
// logger.warn("########################## TEST{} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%",
// testCount);
RegressionTestChromosome test = (RegressionTestChromosome) it.next();
// iterate over test statements (from end to beginning), and remove them one by one,
// see if the assertion is still failing after removing the statement
for (int i = test.getTheTest().size() - 1; i >= 0; i--) {
if (isTimeoutReached()) {
logger.warn("minimization timeout reached. skipping minimization");
break;
}
logger.debug("Current size: " + suite.size() + "/" + suite.totalLengthOfTestCases());
logger.debug(
"Deleting statement {} " + test.getTheTest().getTestCase().getStatement(i).getCode()
+ " from test", i);
TestChromosome originalTestChromosome = (TestChromosome) test.getTheTest().clone();
executeTest(test);
/*
* if(test.getLastExecutionResult()==null ||
* test.getLastRegressionExecutionResult()==null){
* logger.error("test execution result was null"); //continue; }
*/
// originalTestChromosome.setLastExecutionResult(test.getLastExecutionResult());
int preRemovalAssertions = numFailingAssertions(test);
try {
TestFactory testFactory = TestFactory.getInstance();
testFactory.deleteStatementGracefully(test.getTheTest().getTestCase(), i);
test.getTheTest().setChanged(true);
} catch (ConstructionFailedException e) {
test.getTheTest().setChanged(false);
test.getTheTest().setTestCase(originalTestChromosome.getTestCase());
logger.error("Deleting failed");
continue;
}
RegressionTestChromosome rtc = new RegressionTestChromosome();
rtc.setTest((TestChromosome) test.getTheTest().clone());
// rtc.updateClassloader();
executeTest(rtc);
int postRemovalAssertions = numFailingAssertions(rtc);
// logger.warn("Pre-Removal Assertions: {} | Post-Removal Assertions: {}",
// preRemovalAssertions, postRemovalAssertions);
if (postRemovalAssertions == preRemovalAssertions) {
test.updateClassloader();
continue; // the change had no effect
} else {
// Restore previous state
logger.debug(
"Can't remove statement " + originalTestChromosome.getTestCase().getStatement(i)
.getCode());
test.getTheTest().setTestCase(originalTestChromosome.getTestCase());
test.getTheTest().setLastExecutionResult(originalTestChromosome.getLastExecutionResult());
test.getTheTest().setChanged(false);
}
}
test.updateClassloader();
if (test.getTheTest().isChanged()) {
executeTest(test);
}
testCount++;
}
}
/*
* "borrowed" from TestCaseMinimizer
*/
private void removeUnusedVariables(RegressionTestSuiteChromosome suite) {
for (TestChromosome testChromosome : suite.getTestChromosomes()) {
RegressionTestChromosome test = (RegressionTestChromosome) testChromosome;
boolean changed = TestCaseMinimizer.removeUnusedVariables(test.getTheTest().getTestCase());
if (changed) {
test.updateClassloader();
executeSuite(suite);
}
}
}
/*
* "borrowed" from TestSuiteMinimizer
*/
private boolean isTimeoutReached() {
return !TimeController.getInstance().isThereStillTimeInThisPhase();
}
/**
* Helper for tracking output values
*/
private void track(RuntimeVariable variable, Object value) {
ClientServices.getInstance().getClientNode().trackOutputVariable(variable, value);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy