There’s been a great deal of discussion over the years about passing command line arguments to tests with Gradle (IE: -Dfoo=bar). The solutions generally boil down to two approaches:
- Adding a check for every supported parameter
test { systemProperty "myPropOne", System.getProperty("myPropOne") systemProperty "myPropTwo", System.getProperty("myPropTwo") systemProperty "myPropThree", System.getProperty("myPropThree") // etc... }
For projects with hundreds of possible parameters this quickly turns into a maintenance nightmare. Every time a parameter is added, removed, or changed in the source code a duplicate effort must be made in the build script(s).
- Passing all system properties onwards
test { systemProperties = System.getProperties() }
This takes all of the current system properties and passes them on to each forked JVM in which the tests will run. Since this includes the properties which were passed via command line it eliminates the headache of having to manually support them (see approach #1). Unfortunately this can bring in all sorts of unwanted properties that can lead to instability when running tests. For instance, the following error only appears for me when I take approach #2:
Exception in thread "main" java.lang.ClassNotFoundException: org.jacoco.agent.rt.internal_932a715.PreMain
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:300)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:397)
FATAL ERROR in native method: processing of -javaagent failed
At this point I’m not happy with either solution #1 or solution #2. Instead I’d like to manually parse the command line arguments passed to Gradle for system properties and allow only those arguments to be passed to my tests via the systemProperties map. Here’s the code which I’d like to use for this:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
test {
String commandLineString = getFullCommandUsed()
// This is basically just Java code. Is there a more 'Groovy' way?
Pattern argPattern = Pattern.compile('-D(.*?)=(.*?)(\\s|$)')
Matcher argMatcher = argPattern.matcher(commandLineString)
while (argMatcher.find()) {
String argKey = argMatcher.group(1)
String argValue = argMatcher.group(2)
systemProperties.put(argKey, argValue)
}
}
I’ve confirmed that this would work by hard-coding the string in place of getFullCommandUsed(). Unfortunately the getFullCommandUsed function doesn’t exist. If it did it would return the command used to run Gradle along with any command line arguments which were passed to it (IE: “./gradlew test -Dfoo=pretty -Dbar=please”).
I’m wondering if there’s some other function or hidden feature in Gradle that will allow me to do this? If not, is there any other way to retrieve only the system properties that were set via command line arguments, or are they merged with the other system properties and thrown away immediately after Gradle starts?