Class Minimize
- java.lang.Object
-
- randoop.main.CommandHandler
-
- randoop.main.Minimize
-
public class Minimize extends CommandHandler
This program minimizes a failing JUnit test suite. Its three command-line arguments are:- the Java file whose failing tests will be minimized
- an optional classpath containing dependencies needed to compile and run the Java file
- an optional timeout limit, in seconds, allowed for the whole test suite to run. The default is 30 seconds.
The minimizer will only attempt to minimize methods that are annotated with the @Test annotation. In a method that contains a failing assertion, the minimizer will iterate through the statements of the method, from last to first. For each statement, it tries possible replacement statements, from most minimized to least minimized. Removing the statement is the most a statement can be minimized. Leaving the statement unchanged is the least that the statement can be minimized.
If a replacement causes the output test suite to fail differently than the original test suite, the algorithm tries a different replacement. If no replacement allows the output test suite to fail in the same way as the original test suite, the algorithm adds back the original version of the current statement and continues.
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description private static class
Minimize.ClassOrInterfaceTypeComparator
Sorts a type by its simple name.private static class
Minimize.ImportDeclarationComparator
Sorts ImportDeclaration objects by their name.static class
Minimize.Outputs
Contains the command line, exit status, standard output, and standard error from running a process.
-
Field Summary
Fields Modifier and Type Field Description private static Minimize.ClassOrInterfaceTypeComparator
classOrInterfaceTypeComparator
Sorts a type by its simple name.private static Minimize.ImportDeclarationComparator
importDeclarationComparator
Sorts ImportDeclaration objects by their name.private static com.github.javaparser.JavaParser
javaParser
An instance of a Java parser.static int
minimizetimeout
The maximum number of seconds allowed for the entire minimization process.private static String
PATH_SEPARATOR
Path separator as defined by the system, used to separate elements of the classpath.private static String
SUFFIX
The suffix to postpend onto the name of the minimized file and class.static String
suiteclasspath
Classpath that includes dependencies needed to compile and run the JUnit test suite being minimized.static String
suitepath
The Java file whose failing tests will be minimized.static int
testsuitetimeout
The maximum number of seconds allowed for the entire test suite to run.static boolean
verboseminimizer
Produce verbose diagnostics to standard output if true.
-
Constructor Summary
Constructors Constructor Description Minimize()
Create the handler for Randoop'sminimize
command.
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description private static void
addImport(com.github.javaparser.ast.CompilationUnit compilationUnit, String importName)
Add an import to the list ofImportDeclaration
s of the compilation unit.private static boolean
checkCorrectlyMinimized(Path file, String classpath, String packageName, Map<String,String> expectedOutput, int timeoutLimit)
Check if a Java file has been correctly minimized.private static void
cleanUp(Path outputFile, boolean verboseOutput)
Deletes the .class file associated with the outputFile.private static Minimize.Outputs
compileJavaFile(Path file, String classpath, String packageName, int timeoutLimit)
Compile a Java file and return the compilation exit value.private static Path
getExecutionDirectory(Path file, String packageName)
Get directory to execute command in, given file path and package name.private static int
getFileLength(Path file)
Calculate the length of a file, by number of lines.private static com.github.javaparser.ast.expr.LiteralExpr
getLiteralExpression(String value, com.github.javaparser.ast.type.PrimitiveType.Primitive type)
Return a literal expression with the value that is passed in.private static int
getNumberOfTestMethods(com.github.javaparser.ast.CompilationUnit compilationUnit)
Return the number of JUnit test methods in a compilation unit.private static void
getOrphanCommentsBeforeThisChildNode(com.github.javaparser.ast.Node node, List<com.github.javaparser.ast.comments.Comment> result)
This is stolen from JavaParser's PrettyPrintVisitor.printOrphanCommentsBeforeThisChildNode, with light modifications.private static List<com.github.javaparser.ast.stmt.Statement>
getStatementReplacements(com.github.javaparser.ast.stmt.Statement currStmt, Map<String,String> primitiveValues)
Return a list of statements that are a simplification of a given statement, in order from most to least minimized.boolean
handle(String[] args)
Check that the required parameters have been specified by the command-line options and then call the mainMinimize method.private static boolean
isTestMethod(com.github.javaparser.ast.body.MethodDeclaration methodDeclaration)
Check if the method is a JUnit test method.static boolean
mainMinimize(Path file, String classPath, int timeoutLimit, boolean verboseOutput)
Minimize the input test file.static String
minimizedClassName(Path file)
Given a .java filename for non-minimized tests, returns the simple name of the class containing the minimized tests.private static void
minimizeMethod(com.github.javaparser.ast.body.MethodDeclaration method, com.github.javaparser.ast.CompilationUnit compilationUnit, String packageName, Path file, String classpath, Map<String,String> expectedOutput, int timeoutLimit)
Minimize a method by minimizing each statement in turn.private static void
minimizeTestSuite(com.github.javaparser.ast.CompilationUnit compilationUnit, String packageName, Path file, String classpath, Map<String,String> expectedOutput, int timeoutLimit)
Visit and minimize every JUnit test method within a compilation unit.private static Map<String,String>
normalizeJUnitOutput(String input)
Normalize the standard output obtained from running a JUnit test suite.private static void
primitiveVarEquality(com.github.javaparser.ast.expr.Expression exp1, com.github.javaparser.ast.expr.Expression exp2, Map<String,String> primitiveValues, Set<String> primitiveAndWrappedTypeVars)
If one of the arguments is a NamxExpr and the other is a LiteralExpr, put them inprimitiveValues
.private static void
printProgress(int currentTestIndex, int totalTests, com.github.javaparser.ast.expr.SimpleName testName)
Output the minimizer's current progress.private static com.github.javaparser.ast.stmt.Statement
removeLeftHandSideSimplification(com.github.javaparser.ast.expr.VariableDeclarationExpr vdExpr)
Return a statement that contains only the right hand side of a given statement.private static com.github.javaparser.ast.stmt.Statement
rhsAssignValueFromPassingAssertion(com.github.javaparser.ast.expr.VariableDeclarationExpr vdExpr, Map<String,String> primitiveValues)
Return a variable declaration statement that simplifies the right hand side by a calculated value for primitive types.private static com.github.javaparser.ast.stmt.Statement
rhsAssignWithValue(com.github.javaparser.ast.expr.VariableDeclarationExpr vdExpr, com.github.javaparser.ast.type.Type exprType, String value)
Return a variable declaration statement that sets the right hand side of a variable declaration to the value that is passed in.private static List<com.github.javaparser.ast.stmt.Statement>
rhsAssignZeroValue(com.github.javaparser.ast.expr.VariableDeclarationExpr vdExpr)
Return a list of variable declaration statements that could replace the right hand side by 0, false, or null, whichever is type correct.private static String
runJavaFile(Path file, String userClassPath, String packageName, int timeoutLimit)
Run a Java file and return the standard output.static Minimize.Outputs
runProcess(String command, Path executionDir, int timeoutLimit)
Run a command given as a String and return the output and error results in an Outputs object.private static com.github.javaparser.ast.CompilationUnit
simplifyTypeNames(com.github.javaparser.ast.CompilationUnit compilationUnit, String packageName, Path file, String classpath, Map<String,String> expectedOutput, int timeoutLimit, boolean verboseOutput)
Simplify the type names in a compilation unit.private static void
sortImports(com.github.javaparser.ast.CompilationUnit compilationUnit)
Sort a compilation unit's imports by name.private static void
storeValueFromAssertion(com.github.javaparser.ast.stmt.Statement currStmt, Map<String,String> primitiveValues, Set<String> primitiveAndWrappedTypeVars)
IfcurrStmt
is an assertion about a primitive value, store the value associated with the variable in theprimitiveValues
map.static void
writeToFile(com.github.javaparser.ast.CompilationUnit compilationUnit, Path file)
Write a compilation unit to a Java file.-
Methods inherited from class randoop.main.CommandHandler
handles, printHTML, usageMessage
-
-
-
-
Field Detail
-
suitepath
public static String suitepath
The Java file whose failing tests will be minimized.
-
suiteclasspath
public static String suiteclasspath
Classpath that includes dependencies needed to compile and run the JUnit test suite being minimized.
-
minimizetimeout
public static int minimizetimeout
The maximum number of seconds allowed for the entire minimization process.
-
testsuitetimeout
public static int testsuitetimeout
The maximum number of seconds allowed for the entire test suite to run.
-
verboseminimizer
public static boolean verboseminimizer
Produce verbose diagnostics to standard output if true.
-
javaParser
private static final com.github.javaparser.JavaParser javaParser
An instance of a Java parser.
-
PATH_SEPARATOR
private static final String PATH_SEPARATOR
Path separator as defined by the system, used to separate elements of the classpath.
-
SUFFIX
private static final String SUFFIX
The suffix to postpend onto the name of the minimized file and class.- See Also:
- Constant Field Values
-
classOrInterfaceTypeComparator
private static Minimize.ClassOrInterfaceTypeComparator classOrInterfaceTypeComparator
Sorts a type by its simple name.
-
importDeclarationComparator
private static Minimize.ImportDeclarationComparator importDeclarationComparator
Sorts ImportDeclaration objects by their name.
-
-
Method Detail
-
minimizedClassName
public static String minimizedClassName(Path file)
Given a .java filename for non-minimized tests, returns the simple name of the class containing the minimized tests.- Parameters:
file
- the .java filename for non-minimized tests- Returns:
- the simple class name for the minimized tests
-
handle
public boolean handle(String[] args)
Check that the required parameters have been specified by the command-line options and then call the mainMinimize method.- Specified by:
handle
in classCommandHandler
- Parameters:
args
- parameters, specified in command-line style, for the input file, the classpath, the timeout value, and the verbose flag- Returns:
- true if the command was handled successfully
-
mainMinimize
public static boolean mainMinimize(Path file, String classPath, int timeoutLimit, boolean verboseOutput) throws IOException
Minimize the input test file.Given an input Java file, minimization produces a smaller file that fails in the same way as the original, having the same failing assertions with the same stack trace.
- Same failing assertions as in the original input test suite.
- Same stacktrace produced by failing assertions.
The original input Java file will be compiled and run once. The "expected output" derived from the standard output from running the input file is a map from test method name to failure stack trace. A method is included in the map only if the method contains a failing assertion. Thus, the "expected output" of running a test suite with no failing tests will be an empty map. The "expected output" will be used during subsequent runs of the modified test suite to determine whether or not the test suite still fails in the same way.
- Parameters:
file
- the Java file that is being minimizedclassPath
- classpath used to compile and run the Java filetimeoutLimit
- number of seconds allowed for the whole test suite to runverboseOutput
- whether to produce verbose output- Returns:
- true if minimization produced a (possibly unchanged) file that fails the same way as the original file
- Throws:
IOException
- if write to file fails
-
minimizeTestSuite
private static void minimizeTestSuite(com.github.javaparser.ast.CompilationUnit compilationUnit, String packageName, Path file, String classpath, Map<String,String> expectedOutput, int timeoutLimit) throws IOException
Visit and minimize every JUnit test method within a compilation unit.- Parameters:
compilationUnit
- the compilation unit to minimize; is modified by side effectpackageName
- the package that the Java file is infile
- the Java file that is being minimized; is modified by side effectclasspath
- classpath used to compile and run the Java fileexpectedOutput
- expected JUnit output when the Java file is compiled and runtimeoutLimit
- number of seconds allowed for the whole test suite to run- Throws:
IOException
- thrown if minimized method can't be written to file
-
isTestMethod
private static boolean isTestMethod(com.github.javaparser.ast.body.MethodDeclaration methodDeclaration)
Check if the method is a JUnit test method.- Parameters:
methodDeclaration
- the method declaration to check- Returns:
- true if the method is a JUnit test method
-
minimizeMethod
private static void minimizeMethod(com.github.javaparser.ast.body.MethodDeclaration method, com.github.javaparser.ast.CompilationUnit compilationUnit, String packageName, Path file, String classpath, Map<String,String> expectedOutput, int timeoutLimit) throws IOException
Minimize a method by minimizing each statement in turn.- Parameters:
method
- the method to minimize; is modified by side effectcompilationUnit
- compilation unit for the Java file that we are minimizing; is modified by side effectpackageName
- the package that the Java file is infile
- the Java file that is being minimized; is modified by side effectclasspath
- classpath needed to compile and run the Java fileexpectedOutput
- expected output from running the JUnit test suitetimeoutLimit
- number of seconds allowed for the whole test suite to run- Throws:
IOException
- thrown if write to file fails
-
storeValueFromAssertion
private static void storeValueFromAssertion(com.github.javaparser.ast.stmt.Statement currStmt, Map<String,String> primitiveValues, Set<String> primitiveAndWrappedTypeVars)
IfcurrStmt
is an assertion about a primitive value, store the value associated with the variable in theprimitiveValues
map.currStmt
might be an assertTrue statement using an '==' operator, or an assertEquals statement.- Parameters:
currStmt
- a statementprimitiveValues
- a map of variable names to variable values; modified ifcurrStmt
is a passing assertion, asserting a variable's valueprimitiveAndWrappedTypeVars
- set containing the names of all primitive and wrapped type variables
-
primitiveVarEquality
private static void primitiveVarEquality(com.github.javaparser.ast.expr.Expression exp1, com.github.javaparser.ast.expr.Expression exp2, Map<String,String> primitiveValues, Set<String> primitiveAndWrappedTypeVars)
If one of the arguments is a NamxExpr and the other is a LiteralExpr, put them inprimitiveValues
.- Parameters:
exp1
- the first expressionexp2
- the second expressionprimitiveValues
- a map of variable names to variable values; modified ifcurrStmt
is a passing assertion, asserting a variable's valueprimitiveAndWrappedTypeVars
- set containing the names of all primitive and wrapped type variables
-
getStatementReplacements
private static List<com.github.javaparser.ast.stmt.Statement> getStatementReplacements(com.github.javaparser.ast.stmt.Statement currStmt, Map<String,String> primitiveValues)
Return a list of statements that are a simplification of a given statement, in order from most to least minimized. The possible minimizations are:- Remove a statement, represented by null.
- Replace the right hand side expression with
0
,false
, ornull
. - Replace right hand side by a calculated value obtained from a passing assertion.
- Remove the left hand side of a statement, retaining only the expression on the right.
Assertions are never simplified, only removed completely.
- Parameters:
currStmt
- statement to simplifyprimitiveValues
- map of primitive variable names to expressions representing their values- Returns:
- list of statements, where each is a possible simplification of
currStmt
-
rhsAssignZeroValue
private static List<com.github.javaparser.ast.stmt.Statement> rhsAssignZeroValue(com.github.javaparser.ast.expr.VariableDeclarationExpr vdExpr)
Return a list of variable declaration statements that could replace the right hand side by 0, false, or null, whichever is type correct. Returns an empty list if there are multiple variable declarations in a single statement, such asint i, j, k;
.- Parameters:
vdExpr
- variable declaration expression representing the current statement to simplify- Returns:
- a list of
Statement
objects representing the simplified variable declaration expression
-
rhsAssignValueFromPassingAssertion
private static com.github.javaparser.ast.stmt.Statement rhsAssignValueFromPassingAssertion(com.github.javaparser.ast.expr.VariableDeclarationExpr vdExpr, Map<String,String> primitiveValues)
Return a variable declaration statement that simplifies the right hand side by a calculated value for primitive types. Returns null if there are multiple variable declarations in a single statement, such asint i, j, k;
.- Parameters:
vdExpr
- variable declaration expression representing the current statement to simplifyprimitiveValues
- a map of primitive variable names to expressions representing their values- Returns:
- a
Statement
object representing the simplified variable declaration expression if the type of the variable is a primitive and a value has been previously calculated. Otherwise, returnsnull
. Also returnsnull
if more than one variable is declared in theVariableDeclarationExpr
.
-
rhsAssignWithValue
private static com.github.javaparser.ast.stmt.Statement rhsAssignWithValue(com.github.javaparser.ast.expr.VariableDeclarationExpr vdExpr, com.github.javaparser.ast.type.Type exprType, String value)
Return a variable declaration statement that sets the right hand side of a variable declaration to the value that is passed in.- Parameters:
vdExpr
- variable declaration expression representing the current statement to simplifyexprType
- type of the variable declaration expression, should not be nullvalue
- value that will be assigned to the variable being declared. If the value is null, then the right hand side will have the zero value of the variable declaration's type.- Returns:
- a
Statement
object representing the simplified variable declaration expression. Returnsnull
if more than one variable is declared in theVariableDeclarationExpr
.
-
getLiteralExpression
private static com.github.javaparser.ast.expr.LiteralExpr getLiteralExpression(String value, com.github.javaparser.ast.type.PrimitiveType.Primitive type)
Return a literal expression with the value that is passed in.- Parameters:
value
- the value for the literal expression. If null, the value of the literal expression will be the zero value for the type that is passed in.type
- the type of the expression, needs to be one of the eight primitive types- Returns:
- a literal expression containing the value that is passed in
-
removeLeftHandSideSimplification
private static com.github.javaparser.ast.stmt.Statement removeLeftHandSideSimplification(com.github.javaparser.ast.expr.VariableDeclarationExpr vdExpr)
Return a statement that contains only the right hand side of a given statement. Returns null if there are multiple variable declarations in a single statement, such asint i=1, j=2, k=3;
, or if there are no initializers, as inint i;
.- Parameters:
vdExpr
- variable declaration expression that represents the statement to simplify- Returns:
- a
Statement
object that is equal to the right-hand-side ofvdExpr
. Returnsnull
if more than one variable is declared in theVariableDeclarationExpr
.
-
simplifyTypeNames
private static com.github.javaparser.ast.CompilationUnit simplifyTypeNames(com.github.javaparser.ast.CompilationUnit compilationUnit, String packageName, Path file, String classpath, Map<String,String> expectedOutput, int timeoutLimit, boolean verboseOutput) throws IOException
Simplify the type names in a compilation unit. For example,java.lang.String
should be simplified toString
. If two different types have the same simple type name, then the lexicographically first one is simplified and the other is left unchanged.Additionally, sort the import statements of the compilation unit.
- Parameters:
compilationUnit
- compilation unit containing an AST for a Java file, the compilation unit will be modified if a correct minimization of the method is foundpackageName
- the package that the Java file is infile
- the Java file to simplify; is modified by side effectclasspath
- classpath needed to compile and run the Java fileexpectedOutput
- expected standard output from running the JUnit test suitetimeoutLimit
- number of seconds allowed for the whole test suite to runverboseOutput
- whether or not to output information about minimization status- Returns:
CompilationUnit
with fully-qualified type names simplified to simple type names- Throws:
IOException
- thrown if write to file fails
-
checkCorrectlyMinimized
private static boolean checkCorrectlyMinimized(Path file, String classpath, String packageName, Map<String,String> expectedOutput, int timeoutLimit)
Check if a Java file has been correctly minimized. The file should not have compilation errors or run-time errors. The file should fail in the same way as the original file.- Parameters:
file
- the file being checkedclasspath
- classpath needed to compile/run the Java filepackageName
- the package that the Java file is inexpectedOutput
- expected output of running the JUnit test suitetimeoutLimit
- number of seconds allowed for the whole test suite to run- Returns:
- true if there are no compilation and no run-time errors and the output is equal to the expected output
-
compileJavaFile
private static Minimize.Outputs compileJavaFile(Path file, String classpath, String packageName, int timeoutLimit)
Compile a Java file and return the compilation exit value.- Parameters:
file
- the file to be compiled and executedclasspath
- dependencies and complete classpath to compile and run the Java programpackageName
- the package that the Java file is intimeoutLimit
- number of seconds allowed for the whole test suite to run- Returns:
- the result of compilation (includes status and output)
-
runJavaFile
private static String runJavaFile(Path file, String userClassPath, String packageName, int timeoutLimit)
Run a Java file and return the standard output.- Parameters:
file
- the file to be compiled and executeduserClassPath
- dependencies and complete classpath to compile and run the Java programpackageName
- the package that the Java file is intimeoutLimit
- number of seconds allowed for the Java program to run- Returns:
- standard output from running the Java file
-
getExecutionDirectory
private static Path getExecutionDirectory(Path file, String packageName)
Get directory to execute command in, given file path and package name. Returns aPath
pointing to the directory that the Java file should be executed in.For the simplest case where the Java file is nested in a single package layer, i.e. MyJavaFile.java is in the package mypackage, the folder structure would be src/mypackage/MyJavaFile.java. Here, we need to execute the Java file in the src/ directory. We go up 2 layers from the directory of the Java file to get to the parent directory of the directory for the package. For the general case where MyJavaFile.java is nested within multiple layers of packages, we count the number of separators, i.e. ".", and add 2 to get the number of layers to go up from the Java file's directory.
- Parameters:
file
- the Java file to be executedpackageName
- package name of input Java file- Returns:
- the directory to execute the commands in, or null if packageName is null
-
runProcess
public static Minimize.Outputs runProcess(String command, Path executionDir, int timeoutLimit)
Run a command given as a String and return the output and error results in an Outputs object.- Parameters:
command
- the input command to be runexecutionDir
- the directory where the process commands should be executedtimeoutLimit
- number of seconds allowed for the command to run- Returns:
- an
Outputs
object containing the standard and error output
-
normalizeJUnitOutput
private static Map<String,String> normalizeJUnitOutput(String input)
Normalize the standard output obtained from running a JUnit test suite. By normalizing theString
representation of the output, we remove any extraneous information such as line numbers. The resulting output is a map from method name to the method's failure stack trace.- Parameters:
input
- theString
produced from running a JUnit test suite- Returns:
- a map from method name to the method's failure stack trace. The stack trace will not contain any line numbers.
-
writeToFile
public static void writeToFile(com.github.javaparser.ast.CompilationUnit compilationUnit, Path file) throws IOException
Write a compilation unit to a Java file.- Parameters:
compilationUnit
- the compilation unit to write to filefile
- file to write to- Throws:
IOException
- thrown if write to file fails
-
addImport
private static void addImport(com.github.javaparser.ast.CompilationUnit compilationUnit, String importName)
Add an import to the list ofImportDeclaration
s of the compilation unit. This method adds the import statement if it has not been yet included in the list of current imports of the compilation unit.- Parameters:
compilationUnit
- compilation unit to add import toimportName
- the name of the import
-
sortImports
private static void sortImports(com.github.javaparser.ast.CompilationUnit compilationUnit)
Sort a compilation unit's imports by name.- Parameters:
compilationUnit
- the compilation unit whose imports will be sorted by name
-
getFileLength
private static int getFileLength(Path file) throws IOException
Calculate the length of a file, by number of lines.- Parameters:
file
- the file to compute the length of- Returns:
- the number of lines in the file. Returns -1 if an exception occurs while reading the file.
- Throws:
IOException
- thrown if error reading file
-
cleanUp
private static void cleanUp(Path outputFile, boolean verboseOutput)
Deletes the .class file associated with the outputFile.- Parameters:
outputFile
- the source file for the class file to be removedverboseOutput
- whether to print information about minimization status
-
getNumberOfTestMethods
private static int getNumberOfTestMethods(com.github.javaparser.ast.CompilationUnit compilationUnit)
Return the number of JUnit test methods in a compilation unit.- Parameters:
compilationUnit
- the compilation unit to count the number of unit test methods- Returns:
- the number of unit test methods in compilationUnit
-
printProgress
private static void printProgress(int currentTestIndex, int totalTests, com.github.javaparser.ast.expr.SimpleName testName)
Output the minimizer's current progress.- Parameters:
currentTestIndex
- the number of tests that have been minimized so fartotalTests
- the total number of tests in the input test suitetestName
- the current test method being minimized
-
getOrphanCommentsBeforeThisChildNode
private static void getOrphanCommentsBeforeThisChildNode(com.github.javaparser.ast.Node node, List<com.github.javaparser.ast.comments.Comment> result)
This is stolen from JavaParser's PrettyPrintVisitor.printOrphanCommentsBeforeThisChildNode, with light modifications.- Parameters:
node
- the node whose orphan comments to collectresult
- the list to add orphan comments to. Is side-effected by this method. The implementation uses this to minimize the diffs against upstream.
-
-