ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Tomas Zezula <>
Subject Re: JUnitTask + JDK9 question
Date Fri, 08 Apr 2016 10:37:56 GMT
Hi Stefan,
sorry for later answer. I was trying to prototype the proposed test methods
in the Ant based project to find out the “best” possibility.
> On 03 Apr 2016, at 12:19, Stefan Bodewig <> wrote:
> Hi Tomas
> what you describe reminds me of the way you write unit tests in .NET -
> where you'd use "public" for the stuff that is exported in modules and
> "internal" for things that you need inside your assembly (module) buto
> don't want to provide as API.
> Blackbox and Whitebox testing are common there, but you also have a way
> to put your tests into a separate assembly and declare this assembly a
> friend that has access to all "internal" APIs. This is the approach I've
> preferred back when I was doing more .NET. IIUC this could be achived
> with -XaddExports in Java9.
In the Java9 there are several ways how to achieve this.
1st) The module can export the package only to friend module by:

module MyLibrary {
  exports to MyLibraryTests;

The package is exported only to MyLibraryTests module.
The limitation of this solution is that both MyLibrary and MyLibraryTests
have to be compiled in single multi-module compilation using -modulesourcepath src:tests
as compiler tests an existence of the module to which the package is exported.

2nd) Compile & Run the tests in classpath mode where both library and tests will be a
of a single unnamed module. Probably not good solution, it’s actually not testing the module
aspects of the library and there may be a problem in exported services.

3rd) -XaddExports as you mentioned which exports packages of a project module (MyLibrary)
to a test module (MyLibraryTest)

4th) -Xpatch which inserts tests (which are no modular, have no module-info) into tested module.

> Needing special precautions to make JUnit see you tests really adds an
> extra layer of complexity.
> One thing you may have overlooked is that Ant doesn't use any built-in
> JUnit runner but one of its own. And that there is an option to run the
> tests inside the same VM that is currently running (fork=false, which
> is the default).

This adds another layer of complexity to unit testing.
The modular testing when modulepath is given will require fork=true.
The JunitTask added jars (ant-launcher.jar:ant.jar:ant-junit.jar:ant-junit4.jar)
complicates situation in case when they are placed on modulepath.
The situation corresponds to the following command line from my first mail:

java \
-mp build/classes/:lib/lib.jar:lib/junit.jar \
-Xpatch:sourceModule=build/test/classes \
-addmods sourceModule,hamcrest.core \
-XaddExports:sourceModule/testPackage=junit \
-XaddReads:sourceModule=junit \
-m junit/org.junit.runner.JUnitCore \

The Ant version now becomes:

java \
-mp build/classes/:lib/junit.jar:lib/hamcrest-core.jar:ant/lib/ant-launcher.jar:ant/lib/ant.jar:ant/lib/ant-junit.jar:ant/lib/ant-junit4.jar
-Xpatch:sourceModule=build/test/classes \
-addmods sourceModule,junit,hamcrest.core,ant,ant.junit4 \
-XaddExports:sourceModule/javaapplication1=ant.junit,sourceModule/javaapplication1=junit \
-XaddReads:sourceModule=junit \
-m ant.junit/ \

The Ant automatic modules has to be enabled by  the -addmods,
the test package has to be exported to ant.junit module as it loads the test classes,
and the class in ant.junit module
is executed.
Unfortunately the described solution does not work and the VM exits with following exception:
java.lang.module.ResolutionException: Modules ant.junit4 and ant.junit export package
to module hamcrest.core
The problem is that both ant.junit and ant.junit4 exports the same package. The split packages
are not supported by JDK9 module system.

Base on this fact I have to change the proposed "Minimal needed changes”.
Even when the junit.jar is only on the modulepath the JunitTask will add the ant libraries
to classpath
and do the classpath execution.
The command line looks like:
java \
-mp build/classes/:lib/junit.jar:lib/hamcrest-core.jar \
-cp ant/lib/ant-launcher.jar:ant/lib/ant.jar:ant/lib/ant-junit.jar:ant/lib/ant-junit4.jar
-Xpatch:sourceModule=build/test/classes \
-addmods sourceModule,junit,hamcrest.core \
-XaddReads:sourceModule=junit \ \

The -XaddExports:sourceModule/javaapplication1=ALL-UNNAMED arg should be added by Ant JUnitTask
as it’s implementation detail of Ant’s JUnitTestRunner added on classpath.

> AFAIU non-forked execution would only work for classpath mode. Period.
> For blackbox testing you'd also have to add -addmods for the jar
> containing Ant's junit runner - which isn't even inside a module and
> can't be unless we repackage it for Java9. In this case we'll likely
> need the approach you outlined for keeping junit on the CLASSPATH.
Right, the situation is the same as in the white box testing.

In our Ant based project I’ve tried all possible combinations of classpath and modulepath
to test JDK9 projects. The best seems to me to have tested project module and required modules
on modulepath,
inline tests into the project module and keep the test frameworks (junit, hamcrest) on classpath.
Keeping test frameworks on classpath may look like strange in module world as they can be
used as automatic modules on module path.
But it brings lots of benefits and only few problems.
1) The content of classpath (which may contain other libraries) is visible to tested module.
1) No problems with split packages.
2) No problems with automatic -> regular module update. For example if someone add module-info
into junit,
it will become regular named module and unit testing command line will require changes.
3) Very easy way how to add custom test framework like mockito. It’s enough to add it to
classpath, no additional
command line options are needed.
4) Shorter command line.

The command line to run JUnit test in this way is:
java \
-modulepath build/classes \
-Xpatch:sourceModule=build/test/classes \
-addmods sourceModule \
-XaddReads:sourceModule=ALL-UNNAMED \
-XaddExports:sourceModule/javaapplication1=ALL-UNNAMED \
-classpath lib/junit-4.12.jar:lib/hamcrest-core-1.3.jar:ant/lib/ant-launcher.jar:ant/lib/ant.jar:ant/lib/ant-junit.jar:ant/lib/ant-junit4.jar

Hopefully helps someone trying to do JDK 9 testing.

> On 2016-04-01, Tomas Zezula wrote:
>> The JUnit task needs to be extended to support all three scenarios.
>> There are two possibilities how to extend the JUnitTask:
>> 1st) Minimal needed changes
>> Just add a modulepath to JUnit task. When junit library is not found
>> on the classpath but found on the module path do the modular execution
>> by -m junit/<mainclz> rather than classpath execution. The user is
>> responsible for passing the -XPatch, -XaddExports, -XaddReads,
>> -addmods JVM options. Options like -XaddExports are harder to
>> calculate as they require list of test packages. But it can be done by
>> the following pathconvert:
>> <dirset id="packages" dir="build/test/classes" includes="*"/>
>> <pathconvert refid="packages" property=“exports.cmd” pathsep=" ">
>>   <chainedmapper>
>>       <flattenmapper/>
>>       <globmapper from="*" to="-XaddExports:${}/*=junit"/>
>>   </chainedmapper>
>> </pathconvert>
> Sounds good but likely needs to extend to ant-junit?.jar as well. 
Described above, the "-XaddExports:${}/*=junit” is replaced by "-XaddExports:${}/*=ALL-UNNAMED"
>> 2nd) Extend Junit task to automatically set the -XPatch, -XaddExports,
>> -XaddReads, -addmods options for modular execution.
>> This will require adding more attributes to JUnit task (path to test
>> classes to do the -XPatch, ability to disable the automatic options).
> And detect whether module support is available at all. In addition the
> VM that is forked may be a different one from the one currently
> running. We could be forking a Java9 VM even if Ant was running in a
> Java8 VM and vice versa. I'd suggest to add an explicit flag users must
> enable before trying to add any magic.
Because the Ant libraries need to stay on ClassPath I will just add modulepath attribute +
and add -XaddExports:testdemodule/testedpackage=ALL-UNNAMED command line argument in case
when junit.jar is not on classpath but modulepath
and explicit flag is set. The flag may be the tested module name which is required by -XaddExport.
— Tomas
> Cheers
>        Stefan
> ---------------------------------------------------------------------
> To unsubscribe, e-mail:
> For additional commands, e-mail:

To unsubscribe, e-mail:
For additional commands, e-mail:

View raw message