From df9d8465f8ee6486704195e5fbd505b132ea16da Mon Sep 17 00:00:00 2001 From: Aleksei Zotov Date: Sun, 19 Dec 2021 23:19:42 +0400 Subject: [PATCH] junitlauncher - Added fork mode support --- manual/Tasks/junitlauncher.html | 14 ++++- .../taskdefs/optional/junitlauncher.xml | 23 +++++++ .../confined/ForkDefinition.java | 60 +++++++++++++++++++ .../confined/JUnitLauncherTask.java | 30 +++++++++- .../confined/TestDefinition.java | 4 ++ .../junitlauncher/JUnitLauncherTaskTest.java | 20 +++++++ .../junitlauncher/vintage/ForkedTest.java | 1 + 7 files changed, 149 insertions(+), 3 deletions(-) diff --git a/manual/Tasks/junitlauncher.html b/manual/Tasks/junitlauncher.html index 0f20648e41..4736fdd916 100644 --- a/manual/Tasks/junitlauncher.html +++ b/manual/Tasks/junitlauncher.html @@ -582,8 +582,7 @@

testclasses

The fork nested element can be used to run the tests in a newly forked - JVM. All tests that are part of this testclasses element will run in one single - instance of the newly forked JVM. + JVM.

fork

@@ -613,6 +612,17 @@

fork

than this configured value, then the JVM is killed No + + mode + Controls how many JVMs get created if you want to fork some tests. Possible values + are once (the default) and perTest. once creates only a single + JVM for all tests while perTest creates a new JVM for each test suite class. + Note that only tests within the same fork element can share a JVM, so even + if you set mode to once Ant may have to create more than one JVM. +

Since Ant 1.10.13

+ + No; defaults to once + includeJUnitPlatformLibraries If set to true, then the jar files that make up the diff --git a/src/etc/testcases/taskdefs/optional/junitlauncher.xml b/src/etc/testcases/taskdefs/optional/junitlauncher.xml index 87b00adf0a..a469a26b72 100644 --- a/src/etc/testcases/taskdefs/optional/junitlauncher.xml +++ b/src/etc/testcases/taskdefs/optional/junitlauncher.xml @@ -166,6 +166,29 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/ForkDefinition.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/ForkDefinition.java index cfa01d4d8d..d3f33a7b80 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/ForkDefinition.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/ForkDefinition.java @@ -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; @@ -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(); @@ -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(); } @@ -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}; + } + } } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/JUnitLauncherTask.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/JUnitLauncherTask.java index dbe8a1f3ad..ea2c7f90a8 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/JUnitLauncherTask.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/JUnitLauncherTask.java @@ -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 * diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/TestDefinition.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/TestDefinition.java index a3df6d5072..9e5e0f250f 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/TestDefinition.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/TestDefinition.java @@ -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); diff --git a/src/tests/junit/org/apache/tools/ant/taskdefs/optional/junitlauncher/JUnitLauncherTaskTest.java b/src/tests/junit/org/apache/tools/ant/taskdefs/optional/junitlauncher/JUnitLauncherTaskTest.java index 1c36672227..c5e15ca716 100644 --- a/src/tests/junit/org/apache/tools/ant/taskdefs/optional/junitlauncher/JUnitLauncherTaskTest.java +++ b/src/tests/junit/org/apache/tools/ant/taskdefs/optional/junitlauncher/JUnitLauncherTaskTest.java @@ -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 diff --git a/src/tests/junit/org/example/junitlauncher/vintage/ForkedTest.java b/src/tests/junit/org/example/junitlauncher/vintage/ForkedTest.java index 2f432e52b8..d9d95e6639 100644 --- a/src/tests/junit/org/example/junitlauncher/vintage/ForkedTest.java +++ b/src/tests/junit/org/example/junitlauncher/vintage/ForkedTest.java @@ -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"); } }