Skip to content

Commit

Permalink
Merge pull request #451 from GeraldineGalindo/master
Browse files Browse the repository at this point in the history
Heuristics for variable naming
  • Loading branch information
jose committed Jul 11, 2023
2 parents 737d33d + 7236daa commit 1948d76
Show file tree
Hide file tree
Showing 11 changed files with 379 additions and 41 deletions.
4 changes: 3 additions & 1 deletion client/src/main/java/org/evosuite/Properties.java
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,8 @@ public enum OutputGranularity {
@Parameter(key = "max_coverage_depth", group = "Output", description = "Maximum depth in the calltree to count a branch as covered")
public static int MAX_COVERAGE_DEPTH = -1;

// ---------------------------------------------------------------
// Naming
public enum TestNamingStrategy {
NUMBERED, COVERAGE
}
Expand All @@ -1242,7 +1244,7 @@ public enum TestNamingStrategy {
public static TestNamingStrategy TEST_NAMING_STRATEGY = TestNamingStrategy.NUMBERED;

public enum VariableNamingStrategy {
TYPE_BASED
TYPE_BASED, HEURISTICS_BASED
}

@Parameter(key = "variable_naming_strategy", group = "Output", description = "What strategy to use to derive names for variables")
Expand Down
48 changes: 45 additions & 3 deletions client/src/main/java/org/evosuite/testcase/TestCodeVisitor.java
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import dk.brics.automaton.RegExp;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.TypeUtils;
import org.evosuite.PackageInfo;
import org.evosuite.Properties;
Expand Down Expand Up @@ -73,6 +74,17 @@ public class TestCodeVisitor extends TestVisitor {

protected VariableNameStrategy variableNameStrategy = VariableNameStrategyFactory.get();

/**
* Dictionaries for naming information
*/
/**
* Dictionaries for naming information
*/
protected Map<VariableReference, String> methodNames = new HashMap<>();
protected Map<VariableReference, String> argumentNames = new HashMap<>();

private Map<String, Map<VariableReference, String>> information = new HashMap<>();

/**
* <p>
* getCode
Expand Down Expand Up @@ -380,10 +392,16 @@ public String getVariableName(VariableReference var) {
}
return result;
} else {
if(VariableNameStrategyFactory.gatherInformation()){
information.put("MethodNames", methodNames);
information.put("ArgumentNames", argumentNames);
variableNameStrategy.addVariableInformation(information);
}
return variableNameStrategy.getNameForVariable(var);
}
}


/**
* Retrieve the names of all known variables
*
Expand Down Expand Up @@ -1326,17 +1344,18 @@ public void visitMethodStatement(MethodStatement statement) {
Throwable exception = getException(statement);
List<VariableReference> parameters = statement.getParameterReferences();
boolean isGenericMethod = method.hasTypeParameters();

if (exception != null && !statement.isDeclaredException(exception)) {
result += "// Undeclared exception!" + NEWLINE;
}

boolean lastStatement = statement.getPosition() == statement.getTestCase().size() - 1;
boolean unused = !Properties.ASSERTIONS ? exception != null : test != null
&& !test.hasReferences(retval);

if (!retval.isVoid() && retval.getAdditionalVariableReference() == null
&& !unused) {
if (!this.methodNames.containsKey(retval) && VariableNameStrategyFactory.gatherInformation()) {
this.methodNames.put(retval, method.getName());
}
if (exception != null) {
if (!lastStatement || statement.hasAssertions())
result += getClassName(retval) + " " + getVariableName(retval)
Expand All @@ -1349,7 +1368,22 @@ public void visitMethodStatement(MethodStatement statement) {
result += "try { " + NEWLINE + " ";
}


if (!this.argumentNames.containsKey(retval) && VariableNameStrategyFactory.gatherInformation()) {
final List<String> parameterNames = (List<String>)statement.obtainParameterNameListInOrder();
int idx = 0;
for (final VariableReference param : parameters) {
this.argumentNames.put(param, parameterNames.get(idx));
++idx;
}
}
if (retval.isVoid() && VariableNameStrategyFactory.gatherInformation()) {
final List<String> parameterNames = (List<String>)statement.obtainParameterNameListInOrder();
int idx = 0;
for (final VariableReference param : parameters) {
this.argumentNames.put(param, parameterNames.get(idx));
++idx;
}
}
String parameter_string = getParameterString(method.getParameterTypes(),
parameters, isGenericMethod,
method.isOverloaded(parameters), 0);
Expand Down Expand Up @@ -1578,6 +1612,14 @@ public void visitConstructorStatement(ConstructorStatement statement) {
&& !Modifier.isStatic(constructor.getConstructor().getDeclaringClass().getModifiers());

List<VariableReference> parameters = statement.getParameterReferences();
if (!this.argumentNames.containsKey(retval) && VariableNameStrategyFactory.gatherInformation()) {
final List<String> parameterNames = statement.obtainParameterNameListInOrder();
int idx = 0;
for (final VariableReference param : parameters) {
this.argumentNames.put(param, parameterNames.get(idx));
++idx;
}
}
int startPos = 0;
if (isNonStaticMemberClass) {
startPos = 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.*;

/**
Expand Down Expand Up @@ -478,4 +479,19 @@ public String getDeclaringClassName() {
public String getMethodName() {
return "<init>";
}

/**
* Returns a list of the parameter names of a method using reflection. The list is in order of
* declaration in the method.
*
* @return List<String>
*/
public List<String> obtainParameterNameListInOrder() {
final Parameter[] parameters = this.constructor.getParameters();
final List<String> names = new ArrayList<String>();
for (final Parameter p : parameters) {
names.add(p.getName());
}
return names;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -650,4 +651,13 @@ public String getDeclaringClassName() {
public String getMethodName() {
return method.getName();
}

public List<String> obtainParameterNameListInOrder() {
final Parameter[] parameters = this.method.getParameters();
final List<String> names = new ArrayList<String>();
for (final Parameter p : parameters) {
names.add(p.getName());
}
return names;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.evosuite.testcase.utils;
import java.util.ArrayList;

public class HeuristicsUtil {
/**
* List of particles of a method name that can be excluded or avoided when syggesting names
*/
private static ArrayList<String> avoidableParticles = new ArrayList<String>(){
{
add("get");
add("to");
add("has");
add("is");
add("are");
}
};

/**
* Returns a boolean value that indicates if the first word of a method can be avoided/excluded
* on method name suggestion
* @return boolean
*/

public static boolean containsAvoidableParticle(String firstWord){
return avoidableParticles.contains(firstWord);
}
/**
* Separates camelcase strings and retrieves the parts in a list
* @return ArrayList<String>
*/
public static ArrayList<String> separateByCamelCase(String name){
ArrayList<String> separatedName = new ArrayList<>();
for (String word : name.split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])")) {
separatedName.add(word);
}
return separatedName;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.evosuite.testcase.variable.name;

import org.apache.commons.lang3.StringUtils;
import org.evosuite.testcase.utils.HeuristicsUtil;
import org.evosuite.testcase.variable.VariableReference;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class HeuristicsVariableNameStrategy extends AbstractVariableNameStrategy{

protected final Map<String, Integer> nextIndices = new ConcurrentHashMap<>();
/**
* Dictionaries for naming information
*/
protected Map<VariableReference, String> methodNames = new HashMap<>();
protected Map<VariableReference, String> argumentNames = new HashMap<>();

private TypeBasedVariableNameStrategy typeBasedVariableNameStrategy = new TypeBasedVariableNameStrategy();
@Override
public String createNameForVariable(VariableReference variable) {
String typeBasedName = typeBasedVariableNameStrategy.getPlainNameForVariable(variable);
return getPrioritizedName(variable, typeBasedName);
}

/**
* Returns the variable name + the corresponding index if and only if there is more than one repetition of the name,
* otherwise, it returns the name without an index at last.
*
* Mainly used for Heuristic Renaming Strategy.
*
* @return String
*/
private String getVariableWithIndexExcludingFirstAppearance(String variableName) {
if (!this.nextIndices.containsKey(variableName)) {
this.nextIndices.put(variableName, 0);
}
else {
final int index = this.nextIndices.get(variableName);
this.nextIndices.put(variableName, index + 1);
variableName += this.nextIndices.get(variableName);
}
return variableName;
}
/**
* Retrieve a suggested name based on method, argument and type information.
*
* The followed order for prioritizing is:
* 1. Use argument suggestion, if not possible
* 2. Use method suggestion + reductions, if not possible
* 3. Use type suggestion, traditional naming.
*
* @return String
*/
private String getPrioritizedName(final VariableReference var, String variableName) {
final String methodCode = this.methodNames.get(var);
final String arguments = this.argumentNames.get(var);
if (arguments != null) {
variableName = arguments;
}
else if (methodCode != null) {
variableName = analyzeMethodName(methodCode);
}
if(variableName.equals(var.getSimpleClassName())){
variableName = "_" + variableName;
}
return variableName;
}

/**
* Returns the suggested method name controlling camel case and excluding some particles
* of the method names.
*
* @return String
*/
private String analyzeMethodName(String methodCode) {
String name = "";
ArrayList<String> methodName = HeuristicsUtil.separateByCamelCase(methodCode);
if(methodCode.length() > 0){
if(HeuristicsUtil.containsAvoidableParticle(methodName.get(0)) && methodName.size() > 1){
name = StringUtils.join(methodName.subList(1,methodName.size()), "");
final char[] auxCharArray = name.toCharArray();
auxCharArray[0] = Character.toLowerCase(auxCharArray[0]);
return new String(auxCharArray);
}
}
return methodCode;
}

public void addVariableInformation(Map<String, Map<VariableReference, String>> information){
methodNames = information.get("MethodNames");
argumentNames = information.get("ArgumentNames");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,19 @@ public class TypeBasedVariableNameStrategy extends AbstractVariableNameStrategy

@Override
public String createNameForVariable(VariableReference var) {
String variableName = getPlainNameForVariable(var);
return getIndexIncludingFirstAppearance(variableName);
}

public String getPlainNameForVariable(VariableReference var){
String className = var.getSimpleClassName();
String variableName;
if (var instanceof ArrayReference) {
String className = var.getSimpleClassName();
// int num = 0;
// for (VariableReference otherVar : variableNames.keySet()) {
// if (!otherVar.equals(var)
// && otherVar.getVariableClass().equals(var.getVariableClass()))
// num++;
// }
String variableName = className.substring(0, 1).toLowerCase()
variableName = className.substring(0, 1).toLowerCase()
+ className.substring(1) + "Array";
variableName = variableName.replace('.', '_').replace("[]", "");

if (!nextIndices.containsKey(variableName)) {
nextIndices.put(variableName, 0);
}

int index = nextIndices.get(variableName);
nextIndices.put(variableName, index + 1);

variableName += index;

return variableName;
} else {
String className = var.getSimpleClassName();
// int num = 0;
// for (VariableReference otherVar : variableNames.keySet()) {
// if (otherVar.getVariableClass().equals(var.getVariableClass()))
// num++;
// }
String variableName = className.substring(0, 1).toLowerCase()
variableName = className.substring(0, 1).toLowerCase()
+ className.substring(1);
if (variableName.contains("[]")) {
variableName = variableName.replace("[]", "Array");
Expand All @@ -61,19 +44,30 @@ public String createNameForVariable(VariableReference var) {
// if (numObjectsOfType > 1 || className.equals(variableName)) {
if (CharUtils.isAsciiNumeric(variableName.charAt(variableName.length() - 1)))
variableName += "_";

if (!nextIndices.containsKey(variableName)) {
nextIndices.put(variableName, 0);
}

int index = nextIndices.get(variableName);
nextIndices.put(variableName, index + 1);

variableName += index;
// }

return variableName;
}
return variableName;
}

/**
* Returns the variable name + the number of repetitions counting from 0.
* i.e. If the variable appears only once in the test, it is named as variable0.
*
* Mainly used for Type-Based Renaming Strategy (traditional naming in EvoSuite).
*
* @return String
*/
private String getIndexIncludingFirstAppearance(String variableName) {
if (!nextIndices.containsKey(variableName)) {
nextIndices.put(variableName, 0);
}
int index = nextIndices.get(variableName);
nextIndices.put(variableName, index + 1);
return variableName += index;
}
public void addVariableInformation(Map<String, Map<VariableReference, String>> information){
//If needed any information about types
}

}
Loading

0 comments on commit 1948d76

Please sign in to comment.