Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* 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.junit.naming.methods;
import org.evosuite.Properties;
import org.evosuite.coverage.FitnessFunctions;
import org.evosuite.coverage.TestFitnessFactory;
import org.evosuite.coverage.exception.ExceptionCoverageTestFitness;
import org.evosuite.coverage.io.input.InputCoverageTestFitness;
import org.evosuite.coverage.io.input.InputObserver;
import org.evosuite.coverage.method.MethodCoverageTestFitness;
import org.evosuite.coverage.method.MethodNoExceptionCoverageTestFitness;
import org.evosuite.coverage.io.output.OutputCoverageTestFitness;
import org.evosuite.coverage.io.output.OutputObserver;
import org.evosuite.runtime.mock.EvoSuiteMock;
import org.evosuite.testcase.TestCase;
import org.evosuite.testcase.TestFitnessFunction;
import org.evosuite.testcase.execution.ExecutionObserver;
import org.evosuite.testcase.execution.ExecutionResult;
import org.evosuite.testcase.execution.TestCaseExecutor;
import org.evosuite.testcase.statements.ConstructorStatement;
import org.evosuite.testcase.statements.MethodStatement;
import org.evosuite.testcase.statements.Statement;
import org.objectweb.asm.Type;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author Gordon Fraser
* @author Ermira Daka
*/
public class CoverageGoalTestNameGenerationStrategy implements TestNameGenerationStrategy {
private Map testToName = new LinkedHashMap<>();
private Map> methodCount = new LinkedHashMap<>();
public static final String PREFIX = "test";
public static final String STR_CREATE = "Creates";
public static final String STR_CREATE_EXCEPTION = "FailsToCreate";
public static final String STR_THROWS = "Throws";
public static final String STR_WITH = "With";
public static final String STR_WHERE = "Where";
public static final String STR_IS = "Is";
public static final String STR_AND = "And";
public static final String STR_TAKING = "Taking";
public static final String STR_WITHOUT = "TakingNo";
public static final String STR_RETURNS = "Returning";
public static final String STR_ARGUMENTS = "Arguments";
public static final int MAX_SIMILAR_GOALS = 2;
public static final int MAX_CHARS = 70;
public CoverageGoalTestNameGenerationStrategy(List testCases, List results) {
addGoalsNotIncludedInTargetCriteria(results);
Map> testToGoals = initializeCoverageMapFromResults(results);
generateNames(testToGoals);
}
/**
* This assumes all goals are already saved in the tests
* @param testCases
*/
public CoverageGoalTestNameGenerationStrategy(List testCases) {
Map> testToGoals = initializeCoverageMapFromTests(testCases);
generateNames(testToGoals);
}
/**
* Initially, set to the top level goals
*
* @param testToGoals
*/
private void initializeNameGoals(Map> testToGoals) {
// Start off with only the top goals and then iteratively add more goals
for(Map.Entry> entry : testToGoals.entrySet()) {
Set goals = new LinkedHashSet<>();
List topGoals = getTopGoals(entry.getValue());
if(topGoals.isEmpty()) {
// No goals
}
else if(topGoals.size() <= MAX_SIMILAR_GOALS) {
// We can take up to 2 goals explicitly
for(TestFitnessFunction goal : topGoals) {
goals.add(goal);
String goalName = getTestName(entry.getKey(), goals);
if(goalName.length() > MAX_CHARS) {
goals.remove(goal);
break;
}
}
} else {
// If there are more than 2 goals, we have to select something
goals.add(chooseRepresentativeGoal(entry.getKey(), topGoals));
}
testToGoals.put(entry.getKey(), goals);
}
}
/**
* Calculate the test names from the current goals
*
* @param testToGoals
*/
private void setTestNames(Map> testToGoals) {
for (Map.Entry> entry : testToGoals.entrySet()) {
testToName.put(entry.getKey(), getTestName(entry.getKey(), entry.getValue()));
}
}
/**
* Helper method that does the bulk of the work
* @param testToGoals
*/
private void generateNames(Map> testToGoals) {
initializeMethodCoverageCount(testToGoals);
findUniqueGoals(testToGoals);
initializeNameGoals(testToGoals);
// Iteratively add goals as long as there are resolvable ambiguities
boolean changed = true;
while(changed) {
setTestNames(testToGoals);
Map> dupTestNameMap = determineDuplicateNames();
// For each duplicate set, add new test goals
changed = false;
for (Map.Entry> entry : dupTestNameMap.entrySet()) {
if(entry.getKey().length() >= MAX_CHARS) {
continue;
}
// Try adding something unique for the given test set
if(resolveAmbiguity(testToGoals, entry.getValue(), true))
changed = true;
else {
// If there is nothing unique, try using something that reduces
// the number of tests in this ambiguity group
if(resolveAmbiguity(testToGoals, entry.getValue(), false))
changed = true;
}
}
}
// If there is absolutely nothing unique, add the top goals so that the test at least has a name
for (Map.Entry> entry : testToGoals.entrySet()) {
if(entry.getValue().isEmpty()) {
List goals = new ArrayList<>(getUniqueNonMethodGoals(entry.getKey(), testToGoals));
if(goals.isEmpty()) {
goals.addAll(filterSupportedGoals(entry.getKey().getCoveredGoals()));
}
Collections.sort(goals, new GoalComparator());
if(!goals.isEmpty()) {
TestFitnessFunction goal = goals.iterator().next();
entry.getValue().add(goal);
String name = getTestName(entry.getKey(), entry.getValue());
testToName.put(entry.getKey(), name);
}
}
}
// Add numbers to remaining duplicate names
fixAmbiguousTestNames();
}
/**
* There is nothing unique about the test, so we have to add goals until we find a unique name
*/
private Set getUniqueNonMethodGoals(TestCase test, Map> testToGoals) {
Set allGoals = new LinkedHashSet<>();
for (Set goals : testToGoals.values()) {
allGoals.addAll(goals);
}
Set goals = new LinkedHashSet<>();
for(TestFitnessFunction goal : filterSupportedGoals(test.getCoveredGoals())) {
if(goal instanceof MethodCoverageTestFitness)
continue;
if(goal instanceof MethodNoExceptionCoverageTestFitness)
continue;
if(!allGoals.contains(goal)) {
goals.add(goal);
}
}
return goals;
}
/**
* Add additional goals for tests to testsToGoals to make them distinguishable
*
* @param testToGoals
* @param tests
* @param unique
* @return
*/
private boolean resolveAmbiguity(Map> testToGoals, Set tests, boolean unique) {
// Full list of goals for given tests
Map> fullTestToGoals = new LinkedHashMap<>();
for(TestCase test : tests) {
Set goals = filterSupportedGoals(new LinkedHashSet<>(test.getCoveredGoals()));
goals.removeAll(testToGoals.get(test)); // Remove those already used to name the test
fullTestToGoals.put(test, goals);
}
// Find out what is unique about each one, or at least helps reduce the number of ambiguous names
if(unique)
findUniqueGoals(fullTestToGoals);
else
findNonUbiquitousGoals(fullTestToGoals);
// Select the next best goal from the new unique goal sets
boolean added = false;
for(TestCase test : tests) {
List topGoals = getTopGoals(fullTestToGoals.get(test));
if(topGoals.isEmpty()) {
continue;
} else if(topGoals.size() > MAX_SIMILAR_GOALS) {
TestFitnessFunction newGoal = chooseRepresentativeGoal(test, topGoals);
Set newGoals = new LinkedHashSet<>(testToGoals.get(test));
newGoals.add(newGoal);
String newName = getTestName(test, newGoals);
if(newName.length() < MAX_CHARS) {
if (testToGoals.get(test).add(newGoal))
added = true;
}
} else {
Set newGoals = new LinkedHashSet<>(testToGoals.get(test));
Iterator iterator = topGoals.iterator();
String newName = testToName.get(test); //getTestName(test, newGoals);
while(newName.length() < MAX_CHARS && iterator.hasNext()) {
TestFitnessFunction newGoal = iterator.next();
newGoals.add(newGoal);
newName = getTestName(test, newGoals);
if(testToGoals.get(test).add(newGoal))
added = true;
}
}
}
return added;
}
/**
* Builds the name map based on coverage goal stored as covered in each of the tests
* @param tests
* @return
*/
private Map> initializeCoverageMapFromTests(List tests) {
Map> testToGoals = new LinkedHashMap<>();
for(TestCase test : tests) {
testToGoals.put(test, filterSupportedGoals(new LinkedHashSet<>(test.getCoveredGoals())));
}
return testToGoals;
}
/**
* Builds the name map based on coverage goals stored as covered in the tests pointed to by results
* @param results
* @return
*/
private Map> initializeCoverageMapFromResults(List results) {
Map> testToGoals = new LinkedHashMap<>();
for(ExecutionResult result : results) {
testToGoals.put(result.test, filterSupportedGoals(new LinkedHashSet<>(result.test.getCoveredGoals())));
}
return testToGoals;
}
/**
* Name generation assumes that certain coverage criteria are included. If we haven't targeted them yet,
* we need to determine the covered goals. This may require re-executing the tests with observers.
*
* @param results
*/
private void addGoalsNotIncludedInTargetCriteria(List results) {
List requiredCriteria = new ArrayList<>(Arrays.asList(new Properties.Criterion[] { Properties.Criterion.OUTPUT, Properties.Criterion.INPUT, Properties.Criterion.METHOD, Properties.Criterion.METHODNOEXCEPTION, Properties.Criterion.EXCEPTION}));
requiredCriteria.removeAll(Arrays.asList(Properties.CRITERION));
results = getUpdatedResults(requiredCriteria, results);
for(Properties.Criterion c : requiredCriteria) {
TestFitnessFactory extends TestFitnessFunction> goalFactory = FitnessFunctions.getFitnessFactory(c);
List extends TestFitnessFunction> goals = goalFactory.getCoverageGoals();
for(ExecutionResult result : results) {
for(TestFitnessFunction goal : goals) {
if(goal.isCovered(result))
result.test.addCoveredGoal(goal);
}
}
}
}
/**
* Some criteria require re-execution with observers. Make sure the results are up-to-date
*
* @param requiredCriteria
* @param origResults
* @return
*/
private List getUpdatedResults(List requiredCriteria, List origResults) {
List newObservers = new ArrayList();
if(requiredCriteria.contains(Properties.Criterion.INPUT)) {
newObservers.add(new InputObserver());
}
if(requiredCriteria.contains(Properties.Criterion.OUTPUT)) {
newObservers.add(new OutputObserver());
}
if(newObservers.isEmpty()) {
return origResults;
}
for(ExecutionObserver observer : newObservers)
TestCaseExecutor.getInstance().addObserver(observer);
List newResults = new ArrayList();
for(ExecutionResult result : origResults) {
ExecutionResult newResult = TestCaseExecutor.getInstance().runTest(result.test);
newResults.add(newResult);
}
for(ExecutionObserver observer : newObservers)
TestCaseExecutor.getInstance().removeObserver(observer);
return newResults;
}
/** We use only a subset of the possible criteria to determine names */
private List> supportedClasses = Arrays.asList(new Class> [] { MethodCoverageTestFitness.class, MethodNoExceptionCoverageTestFitness.class,
ExceptionCoverageTestFitness.class, OutputCoverageTestFitness.class, InputCoverageTestFitness.class});
/**
* Remove any goals that are irrelevant for name generation
*
* @param goals
* @return
*/
private Set filterSupportedGoals(Set goals) {
return goals.stream().filter(c -> supportedClasses.contains(c.getClass())).collect(Collectors.toSet());
}
/**
* Determine if we have overloaded methods, which requires the use of signatures
*
* @param testToGoals
*/
private void initializeMethodCoverageCount(Map> testToGoals) {
for(Set goals : testToGoals.values()) {
for(TestFitnessFunction goal : goals) {
String methodName = getMethodNameWithoutDescriptor(goal.getTargetMethod());
if(!methodCount.containsKey(methodName)) {
methodCount.put(methodName, new LinkedHashSet<>());
}
methodCount.get(methodName).add(goal.getTargetMethod());
}
}
}
/**
* Determine for each test the set of coverage goals uniquely covered by this test
*
* @param testToGoals
*/
private static void findUniqueGoals(Map> testToGoals) {
// Could be optimised
Map> goalMapCopy = new LinkedHashMap<>();
for(Map.Entry> entry : testToGoals.entrySet()) {
Set goalSet = new LinkedHashSet(entry.getValue());
for(Map.Entry> otherEntry : testToGoals.entrySet()) {
if(entry == otherEntry)
continue;
goalSet.removeAll(otherEntry.getValue());
}
goalMapCopy.put(entry.getKey(), goalSet);
}
testToGoals.clear();
testToGoals.putAll(goalMapCopy);
}
/**
* Determine for each test the set of coverage goals that remain if we remove those that are covered by all tests
*
* @param testToGoals
*/
private static void findNonUbiquitousGoals(Map> testToGoals) {
// Could be optimised
Map> goalMapCopy = new LinkedHashMap<>();
Set commonGoals = new LinkedHashSet<>();
// Get the superset of goals
for(Map.Entry> entry : testToGoals.entrySet()) {
commonGoals.addAll(entry.getValue());
}
// Now only keep stuff that every set has
for(Map.Entry> entry : testToGoals.entrySet()) {
commonGoals.retainAll(entry.getValue());
}
for(Map.Entry> entry : testToGoals.entrySet()) {
Set goalSet = new LinkedHashSet(entry.getValue());
goalSet.removeAll(commonGoals);
goalMapCopy.put(entry.getKey(), goalSet);
}
testToGoals.putAll(goalMapCopy);
}
/**
* Create a map of test name to all the tests that lead to it
* @return
*/
private Map> determineDuplicateNames() {
Map> testMap = new LinkedHashMap<>();
// Count number of tests with same name
for(Map.Entry entry : testToName.entrySet()) {
String methodName = entry.getValue();
if(!testMap.containsKey(methodName)) {
Set tests = new LinkedHashSet<>();
tests.add(entry.getKey());
testMap.put(methodName, tests);
}
else {
testMap.get(methodName).add(entry.getKey());
}
}
testMap.entrySet().removeIf(p -> p.getValue().size() <= 1);
return testMap;
}
private void resolveAmbiguity(Set tests) {
// Full list of goals for given tests
Map> testToGoals = new LinkedHashMap<>();
for(TestCase test : tests) {
testToGoals.put(test, filterSupportedGoals(new LinkedHashSet<>(test.getCoveredGoals())));
}
// Find out what is unique about each one
findUniqueGoals(testToGoals);
}
/**
* There may be tests with the same calculated name, in which case we add a number suffix
*/
private void fixAmbiguousTestNames() {
Map nameCount = new LinkedHashMap<>();
Map testCount = new LinkedHashMap<>();
for(String methodName : testToName.values()) {
if(nameCount.containsKey(methodName))
nameCount.put(methodName, nameCount.get(methodName) + 1);
else {
nameCount.put(methodName, 1);
testCount.put(methodName, 0);
}
}
for(Map.Entry entry : testToName.entrySet()) {
if(nameCount.get(entry.getValue()) > 1) {
int num = testCount.get(entry.getValue());
testCount.put(entry.getValue(), num + 1);
testToName.put(entry.getKey(), entry.getValue() + num);
}
}
}
/**
* Make first letter upper case
*
* @param input
* @return
*/
private static String capitalize(String input) {
final char[] buffer = input.toCharArray();
buffer[0] = Character.toTitleCase(buffer[0]);
return new String(buffer);
}
/**
* Determine name for the given test
*
* @param test
* @param uniqueGoals
* @return
*/
private String getTestName(TestCase test, Set uniqueGoals) {
List goalList = new ArrayList<>(uniqueGoals); // getTopGoals(uniqueGoals);
String name = PREFIX;
if(goalList.isEmpty()) {
return name; // No goals - no name
} else if(goalList.size() == 1) {
name += capitalize(getGoalName(goalList.get(0)));
} else if(goalList.size() == 2) {
name += getGoalPairName(goalList.get(0), goalList.get(1));
} else {
name += getGoalName(goalList.get(0));
for(int i = 1; i < goalList.size(); i++)
name += STR_AND + getGoalName(goalList.get(i));
// name += capitalize(getGoalName(chooseRepresentativeGoal(test, goalList)));
}
return name;
}
/**
* Retrieve all goals at the highest level of priority
*
* @param coveredGoals
* @return
*/
private List getTopGoals(Set coveredGoals) {
List goalList = new ArrayList<>(coveredGoals);
Collections.sort(goalList, new GoalComparator());
List topGoals = new ArrayList<>();
if(coveredGoals.isEmpty())
return topGoals;
Iterator iterator = goalList.iterator();
TestFitnessFunction lastGoal = iterator.next();
topGoals.add(lastGoal);
while(iterator.hasNext()) {
TestFitnessFunction nextGoal = iterator.next();
if(!nextGoal.getClass().equals(lastGoal.getClass()))
break;
topGoals.add(nextGoal);
lastGoal = nextGoal;
}
return topGoals;
}
/**
* Out of a set of multiple goals, select one that is representative.
* Assumes that goals is not empty, and all items in goals have the same type
* @param test
* @param goals
* @return
*/
private TestFitnessFunction chooseRepresentativeGoal(TestCase test, Collection goals) {
Map methodToPosition = new LinkedHashMap<>();
for(Statement st : test) {
if(st instanceof MethodStatement) {
MethodStatement ms = (MethodStatement)st;
String name = ms.getMethodName() + ms.getDescriptor();
methodToPosition.put(name, st.getPosition());
} else if (st instanceof ConstructorStatement) {
ConstructorStatement cs = (ConstructorStatement)st;
String name = "" + cs.getDescriptor();
methodToPosition.put(name, st.getPosition());
}
}
TestFitnessFunction chosenGoal = goals.iterator().next(); //Randomness.choice(goals);
int chosenPosition = -1;
for(TestFitnessFunction goal : goals) {
if(methodToPosition.containsKey(goal.getTargetMethod())) {
int position = methodToPosition.get(goal.getTargetMethod());
if(position >= chosenPosition) {
chosenPosition = position;
chosenGoal = goal;
}
}
}
return chosenGoal;
}
/**
* Helper function to redirect name retrieval to the right method
*
* @param goal
* @return
*/
private String getGoalName(TestFitnessFunction goal) {
if(goal instanceof MethodCoverageTestFitness) {
return getGoalName((MethodCoverageTestFitness)goal);
} else if(goal instanceof MethodCoverageTestFitness) {
return getGoalName((MethodCoverageTestFitness)goal);
} else if(goal instanceof MethodNoExceptionCoverageTestFitness) {
return getGoalName((MethodNoExceptionCoverageTestFitness)goal);
} else if(goal instanceof ExceptionCoverageTestFitness) {
return getGoalName((ExceptionCoverageTestFitness)goal);
} else if(goal instanceof InputCoverageTestFitness) {
return getGoalName((InputCoverageTestFitness)goal);
} else if(goal instanceof OutputCoverageTestFitness) {
return getGoalName((OutputCoverageTestFitness)goal);
} else {
return formatMethodName(goal.getTargetClass(), goal.getTargetMethod());
// throw new RuntimeException("Not implemented yet: "+goal.getClass());
}
}
/**
* Get name for a single method goal
* @param goal
* @return
*/
private String getGoalName(MethodCoverageTestFitness goal) {
return formatMethodName(goal.getClassName(), goal.getMethod());
}
/**
* Get name for a single method without exception goal
* @param goal
* @return
*/
private String getGoalName(MethodNoExceptionCoverageTestFitness goal) {
return formatMethodName(goal.getClassName(), goal.getMethod());
}
/**
* Get name for a single exception goal
* @param goal
* @return
*/
private String getGoalName(ExceptionCoverageTestFitness goal) {
Class> ex = goal.getExceptionClass();
while (!Modifier.isPublic(ex.getModifiers()) || EvoSuiteMock.class.isAssignableFrom(ex) ||
ex.getCanonicalName().startsWith("com.sun.")) {
ex = ex.getSuperclass();
}
if(goal.getTargetMethod().startsWith("")) {
return STR_CREATE_EXCEPTION + capitalize(getUniqueConstructorName(goal.getTargetClass(), goal.getTargetMethod()))+ STR_THROWS + capitalize(ex.getSimpleName());
}
return formatMethodName(goal.getTargetClass(), goal.getTargetMethod()) + STR_THROWS + capitalize(ex.getSimpleName());
}
/**
* Get name for a single input goal
* @param goal
* @return
*/
private String getGoalName(InputCoverageTestFitness goal) {
String descriptor = goal.getValueDescriptor();
return formatMethodName(goal.getClassName(), goal.getMethod()) + STR_WITH + formatValueDescriptor(descriptor);
}
/**
* Get name for a single output goal
* @param goal
* @return
*/
private String getGoalName(OutputCoverageTestFitness goal) {
String descriptor = goal.getValueDescriptor();
return formatMethodName(goal.getClassName(), goal.getMethod()) + STR_RETURNS + formatValueDescriptor(descriptor);
}
/**
* Format the value descriptors used by input and output goals
*
* @param descriptor
* @return
*/
private String formatValueDescriptor(String descriptor) {
String[] components = descriptor.split(":");
if(components.length == 1) {
return capitalize(descriptor);
} else if(components.length == 2) {
// Ignore classname
return capitalize(components[1]);
} else if(components.length == 3) {
// Second one is "non-null", we'll just ignore this
return capitalize(components[2]);
} else if(components.length == 4) {
// Inspector
return capitalize(getShortClassName(components[1])) + STR_WHERE + capitalize(getMethodNameWithoutDescriptor(components[2])) + STR_IS + capitalize(components[3]);
} else {
throw new RuntimeException("Unsupported value descriptor: "+descriptor);
}
}
/**
* Some goals require special treatment when combining two
* @param goal1
* @param goal2
* @return
*/
private String getGoalPairName(TestFitnessFunction goal1, TestFitnessFunction goal2) {
if(goal1.getClass().equals(goal2.getClass())) {
if(goal1 instanceof MethodCoverageTestFitness) {
return getGoalPairName((MethodCoverageTestFitness) goal1, (MethodCoverageTestFitness) goal2);
}
if(goal1.getTargetClass().equals(goal2.getTargetClass()) && goal1.getTargetMethod().equals(goal2.getTargetMethod())) {
if (goal1 instanceof InputCoverageTestFitness) {
return getGoalPairName((InputCoverageTestFitness) goal1, (InputCoverageTestFitness) goal2);
} else if (goal1 instanceof OutputCoverageTestFitness) {
return getGoalPairName((OutputCoverageTestFitness) goal1, (OutputCoverageTestFitness) goal2);
}
}
}
return getGoalName(goal1) + STR_AND + getGoalName(goal2);
}
/**
* Determine name for pair of method goals
* @param goal1
* @param goal2
* @return
*/
private String getGoalPairName(MethodCoverageTestFitness goal1, MethodCoverageTestFitness goal2) {
boolean isConstructor1 = goal1.getTargetMethod().startsWith("");
boolean isConstructor2 = goal2.getTargetMethod().startsWith("");
if(isConstructor1 != isConstructor2) {
if(isConstructor1)
return getGoalName(goal1) + "AndCalls" + getGoalName(goal2);
else
return getGoalName(goal2) + "AndCalls" + getGoalName(goal1);
} else {
return getGoalName(goal1) + STR_AND + getGoalName(goal2);
}
}
/**
* Determine name for pair of input goals
*
* @param goal1
* @param goal2
* @return
*/
private String getGoalPairName(InputCoverageTestFitness goal1, InputCoverageTestFitness goal2) {
return formatMethodName(goal1.getClassName(), goal1.getMethod()) + STR_WITH + formatValueDescriptor(goal1.getValueDescriptor()) + STR_AND + formatValueDescriptor(goal2.getValueDescriptor());
}
/**
* Determine name for pair of output goals
*
* @param goal1
* @param goal2
* @return
*/
private String getGoalPairName(OutputCoverageTestFitness goal1, OutputCoverageTestFitness goal2 ) {
return formatMethodName(goal1.getClassName(), goal1.getMethod()) + STR_RETURNS + formatValueDescriptor(goal1.getValueDescriptor()) + STR_AND + formatValueDescriptor(goal2.getValueDescriptor());
}
/**
* Make sure package names are omitted and array brackets are not used in names
* @param className
* @return
*/
private String getShortClassName(String className) {
int pos = className.lastIndexOf(".");
if(pos >= 0)
return className.substring(pos+1).replace("[]", "Array");
else
return className.replace("[]", "Array");
}
/**
* Distinguish between constructors and methods in creating call goals
*
* @param className
* @param method
* @return
*/
private String formatMethodName(String className, String method) {
if(method.startsWith("")) {
String methodWithoutDescriptor = getMethodNameWithoutDescriptor(method);
if(methodCount.containsKey(methodWithoutDescriptor) && methodCount.get(methodWithoutDescriptor).size() > 1) {
return STR_CREATE + capitalize(getUniqueConstructorName(getShortClassName(className), method));
} else {
return STR_CREATE + capitalize(getShortClassName(className));
}
}
else {
String methodWithoutDescriptor = getMethodNameWithoutDescriptor(method);
if(methodCount.containsKey(methodWithoutDescriptor) && methodCount.get(methodWithoutDescriptor).size() > 1) {
return capitalize(getUniqueMethodName(methodWithoutDescriptor, method));
}
else {
return capitalize(methodWithoutDescriptor);
}
}
}
/**
* Get a unique method name, depending on whether it is unique or overloaded
* @param methodNameWithoutDescriptor
* @param methodName
* @return
*/
private String getUniqueMethodName(String methodNameWithoutDescriptor, String methodName) {
if(!methodCount.containsKey(methodNameWithoutDescriptor))
return methodNameWithoutDescriptor;
if(methodCount.get(methodNameWithoutDescriptor).size() == 1)
return methodNameWithoutDescriptor;
int pos = methodName.indexOf('(');
if(pos < 0) {
return methodName; // TODO: Should this really be possible?
}
String descriptor = methodName.substring(pos);
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
// TODO: Dummy for now
if(argumentTypes.length == 0)
return methodNameWithoutDescriptor + STR_WITHOUT + STR_ARGUMENTS;
else if(argumentTypes.length == 1) {
return methodNameWithoutDescriptor + STR_TAKING + capitalize(getShortClassName(argumentTypes[0].getClassName()));
}
else {
// is there any other implementation of the same method with same number of arguments?
Set otherMethods = new HashSet<>();
otherMethods.addAll(methodCount.get(methodNameWithoutDescriptor));
otherMethods.remove(methodName);
boolean sameCardinality = false;
for (String otherMethod : otherMethods) {
String otherDesc = otherMethod.substring(otherMethod.indexOf('('));
Type[] otherArgTypes = Type.getArgumentTypes(otherDesc);
if (otherArgTypes.length == argumentTypes.length) {
sameCardinality = true;
break;
}
}
if (sameCardinality) { // synthesise descriptor based on arguments' types
return methodNameWithoutDescriptor + STR_TAKING + getTypeArgumentsDescription(argumentTypes);
} else // only method with this number of arguments
return methodNameWithoutDescriptor + STR_TAKING + argumentTypes.length + STR_ARGUMENTS;
}
}
/**
* Concatenate argument types when there is more than one arguments
* @param argumentTypes
* @return
*/
private String getTypeArgumentsDescription(Type[] argumentTypes) {
assert (argumentTypes.length > 1);
Map typeDescs = new LinkedHashMap<>();
for (Type t : argumentTypes) {
String d = capitalize(getShortClassName(t.getClassName()));
if (! typeDescs.containsKey(d))
typeDescs.put(d, 1);
else
typeDescs.put(d, typeDescs.get(d) + 1);
}
StringBuilder builder = new StringBuilder();
Object[] args = typeDescs.keySet().toArray();
for (int i = 0; i < args.length; i ++ ){
String arg = (String)args[i];
if (args.length > 1 && i == args.length - 1)
builder.append(STR_AND);
if (typeDescs.get(arg) == 1)
builder.append(typeDescs.get(arg));
else {
builder.append(Integer.toString(typeDescs.get(arg)));
builder.append(arg);
builder.append('s');
}
}
return builder.toString();
}
/**
* Get a unique constructor name, depending on whether it is unique or overloaded
* @param className
* @param methodName
* @return
*/
private String getUniqueConstructorName(String className, String methodName) {
if(!methodCount.containsKey(""))
return getShortClassName(className);
if(methodCount.get("").size() == 1)
return getShortClassName(className);
int pos = methodName.indexOf('(');
if(pos < 0) {
return getShortClassName(className); // TODO: Should this really be possible?
}
String descriptor = methodName.substring(pos);
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
// TODO: Dummy for now
if(argumentTypes.length == 0)
return getShortClassName(className) + STR_WITHOUT + STR_ARGUMENTS;
else if(argumentTypes.length == 1) {
return getShortClassName(className) + STR_TAKING + capitalize(getShortClassName(argumentTypes[0].getClassName()));
}
else
return getShortClassName(className) + STR_TAKING + argumentTypes.length + STR_ARGUMENTS;
}
/**
* Cut off descriptor from method name
* @param methodName
* @return
*/
private String getMethodNameWithoutDescriptor(String methodName) {
// Should have a descriptor
int pos = methodName.indexOf('(');
if(pos > 0)
return methodName.substring(0, pos);
else
return methodName;
}
public Comparator getComparator() {
return new Comparator() {
@Override
public int compare(TestCase o1, TestCase o2) {
return 0;
}
};
}
@Override
public String getName(TestCase test) {
return testToName.get(test);
}
}