Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

junitlauncher - Added fork mode support #174

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions manual/Tasks/junitlauncher.html
Original file line number Diff line number Diff line change
Expand Up @@ -582,8 +582,7 @@ <h4>testclasses</h4>

<p>
The <a href="#fork">fork</a> nested element can be used to run the tests in a newly forked
JVM. All tests that are part of this <code>testclasses</code> element will run in one single
instance of the newly forked JVM.
JVM.
</p>

<h4 id="fork">fork</h4>
Expand Down Expand Up @@ -613,6 +612,17 @@ <h4 id="fork">fork</h4>
than this configured value, then the JVM is killed</td>
<td>No</td>
</tr>
<tr>
<td>mode</td>
<td>Controls how many JVMs get created if you want to fork some tests. Possible values
are <q>once</q> (the default) and <q>perTest</q>. <q>once</q> creates only a single
JVM for all tests while <q>perTest</q> creates a new JVM for each test suite class.
Note that only tests within the same <q>fork</q> element can share a JVM, so even
if you set <var>mode</var> to <q>once</q> Ant may have to create more than one JVM.
<p><em>Since Ant 1.10.13</em></p>
</td>
<td>No; defaults to <q>once</q></td>
</tr>
<tr>
<td>includeJUnitPlatformLibraries</td>
<td>If set to <code>true</code>, then the jar files that make up the
Expand Down
23 changes: 23 additions & 0 deletions src/etc/testcases/taskdefs/optional/junitlauncher.xml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,29 @@
</junitlauncher>
</target>

<target name="test-per-test-fork-mode" depends="init">
<junitlauncher>
<classpath refid="test.classpath"/>
<testclasses outputdir="${output.dir}">
<!-- the same test is duplicated intentionally; if both are run in the same JVM the second one will
fail, it is a nice way to ensure "perTest" mode runs every test suite class in a separate JVM -->
<fileset dir="${build.classes.dir}" includes="org/example/**/junitlauncher/**/ForkedTest.class"/>
<fileset dir="${build.classes.dir}" includes="org/example/**/junitlauncher/**/ForkedTest.class"/>

<listener classname="org.example.junitlauncher.Tracker"
outputDir="${output.dir}"
resultFile="${test-per-test-fork-mode.tracker}"
if="test-per-test-fork-mode.tracker"/>

<fork dir="${basedir}" mode="perTest">
<sysproperty key="junitlauncher.test.sysprop.one" value="forked"/>
</fork>

<listener type="legacy-xml" sendSysErr="true" sendSysOut="true" useLegacyReportingName="false"/>
</testclasses>
</junitlauncher>
</target>

<target name="test-junit-platform-lib-excluded" depends="init">
<junitlauncher>
<classpath refid="junit.engine.jupiter.classpath"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.CommandlineJava;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.Environment;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.PropertySet;
Expand All @@ -42,6 +43,7 @@ public class ForkDefinition {

private String dir;
private long timeout = -1;
private Mode mode = new Mode("once");

ForkDefinition() {
this.commandLineJava = new CommandlineJava();
Expand All @@ -63,14 +65,37 @@ long getTimeout() {
return this.timeout;
}

/**
* Possible values are "once" and "perTest". If set to "once" (the default),
* only a single Java VM will be forked for all tests, with "perTest" each test
* will be run in a fresh Java VM.
* @param mode the mode to use.
* @since Ant 1.10.13
*/
public void setMode(final Mode mode) {
this.mode = mode;
}

public Mode getMode() {
return mode;
}

public void setIncludeJUnitPlatformLibraries(final boolean include) {
this.includeJUnitPlatformLibraries = include;
}

public boolean isIncludeJUnitPlatformLibraries() {
return includeJUnitPlatformLibraries;
}

public void setIncludeAntRuntimeLibraries(final boolean include) {
this.includeAntRuntimeLibraries = include;
}

public boolean isIncludeAntRuntimeLibraries() {
return includeAntRuntimeLibraries;
}

public Commandline.Argument createJvmArg() {
return this.commandLineJava.createVmArgument();
}
Expand Down Expand Up @@ -140,4 +165,39 @@ CommandlineJava generateCommandLine(final JUnitLauncherTask task) {
return cmdLine;
}

/**
* Forking option. There are two available: "once" and "perTest".
* @since Ant 1.10.13
*/
public static final class Mode extends EnumeratedAttribute {

/**
* fork once only
*/
public static final String ONCE = "once";
/**
* fork once per test class
*/
public static final String PER_TEST = "perTest";

/** No arg constructor. */
public Mode() {
super();
}

/**
* Constructor using a value.
* @param value the value to use - once or perTest.
*/
public Mode(final String value) {
super();
setValue(value);
}

/** {@inheritDoc}. */
@Override
public String[] getValues() {
return new String[] {ONCE, PER_TEST};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,41 @@ public void execute() throws BuildException {
continue;
}
if (test.getForkDefinition() != null) {
forkTest(test);
if (test instanceof TestClasses
&& test.getForkDefinition().getMode().getValue().equals(ForkDefinition.Mode.PER_TEST)) {
TestClasses testClasses = (TestClasses) test;
for (String testClassName : testClasses.getTestClassNames()) {
forkTest(convertToSingleTestClass(testClasses, testClassName));
}
} else {
forkTest(test);
}
} else {
launchViaReflection(new InVMLaunch(Collections.singletonList(test)));
}
}
}

private SingleTestClass convertToSingleTestClass(TestClasses testClasses, String testClassName) {
SingleTestClass singleTestClass = new SingleTestClass();
singleTestClass.setName(testClassName);
singleTestClass.setIf(testClasses.ifProperty);
singleTestClass.setUnless(testClasses.unlessProperty);
singleTestClass.setHaltOnFailure(testClasses.haltOnFailure);
singleTestClass.setFailureProperty(testClasses.failureProperty);
singleTestClass.setOutputDir(testClasses.outputDir);
singleTestClass.setIncludeEngines(testClasses.includeEngines);
singleTestClass.setExcludeEngines(testClasses.excludeEngines);

for (ListenerDefinition listener : testClasses.getListeners()) {
singleTestClass.addConfiguredListener(listener);
}

singleTestClass.setForkDefinition(testClasses.getForkDefinition());

return singleTestClass;
}

/**
* Adds the {@link Path} to the classpath which will be used for execution of the tests
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ ForkDefinition getForkDefinition() {
return this.forkDefinition;
}

public void setForkDefinition(ForkDefinition forkDefinition) {
this.forkDefinition = forkDefinition;
}

protected boolean shouldRun(final Project project) {
final PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper(project);
return propertyHelper.testIfCondition(this.ifProperty) && propertyHelper.testUnlessCondition(this.unlessProperty);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,26 @@ public void testBasicFork() throws Exception {
ForkedTest.class.getName(), "testSysProp"));
}

/**
* Tests the execution of a forked test with "perTest" mode
*/
@Test
public void testPerTestForkMode() throws Exception {
final String targetName = "test-per-test-fork-mode";
final Path trackerFile = setupTrackerProperty(targetName);
// setup a dummy and incorrect value of a sysproperty that's used in the test being forked
System.setProperty(ForkedTest.SYS_PROP_ONE, "dummy");
buildRule.executeTarget(targetName);
// verify that our JVM's sysprop value didn't get changed
Assert.assertEquals("System property " + ForkedTest.SYS_PROP_ONE + " was unexpected updated",
"dummy", System.getProperty(ForkedTest.SYS_PROP_ONE));

Assert.assertTrue("At least one run of ForkedTest#testSysProp was expected to succeed",
verifySuccess(trackerFile, ForkedTest.class.getName(), "testSysProp"));
Assert.assertFalse("No runs of ForkedTest#testSysProp was expected to fail",
verifyFailed(trackerFile, ForkedTest.class.getName(), "testSysProp"));
}

/**
* Tests that in a forked mode execution of tests, when the {@code includeJUnitPlatformLibraries} attribute
* is set to false, then the execution of such tests fails with a classloading error for the JUnit platform
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ public class ForkedTest {
public void testSysProp() {
Assert.assertEquals("Unexpected value for system property",
"forked", System.getProperty(SYS_PROP_ONE));
System.setProperty(SYS_PROP_ONE, "changed_inside_fork");
}
}