(experimental) randoop gradle plug-in
This Gradle plug-in creates a task named runRandoop
to run Randoop on Java projects.
To use this plug-in you must apply the Randoop plug-in to the root project’s build.gradle
:
plugins {
id 'java'
id 'maven-publish'
id 'com.sri.gradle.randoop' version '0.0.1-SNAPSHOT'
}
Then, you must specify you must specify a few configuration parameters in your runRandoop
configuration.
For example, the location where the plug-in can find the Randoop tool, the Randoop output directory,
the name of the package for the generated JUnit files, etc.
In the following example, Randoop is in the project's libs
directory, the output directory is
the project's src/test/java
directory, and the JUnit package name is com.foo
:
runRandoop {
randoopJar = file("libs/randoop.jar")
junitOutputDir = file("${projectDir}/src/test/java")
//....
junitPackageName = 'com.foo'
}
You can find a complete example of this configuration below:
plugins {
id 'java'
id 'maven-publish'
id 'com.sri.gradle.randoop' version '0.0.1-SNAPSHOT'
}
repositories {
mavenLocal()
mavenCentral()
maven {
url 'https://plugins.gradle.org/m2/'
}
}
dependencies {
implementation 'com.google.guava:guava:28.0-jre'
implementation 'com.google.code.gson:gson:2.8.6'
testImplementation 'org.hamcrest:hamcrest:2.2'
testImplementation 'junit:junit:4.13'
}
tasks.withType(Javadoc){
enabled = false
}
runRandoop {
randoopJar = file("libs/randoop.jar")
junitOutputDir = file("${projectDir}/src/test/java")
timeoutSeconds = 30
stopOnErrorTest = false
flakyTestBehavior = 'output'
noErrorRevealingTests = true
junitReflectionAllowed = false
usethreads = true
outputLimit = 2000
junitPackageName = 'com.foo'
}
On this runRandoop
extension object, you can change the following settings:
randoopJar
=file. The location where the plugin can find Randoop.junitOutputDir
=file. Randoop output directorytimeoutSeconds
=int. Maximum number of seconds to spend generating tests. Zero means no limit. If nonzero, Randoop is nondeterministic: it may generate different test suites on different runs.stopOnErrorTest
=boolean. Stop generation as soon as one error-revealing test has been generated. (default false)flakyTestBehavior
=enum. What to do if Randoop generates a flaky test. A flaky test is one that behaves differently on different executions. Options include: (1) halt: Randoop halts with a diagnostic message; (2) discard: Discard the flaky test; (3) output: Output the flaky test, but with flaky assertions commented out.noErrorRevealingTests
=boolean. Whether to output error-revealing tests.junitReflectionAllowed
=boolean. Whether to use JUnit's standard reflective mechanisms for invoking tests.usethreads
=boolean. If true, Randoop executes each test in a separate thread and kills tests that take too long to finish.outputLimit
=int. The number of error-revealing and regression tests reaches the output limit.junitPackageName
=string. Name of the package for the generated JUnit files. When the package is the same as the package of a class under test, then package visibility rules are used to determine whether to include the class or class members in a test.
You can build the plug-in locally rather than downloading it from Maven Central.
To build the plug-in from source, run the ./gradlew build
command.
If you want to use a locally-built version of the plug-in, you must add the following configuration to
the settings.gradle
file of your Gradle project:
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
}
}
Then, you can publish the plug-in to your local Maven repository simply by running the ./gradlew publishToMavenLocal
command.
Make sure you run both the clean
and the build
tasks before running this command:
› ./gradlew clean
› ./gradlew build
› ./gradlew publishToMavenLocal
After that, you can use any of the Gradle tasks offered by the Randoop plug-in.
The plug-in offers the following Gradle tasks:
checkForRandoop
- Checks if Randoop is in the CLASSPATH.cleanupRandoopOutput
- Deletes all Randoop-generated tests; including theclasslist.txt
file.generateClassListFile
- Generates a file that lists classes that Randoop will explore to generate tests.generateTests
- Generates tests for a given project using Randoop (Main task).randoopEvidence
- Produces an evidence artifact containing the specific details of the Randoop execution.
As shown above, the main task of this plug-in is the generateTests
task. However, any task can be executed independently.
Additional build properties:
-Pevidence.only
- Tells the plug-in to only generate therandoop-evidence.json
file; i.e., test generation won't be triggered. Assumes the build task has already generated tests using Randoop and thus there is arandoop-summary.txt
file.
If the above property is not provided, then the plug-in will sequentially execute cleanupRandoopOutput
, checkForRandoop
, generateClassListFile
, generateTests
, and randoopEvidence
tasks.
A simple example of how to use this plug-in on a basic Java project can be found at this project's
consumer
sub-directory. Here are the steps for using the plug-in:
- Make sure you publish the plug-in to Maven local first (See steps above)
If build
tasks throws an error, try running the used commands with the --stacktrace
or --debug
options. E.g.,
› ./gradlew clean
› ./gradlew build --stacktrace
› ./gradlew publishToMavenLocal
- Run either
randoopEvidence
orgenerateTests
tasks
If you want to see the plug-in in action, you can just run the generateTests
task. That should be enough.
This task will generate test Java files using Randoop. However, if you want to generate an evidence artifact that provides a summary of a Randoop execution,
which will also run the generateTests
task, then you can run the randoopEvidence
task.
› cd consumer
› ./gradlew randoopEvidence
On any execution, this task will execute all the Gradle tasks offered by this plugin unless the -Pevidence.only
property is appended to this command.
This property tells the plug-in to just re-generate the evidence artifact.
› ./gradlew randoopEvidence -Pevidence.only
Besides generating test Java files, the tasks generateTests
and randoopEvidence
generates the following files:
randoop-summary.txt
, randoop-evidence.json
, RandoopTestsAndMetrics.csv
, RandoopJUnitTestGeneration.csv
, and RandoopToolQualification.csv
.
The first (see next) provides information regarding Randoop execution.
Randoop for Java version "4.2.3, local changes, branch master, commit 6fb16d1, 2020-03-31".
Will explore 2 classes
PUBLIC MEMBERS=6
Explorer = ForwardGenerator(steps: 0, null steps: 0, num_sequences_generated: 0;
allSequences: 0, regresson seqs: 0, error seqs: 0=0=0, invalid seqs: 0, subsumed_sequences: 0, num_failed_output_test: 0;
runtimePrimitivesSeen:38)
Progress update: steps=1, test inputs generated=0, failing inputs=0 (Wed Mar 03 20:13:47 PST 2021 66MB used)
Progress update: steps=1000, test inputs generated=544, failing inputs=0 (Wed Mar 03 20:13:56 PST 2021 1628MB used)
Progress update: steps=2000, test inputs generated=1081, failing inputs=0 (Wed Mar 03 20:14:06 PST 2021 488MB used)
Progress update: steps=3000, test inputs generated=1589, failing inputs=0 (Wed Mar 03 20:14:16 PST 2021 4754MB used)
Progress update: steps=3069, test inputs generated=1629, failing inputs=0 (Wed Mar 03 20:14:17 PST 2021 248MB used)
Normal method executions: 290613
Exceptional method executions: 1
Average method execution time (normal termination): 0.0612
Average method execution time (exceptional termination): 0.186
Approximate memory usage 248MB
Explorer = ForwardGenerator(steps: 3069, null steps: 1440, num_sequences_generated: 1629;
allSequences: 1629, regresson seqs: 1628, error seqs: 0=0=0, invalid seqs: 0, subsumed_sequences: 0, num_failed_output_test: 1;
runtimePrimitivesSeen:38)
About to look for failing assertions in 834 regression sequences.
Regression test output:
Regression test count: 834
Writing regression JUnit tests...
Created file /<user-id>/randoop-gradle-plugin/consumer/src/test/java/com/foo/RegressionTest0.java
Created file /<user-id>/randoop-gradle-plugin/consumer/src/test/java/com/foo/RegressionTest1.java
Created file /<user-id>/randoop-gradle-plugin/consumer/src/test/java/com/foo/RegressionTestDriver.java
Wrote regression JUnit tests.
About to look for flaky methods.
Invalid tests generated: 0
The second file is the evidence summary artifact, which is built using information from the randoop-summary.txt
file.
This file aggregates all the information from the other CSV
files.
{
"Evidence": {
"RandoopJUnitTestGeneration": {
"INVOKEDBY": "RandoopGradlePlugin",
"AUTOMATEDBY": "RandoopGradlePlugin",
"PARAMETERS": "[--time-limit:30, --flaky-test-behavior:output, --output-limit:2000, --usethread:true, --no-error-revealing-tests:true, --stop-on-error-test:false, --junit-reflection-allowed:false, --junit-package-name:com.foo, --junit-output-dir:src/test/java]"
},
"RandoopToolQualification": {
"RANDOOP_VERSION": "4.2.3",
"DATE": "2020-03-31",
"SUMMARY": "Runs the Randoop Tool",
"QUALIFIEDBY": "SRI International",
"INSTALLATION": "https://github.com/SRI-CSL/randoop-gradle-plugin/blob/master/README.md",
"USERGUIDE": "https://github.com/SRI-CSL/randoop-gradle-plugin/blob/master/README.md",
"RANDOOP_PLUGIN_VERSION": "0.1",
"TITLE": "RandoopGradlePlugin",
"ACTIVITY": "TestGeneration"
},
"RandoopTestsAndMetrics": {
"BRANCH": "master",
"EXPLORED_CLASSES": "2",
"COMMIT": "6fb16d1",
"PUBLIC_MEMBERS": "6",
"NORMAL_EXECUTIONS": "314804",
"REGRESSION_TEST_COUNT": "885",
"ERROR_REVEALING_TEST_COUNT": "0",
"AVG_EXCEPTIONAL_TERMINATION_TIME": "0.224",
"MEMORY_USAGE": "4647MB",
"EXCEPTIONAL_EXECUTIONS": "0",
"GENERATED_TEST_FILES_COUNT": "3",
"AVG_NORMAL_TERMINATION_TIME": "0.0572",
"GENERATED_TEST_FILES": [
"src/test/java/com/foo/RegressionTest0.java",
"src/test/java/com/foo/RegressionTest1.java",
"src/test/java/com/foo/RegressionTestDriver.java"
],
"CHANGES": "local",
"INVALID_TESTS_GENERATED": "0",
"NUMBEROFTESTCASES": "885"
}
}
}
Copyright (C) 2020 SRI International
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.