All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.evosuite.testcase.localsearch.DSETestCaseLocalSearch 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.testcase.localsearch;

import java.util.HashSet;
import java.util.Set;

import org.evosuite.Properties;
import org.evosuite.ga.localsearch.LocalSearchBudget;
import org.evosuite.ga.localsearch.LocalSearchObjective;
import org.evosuite.testcase.TestCase;
import org.evosuite.testcase.TestChromosome;
import org.evosuite.testcase.execution.ExecutionTrace;
import org.evosuite.testsuite.TestSuiteChromosome;

/**
 * Applies DSE on a given test case. If the test case belongs to a suite, it
 * should be provided by using the constructor that receives a suite chromosome.
 * 
 * If the test case has no symbolic variables (or these variables are not
 * reached due to a thrown exception during test execution), or it does not
 * reach an uncovered branch, DSE is skipped on this test case.
 * 
 * @author galeotti
 */
public class DSETestCaseLocalSearch extends TestCaseLocalSearch {

	/**
	 * Returns true iff the test reaches a decision (if/while) with an uncovered
	 * branch
	 * 
	 * @param test
	 * @param uncoveredBranches
	 * @return
	 */
	private static boolean hasUncoveredBranch(TestChromosome test, Set uncoveredBranches) {
		Set testCoveredBranches = getCoveredBranches(test);
		for (Branch b : testCoveredBranches) {
			Branch negate = b.negate();
			if (uncoveredBranches.contains(negate)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Represents a branch in the target program
	 * 
	 * @author galeotti
	 */
	private static class Branch {

		public Branch(int branchIndex, boolean isTrueBranch) {
			super();
			this.branchIndex = branchIndex;
			this.isTrueBranch = isTrueBranch;
		}

		private int branchIndex;

		private boolean isTrueBranch;

		public Branch negate() {
			return new Branch(branchIndex, !isTrueBranch);
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + branchIndex;
			result = prime * result + (isTrueBranch ? 1231 : 1237);
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Branch other = (Branch) obj;
			if (branchIndex != other.branchIndex)
				return false;
			if (isTrueBranch != other.isTrueBranch)
				return false;
			return true;
		}

		@Override
		public String toString() {
			return "Branch [branchIndex=" + branchIndex + ", isTrueBranch=" + isTrueBranch + "]";
		}

	}

	private final TestSuiteChromosome suite;

	/**
	 * Creates a DSE local search with no whole test suite
	 */
	public DSETestCaseLocalSearch() {
		this(null);
	}

	/**
	 * Creates a DSE local search for a test case that belongs to a whole test
	 * suite
	 * 
	 * @param suite
	 */
	public DSETestCaseLocalSearch(TestSuiteChromosome suite) {
		this.suite = suite;
	}

	/**
	 * Returns those branches that are reached but are not covered
	 * 
	 * @param coveredBranches
	 * @return
	 */
	private static Set collectUncoveredBranches(Set coveredBranches) {
		Set uncoveredBranches = new HashSet();
		for (Branch b : coveredBranches) {
			final Branch negate = b.negate();
			if (!coveredBranches.contains(negate)) {
				uncoveredBranches.add(negate);
			}
		}
		return uncoveredBranches;
	}

	/**
	 * Applies DSE on a test case using the passed local search objective. The
	 * local search modifies the test chromosome, it cannot create a
	 * fresh new test chromosome. If the test case has no symbolic variables, or
	 * it does not reach an uncovered branch, DSE is skipped.
	 * 
	 * @param test
	 *            the test case chromosome.
	 * 
	 * @param objective
	 *            the fitness functions for the test chromosome
	 */
	@Override
	public boolean doSearch(TestChromosome test, LocalSearchObjective objective) {
		logger.info("Test before local search: " + test.getTestCase().toCode());

		// gather covered branches true/false branch indexes

		final Set coveredBranches;
		if (suite != null) {
			coveredBranches = collectCoveredBranches(suite);
		} else {
			coveredBranches = getCoveredBranches(test);
		}
		final Set uncoveredBranches = collectUncoveredBranches(coveredBranches);

		if (uncoveredBranches.isEmpty()) {
			/*
			 * As there are no branches uncovered (true or false branch missing)
			 * in this suite, there is no point in continuing DSE. Therefore, we
			 * should stop DSE (no DSE improvement)
			 */
			return false;
		}
 
		if (!hasUncoveredBranch(test, uncoveredBranches)) {
			/*
			 * As there are uncovered branches, but none is reached by this
			 * test, the DSE is skipped and we return false (no DSE improvement)
			 */
			return false;
		}

		Set targetStatementIndexes = collectStatementIndexesWithSymbolicVariables(test, objective);

		final boolean fitnessHasImproved;
		if (targetStatementIndexes.isEmpty()) {
			// Cannot apply DSE because there are no symbolic variables
			// Therefore, no improvement on objective.
			fitnessHasImproved = false;
		} else {
			logger.info("Yes, now applying the search at positions {}!", targetStatementIndexes);
			DSETestGenerator dseTestGenerator;
			if (suite != null) {
				dseTestGenerator = new DSETestGenerator(suite);
			} else {
				dseTestGenerator = new DSETestGenerator();
			}
			final TestChromosome newTest = dseTestGenerator.generateNewTest(test, targetStatementIndexes, objective);
			if (newTest != null) {
				fitnessHasImproved = true;
			} else {
				fitnessHasImproved = false;
			}
		}

		LocalSearchBudget.getInstance().countLocalSearchOnTest();

		// Return true iff search was successful
		return fitnessHasImproved;

		// TODO: Handle arrays in local search
		// TODO: mutating an int might have an effect on array lengths
	}

	/**
	 * Collects a set with the indexes of those positions that have symbolic
	 * variables. If the symbolic variable is beyond a statement that throws an
	 * exception, it is ignored.
	 * 
	 * @param testChromosome
	 *            the test case to apply DSE
	 * @param localSearchObjective
	 *            the objective to measure fitness improvement
	 * @return a set with statement indexes in the test case with symbolic
	 *         variables
	 */
	private static Set collectStatementIndexesWithSymbolicVariables(TestChromosome testChromosome,
			LocalSearchObjective localSearchObjective) {
		// Only apply local search up to the point where an exception was thrown
		// TODO: Check whether this conflicts with test expansion
		int lastPosition = testChromosome.size() - 1;
		if (testChromosome.getLastExecutionResult() != null && !testChromosome.isChanged()) {
			Integer lastPos = testChromosome.getLastExecutionResult().getFirstPositionOfThrownException();
			if (lastPos != null)
				lastPosition = lastPos.intValue();
		}
		TestCase test = testChromosome.getTestCase();
		Set targetStatementIndexes = new HashSet();

		// We count down to make the code work when lines are
		// added during the search (see NullReferenceSearch).

		for (int i = lastPosition; i >= 0; i--) {
			if (LocalSearchBudget.getInstance().isFinished())
				break;

			if (localSearchObjective.isDone()) {
				break;
			}

			if (i >= testChromosome.size()) {
				logger.warn("Test size decreased unexpectedly during local search, aborting local search");
				logger.warn(testChromosome.getTestCase().toCode());
				break;
			}
			final Class targetClass = Properties.getTargetClassAndDontInitialise();

			if (!test.hasReferences(test.getStatement(i).getReturnValue())
					&& !test.getStatement(i).getReturnClass().equals(targetClass)) {
				logger.info(
						"Return value of statement " + i + " is not referenced and not SUT, not doing local search");
				continue;
			}

			targetStatementIndexes.add(i);

		}
		return targetStatementIndexes;
	}

	/**
	 * Returns the set covered branches by this suite
	 * 
	 * @param suite
	 * @return
	 */
	private static Set collectCoveredBranches(TestSuiteChromosome suite) {
		final Set suiteCoveredBranches = new HashSet();
		for (TestChromosome test : suite.getTestChromosomes()) {
			final Set testCoveredBranches = getCoveredBranches(test);
			suiteCoveredBranches.addAll(testCoveredBranches);
		}
		return suiteCoveredBranches;

	}

	/**
	 * Returns the set of covered branches by this test
	 * 
	 * @param test
	 * @return
	 */
	private static Set getCoveredBranches(TestChromosome test) {
		final Set testCoveredBranches = new HashSet();

		ExecutionTrace trace = test.getLastExecutionResult().getTrace();
		{
			Set coveredTrueBranchIndexesInTrace = trace.getCoveredTrueBranches();
			for (Integer branchIndex : coveredTrueBranchIndexesInTrace) {
				Branch b = new Branch(branchIndex, true);
				testCoveredBranches.add(b);
			}
		}
		{

			Set coveredFalseBranchIndexesInTrace = trace.getCoveredFalseBranches();
			for (Integer branchIndex : coveredFalseBranchIndexesInTrace) {
				Branch b = new Branch(branchIndex, false);
				testCoveredBranches.add(b);
			}
		}
		return testCoveredBranches;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy