From 0cb9d22b77dda1dcabba91d4c2a1616d0042d16c Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Wed, 29 Aug 2018 15:26:38 +0530 Subject: [PATCH] [junitlauncher] Allow JUnit libraries to be part of the task's classpath instead of mandating it to be part of Ant's runtime classpath --- .../tools/ant/taskdefs/defaults.properties | 2 +- .../optional/junitlauncher/Constants.java | 54 ------- .../optional/junitlauncher/InVMExecution.java | 50 +++++++ .../junitlauncher/LauncherSupport.java | 104 ++++++++++++-- .../junitlauncher/StandaloneLauncher.java | 32 ++--- .../junitlauncher/TestExecutionContext.java | 1 + .../optional/junitlauncher/TestRequest.java | 2 + .../junitlauncher/confined/Constants.java | 54 +++++++ .../{ => confined}/ForkDefinition.java | 52 ++----- .../confined/JUnitLauncherClassPathUtil.java | 76 ++++++++++ .../{ => confined}/JUnitLauncherTask.java | 132 +++++++++++++----- .../{ => confined}/LaunchDefinition.java | 13 +- .../{ => confined}/ListenerDefinition.java | 26 ++-- .../{ => confined}/NamedTest.java | 2 +- .../{ => confined}/SingleTestClass.java | 37 ++--- .../{ => confined}/TestClasses.java | 34 ++--- .../{ => confined}/TestDefinition.java | 61 ++------ .../junitlauncher/confined/package-info.java | 27 ++++ .../optional/junitlauncher/package-info.java | 28 ++++ .../junitlauncher/JUnitLauncherTaskTest.java | 1 + 20 files changed, 500 insertions(+), 288 deletions(-) delete mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/Constants.java create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/InVMExecution.java create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/Constants.java rename src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/{ => confined}/ForkDefinition.java (68%) create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/JUnitLauncherClassPathUtil.java rename src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/{ => confined}/JUnitLauncherTask.java (69%) rename src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/{ => confined}/LaunchDefinition.java (82%) rename src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/{ => confined}/ListenerDefinition.java (90%) rename src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/{ => confined}/NamedTest.java (93%) rename src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/{ => confined}/SingleTestClass.java (85%) rename src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/{ => confined}/TestClasses.java (86%) rename src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/{ => confined}/TestDefinition.java (65%) create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/package-info.java create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/package-info.java diff --git a/src/main/org/apache/tools/ant/taskdefs/defaults.properties b/src/main/org/apache/tools/ant/taskdefs/defaults.properties index c77c20024..a78cedb34 100644 --- a/src/main/org/apache/tools/ant/taskdefs/defaults.properties +++ b/src/main/org/apache/tools/ant/taskdefs/defaults.properties @@ -161,7 +161,7 @@ jjdoc=org.apache.tools.ant.taskdefs.optional.javacc.JJDoc jjtree=org.apache.tools.ant.taskdefs.optional.javacc.JJTree junit=org.apache.tools.ant.taskdefs.optional.junit.JUnitTask junitreport=org.apache.tools.ant.taskdefs.optional.junit.XMLResultAggregator -junitlauncher=org.apache.tools.ant.taskdefs.optional.junitlauncher.JUnitLauncherTask +junitlauncher=org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.JUnitLauncherTask native2ascii=org.apache.tools.ant.taskdefs.optional.Native2Ascii netrexxc=org.apache.tools.ant.taskdefs.optional.NetRexxC propertyfile=org.apache.tools.ant.taskdefs.optional.PropertyFile diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/Constants.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/Constants.java deleted file mode 100644 index a8b501c19..000000000 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/Constants.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - * - */ - -package org.apache.tools.ant.taskdefs.optional.junitlauncher; - -/** - * Constants used within the junitlauncher task - */ -final class Constants { - - static final int FORK_EXIT_CODE_SUCCESS = 0; - static final int FORK_EXIT_CODE_EXCEPTION = 1; - static final int FORK_EXIT_CODE_TESTS_FAILED = 2; - static final int FORK_EXIT_CODE_TIMED_OUT = 3; - - static final String ARG_PROPERTIES = "--properties"; - static final String ARG_LAUNCH_DEFINITION = "--launch-definition"; - - - static final String LD_XML_ELM_LAUNCH_DEF = "launch-definition"; - static final String LD_XML_ELM_TEST = "test"; - static final String LD_XML_ELM_TEST_CLASSES = "test-classes"; - static final String LD_XML_ATTR_HALT_ON_FAILURE = "haltOnFailure"; - static final String LD_XML_ATTR_OUTPUT_DIRECTORY = "outDir"; - static final String LD_XML_ATTR_INCLUDE_ENGINES = "includeEngines"; - static final String LD_XML_ATTR_EXCLUDE_ENGINES = "excludeEngines"; - static final String LD_XML_ATTR_CLASS_NAME = "classname"; - static final String LD_XML_ATTR_METHODS = "methods"; - static final String LD_XML_ATTR_PRINT_SUMMARY = "printSummary"; - static final String LD_XML_ELM_LISTENER = "listener"; - static final String LD_XML_ATTR_SEND_SYS_ERR = "sendSysErr"; - static final String LD_XML_ATTR_SEND_SYS_OUT = "sendSysOut"; - static final String LD_XML_ATTR_LISTENER_RESULT_FILE = "resultFile"; - - - private Constants() { - - } -} diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/InVMExecution.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/InVMExecution.java new file mode 100644 index 000000000..6b7469af0 --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/InVMExecution.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +package org.apache.tools.ant.taskdefs.optional.junitlauncher; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.JUnitLauncherTask; + +import java.util.Optional; +import java.util.Properties; + +/** + * Used during in-vm (non-forked mode) launching of tests + */ +public class InVMExecution implements TestExecutionContext { + + private final JUnitLauncherTask task; + private final Properties props; + + public InVMExecution(final JUnitLauncherTask task) { + this.task = task; + this.props = new Properties(); + this.props.putAll(task.getProject().getProperties()); + } + + @Override + public Properties getProperties() { + return this.props; + } + + @Override + public Optional getProject() { + return Optional.of(this.task.getProject()); + } +} diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/LauncherSupport.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/LauncherSupport.java index 1260a3bca..e55856af5 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/LauncherSupport.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/LauncherSupport.java @@ -21,12 +21,22 @@ package org.apache.tools.ant.taskdefs.optional.junitlauncher; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.MagicNames; import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.LaunchDefinition; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.ListenerDefinition; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.NamedTest; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.SingleTestClass; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.TestClasses; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.TestDefinition; import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.KeepAliveOutputStream; +import org.junit.platform.engine.Filter; +import org.junit.platform.engine.discovery.DiscoverySelectors; +import org.junit.platform.launcher.EngineFilter; import org.junit.platform.launcher.Launcher; import org.junit.platform.launcher.LauncherDiscoveryRequest; import org.junit.platform.launcher.TestExecutionListener; import org.junit.platform.launcher.TestPlan; +import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; import org.junit.platform.launcher.core.LauncherFactory; import org.junit.platform.launcher.listeners.SummaryGeneratingListener; import org.junit.platform.launcher.listeners.TestExecutionSummary; @@ -67,23 +77,33 @@ import java.util.concurrent.TimeUnit; *

* This class is not thread-safe and isn't expected to be used for launching from * multiple different threads simultaneously. + *

This class is an internal implementation detail of the Ant project and although + * it's a public class, it isn't meant to be used outside of this project. This class + * can be changed, across releases, without any backward compatible guarantees and hence + * shouldn't be used or relied upon outside of this project. */ -class LauncherSupport { +public class LauncherSupport { private final LaunchDefinition launchDefinition; + private final TestExecutionContext testExecutionContext; private boolean testsFailed; /** * Create a {@link LauncherSupport} for the passed {@link LaunchDefinition} * - * @param definition The launch definition which will be used for launching the tests + * @param definition The launch definition which will be used for launching the tests + * @param testExecutionContext The {@link TestExecutionContext} to use for the tests */ - LauncherSupport(final LaunchDefinition definition) { + public LauncherSupport(final LaunchDefinition definition, final TestExecutionContext testExecutionContext) { if (definition == null) { throw new IllegalArgumentException("Launch definition cannot be null"); } + if (testExecutionContext == null) { + throw new IllegalArgumentException("Test execution context cannot be null"); + } this.launchDefinition = definition; + this.testExecutionContext = testExecutionContext; } /** @@ -93,7 +113,7 @@ class LauncherSupport { * an exception, or if any other exception occurred before or after launching * the tests */ - void launch() throws BuildException { + public void launch() throws BuildException { final ClassLoader previousClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(this.launchDefinition.getClassLoader()); @@ -162,7 +182,14 @@ class LauncherSupport { } final List requests = new ArrayList<>(); for (final TestDefinition test : tests) { - final List testRequests = test.createTestRequests(); + final List testRequests; + if (test instanceof SingleTestClass) { + testRequests = createTestRequests((SingleTestClass) test); + } else if (test instanceof TestClasses) { + testRequests = createTestRequests((TestClasses) test); + } else { + throw new BuildException("Unexpected test definition type " + test.getClass().getName()); + } if (testRequests == null || testRequests.isEmpty()) { continue; } @@ -176,7 +203,7 @@ class LauncherSupport { final List applicableListenerElements = test.getListeners().isEmpty() ? this.launchDefinition.getListeners() : test.getListeners(); final List listeners = new ArrayList<>(); - final Optional project = this.launchDefinition.getTestExecutionContext().getProject(); + final Optional project = this.testExecutionContext.getProject(); for (final ListenerDefinition applicableListener : applicableListenerElements) { if (project.isPresent() && !applicableListener.shouldUse(project.get())) { log("Excluding listener " + applicableListener.getClassName() + " since it's not applicable" + @@ -198,7 +225,7 @@ class LauncherSupport { testRequest.closeUponCompletion(resultFormatter); // set the execution context - resultFormatter.setContext(this.launchDefinition.getTestExecutionContext()); + resultFormatter.setContext(this.testExecutionContext); // set the destination output stream for writing out the formatted result final java.nio.file.Path resultOutputFile = getListenerOutputFile(testRequest, formatterDefinition); try { @@ -230,7 +257,7 @@ class LauncherSupport { return Paths.get(test.getOutputDir(), filename); } // neither listener nor the test define a output dir, so use basedir of the project - final TestExecutionContext testExecutionContext = this.launchDefinition.getTestExecutionContext(); + final TestExecutionContext testExecutionContext = this.testExecutionContext; final String baseDir = testExecutionContext.getProperties().getProperty(MagicNames.PROJECT_BASEDIR); return Paths.get(baseDir, filename); } @@ -270,7 +297,7 @@ class LauncherSupport { if (hasTestFailures && test.getFailureProperty() != null) { // if there are test failures and the test is configured to set a property in case // of failure, then set the property to true - final TestExecutionContext testExecutionContext = this.launchDefinition.getTestExecutionContext(); + final TestExecutionContext testExecutionContext = this.testExecutionContext; if (testExecutionContext.getProject().isPresent()) { final Project project = testExecutionContext.getProject().get(); project.setNewProperty(test.getFailureProperty(), "true"); @@ -351,7 +378,7 @@ class LauncherSupport { } private void log(final String message, final Throwable t, final int level) { - final TestExecutionContext testExecutionContext = this.launchDefinition.getTestExecutionContext(); + final TestExecutionContext testExecutionContext = this.testExecutionContext; if (testExecutionContext.getProject().isPresent()) { testExecutionContext.getProject().get().log(message, t, level); return; @@ -364,6 +391,63 @@ class LauncherSupport { } } + + private List createTestRequests(final TestDefinition test) { + // create a TestRequest and add necessary selectors, filters to it + final LauncherDiscoveryRequestBuilder requestBuilder = LauncherDiscoveryRequestBuilder.request(); + final TestRequest request = new TestRequest(test, requestBuilder); + addDiscoverySelectors(request); + addFilters(request); + return Collections.singletonList(request); + } + + private void addDiscoverySelectors(final TestRequest testRequest) { + final TestDefinition test = testRequest.getOwner(); + final LauncherDiscoveryRequestBuilder requestBuilder = testRequest.getDiscoveryRequest(); + if (test instanceof SingleTestClass) { + final SingleTestClass singleTestClass = (SingleTestClass) test; + final String[] methods = singleTestClass.getMethods(); + if (methods == null) { + requestBuilder.selectors(DiscoverySelectors.selectClass(singleTestClass.getName())); + } else { + // add specific methods + for (final String method : methods) { + requestBuilder.selectors(DiscoverySelectors.selectMethod(singleTestClass.getName(), method)); + } + } + return; + } + if (test instanceof TestClasses) { + final TestClasses testClasses = (TestClasses) test; + final List testClassNames = testClasses.getTestClassNames(); + if (testClassNames.isEmpty()) { + return; + } + for (final String testClass : testClassNames) { + requestBuilder.selectors(DiscoverySelectors.selectClass(testClass)); + } + return; + } + } + + /** + * Add necessary {@link Filter JUnit filters} to the {@code testRequest} + * + * @param testRequest The test request + */ + private void addFilters(final TestRequest testRequest) { + final LauncherDiscoveryRequestBuilder requestBuilder = testRequest.getDiscoveryRequest(); + // add any engine filters + final String[] enginesToInclude = testRequest.getOwner().getIncludeEngines(); + if (enginesToInclude != null && enginesToInclude.length > 0) { + requestBuilder.filters(EngineFilter.includeEngines(enginesToInclude)); + } + final String[] enginesToExclude = testRequest.getOwner().getExcludeEngines(); + if (enginesToExclude != null && enginesToExclude.length > 0) { + requestBuilder.filters(EngineFilter.excludeEngines(enginesToExclude)); + } + } + private enum StreamType { SYS_OUT, SYS_ERR diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/StandaloneLauncher.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/StandaloneLauncher.java index 69a1556c8..eb6e064c5 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/StandaloneLauncher.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/StandaloneLauncher.java @@ -20,6 +20,13 @@ package org.apache.tools.ant.taskdefs.optional.junitlauncher; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.JUnitLauncherTask; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.LaunchDefinition; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.ListenerDefinition; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.SingleTestClass; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.TestClasses; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.TestDefinition; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamReader; @@ -37,12 +44,12 @@ import static javax.xml.stream.XMLStreamConstants.END_DOCUMENT; import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; import static javax.xml.stream.XMLStreamConstants.START_DOCUMENT; import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_HALT_ON_FAILURE; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_PRINT_SUMMARY; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ELM_LAUNCH_DEF; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ELM_LISTENER; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ELM_TEST; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ELM_TEST_CLASSES; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_HALT_ON_FAILURE; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_PRINT_SUMMARY; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ELM_LAUNCH_DEF; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ELM_LISTENER; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ELM_TEST; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ELM_TEST_CLASSES; /** * Used for launching forked tests from the {@link JUnitLauncherTask}. @@ -98,8 +105,7 @@ public class StandaloneLauncher { i = i + numArgsConsumed; } - launchDefinition.setTestExecutionContext(forkedExecution); - final LauncherSupport launcherSupport = new LauncherSupport(launchDefinition); + final LauncherSupport launcherSupport = new LauncherSupport(launchDefinition, forkedExecution); try { launcherSupport.launch(); } catch (Throwable t) { @@ -240,19 +246,9 @@ public class StandaloneLauncher { return this; } - public ForkedLaunch setTestExecutionContext(final TestExecutionContext testExecutionContext) { - this.testExecutionContext = testExecutionContext; - return this; - } - @Override public ClassLoader getClassLoader() { return this.getClass().getClassLoader(); } - - @Override - public TestExecutionContext getTestExecutionContext() { - return this.testExecutionContext; - } } } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestExecutionContext.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestExecutionContext.java index 5e5b60843..207c144cb 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestExecutionContext.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestExecutionContext.java @@ -18,6 +18,7 @@ package org.apache.tools.ant.taskdefs.optional.junitlauncher; import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.JUnitLauncherTask; import java.util.Optional; import java.util.Properties; diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestRequest.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestRequest.java index 0f2f67755..ef15536d8 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestRequest.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestRequest.java @@ -17,6 +17,8 @@ */ package org.apache.tools.ant.taskdefs.optional.junitlauncher; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.JUnitLauncherTask; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.TestDefinition; import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; import java.io.Closeable; diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/Constants.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/Constants.java new file mode 100644 index 000000000..53bc0dc2d --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/Constants.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +package org.apache.tools.ant.taskdefs.optional.junitlauncher.confined; + +/** + * Constants used within the junitlauncher task + */ +public final class Constants { + + public static final int FORK_EXIT_CODE_SUCCESS = 0; + public static final int FORK_EXIT_CODE_EXCEPTION = 1; + public static final int FORK_EXIT_CODE_TESTS_FAILED = 2; + public static final int FORK_EXIT_CODE_TIMED_OUT = 3; + + public static final String ARG_PROPERTIES = "--properties"; + public static final String ARG_LAUNCH_DEFINITION = "--launch-definition"; + + + public static final String LD_XML_ELM_LAUNCH_DEF = "launch-definition"; + public static final String LD_XML_ELM_TEST = "test"; + public static final String LD_XML_ELM_TEST_CLASSES = "test-classes"; + public static final String LD_XML_ATTR_HALT_ON_FAILURE = "haltOnFailure"; + public static final String LD_XML_ATTR_OUTPUT_DIRECTORY = "outDir"; + public static final String LD_XML_ATTR_INCLUDE_ENGINES = "includeEngines"; + public static final String LD_XML_ATTR_EXCLUDE_ENGINES = "excludeEngines"; + public static final String LD_XML_ATTR_CLASS_NAME = "classname"; + public static final String LD_XML_ATTR_METHODS = "methods"; + public static final String LD_XML_ATTR_PRINT_SUMMARY = "printSummary"; + public static final String LD_XML_ELM_LISTENER = "listener"; + public static final String LD_XML_ATTR_SEND_SYS_ERR = "sendSysErr"; + public static final String LD_XML_ATTR_SEND_SYS_OUT = "sendSysOut"; + public static final String LD_XML_ATTR_LISTENER_RESULT_FILE = "resultFile"; + + + private Constants() { + + } +} diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/ForkDefinition.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/ForkDefinition.java similarity index 68% rename from src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/ForkDefinition.java rename to src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/ForkDefinition.java index c5c95cc0d..28f7225fa 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/ForkDefinition.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/ForkDefinition.java @@ -16,23 +16,16 @@ * */ -package org.apache.tools.ant.taskdefs.optional.junitlauncher; +package org.apache.tools.ant.taskdefs.optional.junitlauncher.confined; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; -import org.apache.tools.ant.Task; -import org.apache.tools.ant.launch.AntMain; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.StandaloneLauncher; import org.apache.tools.ant.types.Commandline; import org.apache.tools.ant.types.CommandlineJava; import org.apache.tools.ant.types.Environment; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.PropertySet; -import org.apache.tools.ant.util.LoaderUtils; -import org.junit.platform.commons.annotation.Testable; -import org.junit.platform.engine.TestEngine; -import org.junit.platform.launcher.core.LauncherFactory; - -import java.io.File; /** * Represents the {@code fork} element within test definitions of the @@ -124,45 +117,26 @@ public class ForkDefinition { cmdLine.setClassname(StandaloneLauncher.class.getName()); // VM arguments final Project project = task.getProject(); - final Path antRuntimeResourceSources = new Path(project); + final ClassLoader taskClassLoader = task.getClass().getClassLoader(); + // Ant runtime classes if (this.includeAntRuntimeLibraries) { - addAntRuntimeResourceSource(antRuntimeResourceSources, task, toResourceName(AntMain.class)); - addAntRuntimeResourceSource(antRuntimeResourceSources, task, toResourceName(Task.class)); - addAntRuntimeResourceSource(antRuntimeResourceSources, task, toResourceName(JUnitLauncherTask.class)); + final Path antRuntimeResources = new Path(project); + JUnitLauncherClassPathUtil.addAntRuntimeResourceLocations(antRuntimeResources, taskClassLoader); + final Path classPath = cmdLine.createClasspath(project); + classPath.createPath().append(antRuntimeResources); } else { task.log("Excluding Ant runtime libraries from forked JVM classpath", Project.MSG_DEBUG); } + // JUnit platform classes if (this.includeJUnitPlatformLibraries) { - // platform-engine - addAntRuntimeResourceSource(antRuntimeResourceSources, task, toResourceName(TestEngine.class)); - // platform-launcher - addAntRuntimeResourceSource(antRuntimeResourceSources, task, toResourceName(LauncherFactory.class)); - // platform-commons - addAntRuntimeResourceSource(antRuntimeResourceSources, task, toResourceName(Testable.class)); + final Path junitPlatformResources = new Path(project); + JUnitLauncherClassPathUtil.addJUnitPlatformResourceLocations(junitPlatformResources, taskClassLoader); + final Path classPath = cmdLine.createClasspath(project); + classPath.createPath().append(junitPlatformResources); } else { task.log("Excluding JUnit platform libraries from forked JVM classpath", Project.MSG_DEBUG); } - final Path classPath = cmdLine.createClasspath(project); - classPath.createPath().append(antRuntimeResourceSources); - - return cmdLine; } - private static boolean addAntRuntimeResourceSource(final Path path, final JUnitLauncherTask task, final String resource) { - final File f = LoaderUtils.getResourceSource(task.getClass().getClassLoader(), resource); - if (f == null) { - task.log("Could not locate source of resource " + resource); - return false; - } - task.log("Found source " + f + " of resource " + resource); - path.createPath().setLocation(f); - return true; - } - - private static String toResourceName(final Class klass) { - final String name = klass.getName(); - return name.replaceAll("\\.", "/") + ".class"; - } - } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/JUnitLauncherClassPathUtil.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/JUnitLauncherClassPathUtil.java new file mode 100644 index 000000000..dcd4145eb --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/JUnitLauncherClassPathUtil.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +package org.apache.tools.ant.taskdefs.optional.junitlauncher.confined; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.launch.AntMain; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.LoaderUtils; + +import java.io.File; + +/** + * + */ +final class JUnitLauncherClassPathUtil { + + private static final String RESOURCE_IN_PLATFORM_ENGINE = "org/junit/platform/engine/TestEngine.class"; + private static final String RESOURCE_IN_PLATFORM_LAUNCHER = "org/junit/platform/launcher/core/LauncherFactory.class"; + private static final String RESOURCE_IN_PLATFORM_COMMON = "org/junit/platform/commons/annotation/Testable.class"; + private static final String RESOURCE_NAME_LAUNCHER_SUPPORT = "org/apache/tools/ant/taskdefs/optional/junitlauncher/LauncherSupport.class"; + + + static void addAntRuntimeResourceLocations(final Path path, final ClassLoader classLoader) { + addResourceLocationToPath(path, classLoader, toResourceName(AntMain.class)); + addResourceLocationToPath(path, classLoader, toResourceName(Task.class)); + addResourceLocationToPath(path, classLoader, RESOURCE_NAME_LAUNCHER_SUPPORT); + } + + static void addLauncherSupportResourceLocation(final Path path, final ClassLoader classLoader) { + addResourceLocationToPath(path, classLoader, RESOURCE_NAME_LAUNCHER_SUPPORT); + } + + static void addJUnitPlatformResourceLocations(final Path path, final ClassLoader classLoader) { + // platform-engine + addResourceLocationToPath(path, classLoader, RESOURCE_IN_PLATFORM_ENGINE); + // platform-launcher + addResourceLocationToPath(path, classLoader, RESOURCE_IN_PLATFORM_LAUNCHER); + // platform-commons + addResourceLocationToPath(path, classLoader, RESOURCE_IN_PLATFORM_COMMON); + } + + static boolean addResourceLocationToPath(final Path path, final ClassLoader classLoader, final String resource) { + final File f = LoaderUtils.getResourceSource(classLoader, resource); + if (f == null) { + return false; + } + path.createPath().setLocation(f); + return true; + } + + static boolean hasJUnitPlatformResources(final ClassLoader cl) { + final File f = LoaderUtils.getResourceSource(cl, RESOURCE_IN_PLATFORM_ENGINE); + return f != null; + } + + private static String toResourceName(final Class klass) { + final String name = klass.getName(); + return name.replaceAll("\\.", "/") + ".class"; + } +} diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/JUnitLauncherTask.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/JUnitLauncherTask.java similarity index 69% rename from src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/JUnitLauncherTask.java rename to src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/JUnitLauncherTask.java index 028397e87..654211e75 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/JUnitLauncherTask.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/JUnitLauncherTask.java @@ -15,7 +15,7 @@ * limitations under the License. * */ -package org.apache.tools.ant.taskdefs.optional.junitlauncher; +package org.apache.tools.ant.taskdefs.optional.junitlauncher.confined; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; @@ -40,13 +40,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Hashtable; import java.util.List; -import java.util.Optional; import java.util.Properties; import java.util.concurrent.TimeoutException; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_HALT_ON_FAILURE; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_PRINT_SUMMARY; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ELM_LAUNCH_DEF; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_HALT_ON_FAILURE; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_PRINT_SUMMARY; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ELM_LAUNCH_DEF; /** * An Ant {@link Task} responsible for launching the JUnit platform for running tests. @@ -66,6 +65,10 @@ import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_ */ public class JUnitLauncherTask extends Task { + private static final String LAUNCHER_SUPPORT_CLASS_NAME = "org.apache.tools.ant.taskdefs.optional.junitlauncher.LauncherSupport"; + private static final String IN_VM_TEST_EXECUTION_CONTEXT_CLASS_NAME = "org.apache.tools.ant.taskdefs.optional.junitlauncher.InVMExecution"; + private static final String TEST_EXECUTION_CONTEXT_CLASS_NAME = "org.apache.tools.ant.taskdefs.optional.junitlauncher.TestExecutionContext"; + private Path classPath; private boolean haltOnFailure; private String failureProperty; @@ -91,8 +94,7 @@ public class JUnitLauncherTask extends Task { if (test.getForkDefinition() != null) { forkTest(test); } else { - final LauncherSupport launcherSupport = new LauncherSupport(new InVMLaunch(Collections.singletonList(test))); - launcherSupport.launch(); + launchViaReflection(new InVMLaunch(Collections.singletonList(test))); } } } @@ -165,14 +167,29 @@ public class JUnitLauncherTask extends Task { } } - private ClassLoader createClassLoaderForTestExecution() { - if (this.classPath == null) { - return this.getClass().getClassLoader(); + private void launchViaReflection(final InVMLaunch launchDefinition) { + final ClassLoader cl = launchDefinition.getClassLoader(); + // instantiate a new TestExecutionContext instance using the launch definition's classloader + final Class testExecutionCtxClass; + final Object testExecutionCtx; + try { + testExecutionCtxClass = Class.forName(TEST_EXECUTION_CONTEXT_CLASS_NAME, false, cl); + final Class klass = Class.forName(IN_VM_TEST_EXECUTION_CONTEXT_CLASS_NAME, false, cl); + testExecutionCtx = klass.getConstructor(JUnitLauncherTask.class).newInstance(this); + } catch (Exception e) { + throw new BuildException("Failed to create a test execution context for in-vm tests", e); + } + // instantiate a new LauncherSupport instance using the launch definition's ClassLoader + try { + final Class klass = Class.forName(LAUNCHER_SUPPORT_CLASS_NAME, false, cl); + final Object launcherSupport = klass.getConstructor(LaunchDefinition.class, testExecutionCtxClass) + .newInstance(launchDefinition, testExecutionCtx); + klass.getMethod("launch").invoke(launcherSupport); + } catch (Exception e) { + throw new BuildException("Failed to launch in-vm tests", e); } - return new AntClassLoader(this.getClass().getClassLoader(), getProject(), this.classPath, true); } - private java.nio.file.Path dumpProjectProperties() throws IOException { final java.nio.file.Path propsPath = Files.createTempFile(null, "properties"); propsPath.toFile().deleteOnExit(); @@ -312,35 +329,14 @@ public class JUnitLauncherTask extends Task { return xmlFilePath; } - private final class InVMExecution implements TestExecutionContext { - - private final Properties props; - - InVMExecution() { - this.props = new Properties(); - this.props.putAll(JUnitLauncherTask.this.getProject().getProperties()); - } - - @Override - public Properties getProperties() { - return this.props; - } - - @Override - public Optional getProject() { - return Optional.of(JUnitLauncherTask.this.getProject()); - } - } - private final class InVMLaunch implements LaunchDefinition { - private final TestExecutionContext testExecutionContext = new InVMExecution(); private final List inVMTests; private final ClassLoader executionCL; private InVMLaunch(final List inVMTests) { this.inVMTests = inVMTests; - this.executionCL = createClassLoaderForTestExecution(); + this.executionCL = createInVMExecutionClassLoader(); } @Override @@ -368,9 +364,73 @@ public class JUnitLauncherTask extends Task { return this.executionCL; } + private ClassLoader createInVMExecutionClassLoader() { + final Path taskConfiguredClassPath = JUnitLauncherTask.this.classPath; + if (taskConfiguredClassPath == null) { + // no specific classpath configured for the task, so use the classloader + // of this task + return JUnitLauncherTask.class.getClassLoader(); + } + // there's a classpath configured for the task. + // we first check if the Ant runtime classpath has JUnit platform classes. + // - if it does, then we use the Ant runtime classpath plus the task's configured classpath + // with the traditional parent first loading. + // - else (i.e. Ant runtime classpath doesn't have JUnit platform classes), then we + // expect/assume the task's configured classpath to have the JUnit platform classes and we + // then create a "overriding" classloader which prefers certain resources (specifically the classes + // from org.apache.tools.ant.taskdefs.optional.junitlauncher package), from the task's + // classpath, even if the Ant's runtime classpath has those resources. + if (JUnitLauncherClassPathUtil.hasJUnitPlatformResources(JUnitLauncherTask.class.getClassLoader())) { + return new AntClassLoader(JUnitLauncherTask.class.getClassLoader(), getProject(), taskConfiguredClassPath, true); + } + final Path cp = new Path(getProject()); + cp.add(taskConfiguredClassPath); + // add the Ant runtime resources to this path + JUnitLauncherClassPathUtil.addLauncherSupportResourceLocation(cp, JUnitLauncherTask.class.getClassLoader()); + return new TaskConfiguredPathClassLoader(JUnitLauncherTask.class.getClassLoader(), cp, getProject()); + } + } + + /** + * A {@link ClassLoader}, very similar to the {@link org.apache.tools.ant.util.SplitClassLoader}, + * which uses the {@link #TaskConfiguredPathClassLoader(ClassLoader, Path, Project) configured Path} + * to load a class, if the class belongs to the {@code org.apache.tools.ant.taskdefs.optional.junitlauncher} + * package. + *

+ * While looking for classes belonging to the {@code org.apache.tools.ant.taskdefs.optional.junitlauncher} + * package, this classloader completely ignores Ant runtime classpath, even if that classpath has + * those classes. This allows the users of this classloader to use a custom location and thus more control over + * where these classes reside, when running the {@code junitlauncher} task + */ + private final class TaskConfiguredPathClassLoader extends AntClassLoader { + + /** + * @param parent ClassLoader + * @param path Path + * @param project Project + */ + private TaskConfiguredPathClassLoader(ClassLoader parent, Path path, Project project) { + super(parent, project, path, true); + } + + // forceLoadClass is not convenient here since it would not + // properly deal with inner classes of these classes. @Override - public TestExecutionContext getTestExecutionContext() { - return this.testExecutionContext; + protected synchronized Class loadClass(String classname, boolean resolve) + throws ClassNotFoundException { + Class theClass = findLoadedClass(classname); + if (theClass != null) { + return theClass; + } + final String packageName = classname.substring(0, classname.lastIndexOf('.')); + if (packageName.equals("org.apache.tools.ant.taskdefs.optional.junitlauncher")) { + theClass = findClass(classname); + if (resolve) { + resolveClass(theClass); + } + return theClass; + } + return super.loadClass(classname, resolve); } } } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/LaunchDefinition.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/LaunchDefinition.java similarity index 82% rename from src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/LaunchDefinition.java rename to src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/LaunchDefinition.java index 3dd5350f0..10e7f4d7f 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/LaunchDefinition.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/LaunchDefinition.java @@ -16,7 +16,7 @@ * */ -package org.apache.tools.ant.taskdefs.optional.junitlauncher; +package org.apache.tools.ant.taskdefs.optional.junitlauncher.confined; import java.util.List; @@ -28,42 +28,31 @@ public interface LaunchDefinition { /** * @return Returns the {@link TestDefinition tests} that have to be launched - * */ List getTests(); /** * @return Returns the default {@link ListenerDefinition listeners} that will be used * for the tests, if the {@link #getTests() tests} themselves don't specify any - * */ List getListeners(); /** * @return Returns true if a summary needs to be printed out after the execution of the * tests. False otherwise. - * */ boolean isPrintSummary(); /** * @return Returns true if any remaining tests launch need to be stopped if any test execution * failed. False otherwise. - * */ boolean isHaltOnFailure(); /** * @return Returns the {@link ClassLoader} that has to be used for launching and execution of the * tests - * */ ClassLoader getClassLoader(); - /** - * @return Returns the {@link TestExecutionContext} that will be passed to {@link TestResultFormatter#setContext(TestExecutionContext) - * result formatters} which are applicable during the execution of the tests. - * - */ - TestExecutionContext getTestExecutionContext(); } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/ListenerDefinition.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/ListenerDefinition.java similarity index 90% rename from src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/ListenerDefinition.java rename to src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/ListenerDefinition.java index b2a3dba67..9eaf14734 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/ListenerDefinition.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/ListenerDefinition.java @@ -15,7 +15,7 @@ * limitations under the License. * */ -package org.apache.tools.ant.taskdefs.optional.junitlauncher; +package org.apache.tools.ant.taskdefs.optional.junitlauncher.confined; import org.apache.tools.ant.Project; import org.apache.tools.ant.PropertyHelper; @@ -26,11 +26,11 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_CLASS_NAME; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_LISTENER_RESULT_FILE; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_SEND_SYS_ERR; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_SEND_SYS_OUT; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ELM_LISTENER; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_CLASS_NAME; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_LISTENER_RESULT_FILE; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_SEND_SYS_ERR; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_SEND_SYS_OUT; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ELM_LISTENER; /** * Represents the {@code <listener>} element within the {@code <junitlauncher>} @@ -59,7 +59,7 @@ public class ListenerDefinition { this.className = className; } - String getClassName() { + public String getClassName() { return this.className; } @@ -100,7 +100,7 @@ public class ListenerDefinition { this.resultFile = filename; } - String requireResultFile(final TestDefinition test) { + public String requireResultFile(final TestDefinition test) { if (this.resultFile != null) { return this.resultFile; } @@ -125,7 +125,7 @@ public class ListenerDefinition { this.sendSysOut = sendSysOut; } - boolean shouldSendSysOut() { + public boolean shouldSendSysOut() { return this.sendSysOut; } @@ -133,7 +133,7 @@ public class ListenerDefinition { this.sendSysErr = sendSysErr; } - boolean shouldSendSysErr() { + public boolean shouldSendSysErr() { return this.sendSysErr; } @@ -147,11 +147,11 @@ public class ListenerDefinition { this.outputDir = dir; } - String getOutputDir() { + public String getOutputDir() { return this.outputDir; } - protected boolean shouldUse(final Project project) { + public boolean shouldUse(final Project project) { final PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper(project); return propertyHelper.testIfCondition(this.ifProperty) && propertyHelper.testUnlessCondition(this.unlessProperty); } @@ -175,7 +175,7 @@ public class ListenerDefinition { writer.writeEndElement(); } - static ListenerDefinition fromForkedRepresentation(final XMLStreamReader reader) throws XMLStreamException { + public static ListenerDefinition fromForkedRepresentation(final XMLStreamReader reader) throws XMLStreamException { reader.require(XMLStreamConstants.START_ELEMENT, null, LD_XML_ELM_LISTENER); final ListenerDefinition listenerDef = new ListenerDefinition(); final String className = requireAttributeValue(reader, LD_XML_ATTR_CLASS_NAME); diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/NamedTest.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/NamedTest.java similarity index 93% rename from src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/NamedTest.java rename to src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/NamedTest.java index 07039a6bd..295acd971 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/NamedTest.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/NamedTest.java @@ -15,7 +15,7 @@ * limitations under the License. * */ -package org.apache.tools.ant.taskdefs.optional.junitlauncher; +package org.apache.tools.ant.taskdefs.optional.junitlauncher.confined; /** * A test that has a name associated with it diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/SingleTestClass.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/SingleTestClass.java similarity index 85% rename from src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/SingleTestClass.java rename to src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/SingleTestClass.java index 0321c48d4..7c0186ff3 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/SingleTestClass.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/SingleTestClass.java @@ -15,10 +15,7 @@ * limitations under the License. * */ -package org.apache.tools.ant.taskdefs.optional.junitlauncher; - -import org.junit.platform.engine.discovery.DiscoverySelectors; -import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; +package org.apache.tools.ant.taskdefs.optional.junitlauncher.confined; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; @@ -29,13 +26,13 @@ import java.util.LinkedHashSet; import java.util.Set; import java.util.StringTokenizer; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_CLASS_NAME; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_EXCLUDE_ENGINES; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_HALT_ON_FAILURE; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_INCLUDE_ENGINES; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_METHODS; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_OUTPUT_DIRECTORY; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ELM_TEST; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_CLASS_NAME; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_EXCLUDE_ENGINES; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_HALT_ON_FAILURE; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_INCLUDE_ENGINES; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_METHODS; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_OUTPUT_DIRECTORY; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ELM_TEST; /** * Represents the single {@code test} (class) that's configured to be launched by the {@link JUnitLauncherTask} @@ -86,27 +83,13 @@ public class SingleTestClass extends TestDefinition implements NamedTest { return this.testMethods != null && !this.testMethods.isEmpty(); } - String[] getMethods() { + public String[] getMethods() { if (!hasMethodsSpecified()) { return null; } return this.testMethods.toArray(new String[this.testMethods.size()]); } - @Override - void addDiscoverySelectors(final TestRequest testRequest) { - final LauncherDiscoveryRequestBuilder requestBuilder = testRequest.getDiscoveryRequest(); - if (!this.hasMethodsSpecified()) { - requestBuilder.selectors(DiscoverySelectors.selectClass(this.testClass)); - } else { - // add specific methods - for (final String method : this.getMethods()) { - requestBuilder.selectors(DiscoverySelectors.selectMethod(this.testClass, method)); - } - } - } - - @Override protected void toForkedRepresentation(final JUnitLauncherTask task, final XMLStreamWriter writer) throws XMLStreamException { writer.writeStartElement(LD_XML_ELM_TEST); @@ -146,7 +129,7 @@ public class SingleTestClass extends TestDefinition implements NamedTest { writer.writeEndElement(); } - static TestDefinition fromForkedRepresentation(final XMLStreamReader reader) throws XMLStreamException { + public static TestDefinition fromForkedRepresentation(final XMLStreamReader reader) throws XMLStreamException { reader.require(XMLStreamConstants.START_ELEMENT, null, LD_XML_ELM_TEST); final SingleTestClass testDefinition = new SingleTestClass(); final String testClassName = requireAttributeValue(reader, LD_XML_ATTR_CLASS_NAME); diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestClasses.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/TestClasses.java similarity index 86% rename from src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestClasses.java rename to src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/TestClasses.java index 1769d10ad..f119a348b 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestClasses.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/TestClasses.java @@ -15,14 +15,12 @@ * limitations under the License. * */ -package org.apache.tools.ant.taskdefs.optional.junitlauncher; +package org.apache.tools.ant.taskdefs.optional.junitlauncher.confined; import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.types.ResourceCollection; import org.apache.tools.ant.types.resources.Resources; import org.apache.tools.ant.types.resources.StringResource; -import org.junit.platform.engine.discovery.DiscoverySelectors; -import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; @@ -33,13 +31,13 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_CLASS_NAME; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_EXCLUDE_ENGINES; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_HALT_ON_FAILURE; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_INCLUDE_ENGINES; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ATTR_OUTPUT_DIRECTORY; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ELM_TEST; -import static org.apache.tools.ant.taskdefs.optional.junitlauncher.Constants.LD_XML_ELM_TEST_CLASSES; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_CLASS_NAME; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_EXCLUDE_ENGINES; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_HALT_ON_FAILURE; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_INCLUDE_ENGINES; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ATTR_OUTPUT_DIRECTORY; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ELM_TEST; +import static org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.Constants.LD_XML_ELM_TEST_CLASSES; /** * Represents a {@code testclasses} that's configured to be launched by the {@link JUnitLauncherTask} @@ -56,19 +54,7 @@ public class TestClasses extends TestDefinition { this.resources.add(resourceCollection); } - @Override - void addDiscoverySelectors(final TestRequest testRequest) { - final List tests = getTestClassNames(); - if (tests.isEmpty()) { - return; - } - final LauncherDiscoveryRequestBuilder requestBuilder = testRequest.getDiscoveryRequest(); - for (final String test : tests) { - requestBuilder.selectors(DiscoverySelectors.selectClass(test)); - } - } - - private List getTestClassNames() { + public List getTestClassNames() { if (this.resources.isEmpty()) { return Collections.emptyList(); } @@ -122,7 +108,7 @@ public class TestClasses extends TestDefinition { writer.writeEndElement(); } - static List fromForkedRepresentation(final XMLStreamReader reader) throws XMLStreamException { + public static List fromForkedRepresentation(final XMLStreamReader reader) throws XMLStreamException { reader.require(XMLStreamConstants.START_ELEMENT, null, LD_XML_ELM_TEST_CLASSES); final TestClasses testDefinition = new TestClasses(); // read out as multiple SingleTestClass representations diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestDefinition.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/TestDefinition.java similarity index 65% rename from src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestDefinition.java rename to src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/TestDefinition.java index 4a42b6aab..e2403e5d3 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/TestDefinition.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/TestDefinition.java @@ -15,14 +15,11 @@ * limitations under the License. * */ -package org.apache.tools.ant.taskdefs.optional.junitlauncher; +package org.apache.tools.ant.taskdefs.optional.junitlauncher.confined; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.PropertyHelper; -import org.junit.platform.engine.Filter; -import org.junit.platform.launcher.EngineFilter; -import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; @@ -33,7 +30,7 @@ import java.util.List; /** * Represents the configuration details of a test that needs to be launched by the {@link JUnitLauncherTask} */ -abstract class TestDefinition { +public abstract class TestDefinition { protected String ifProperty; protected String unlessProperty; @@ -62,7 +59,7 @@ abstract class TestDefinition { this.unlessProperty = unlessProperty; } - boolean isHaltOnFailure() { + public boolean isHaltOnFailure() { return this.haltOnFailure != null && this.haltOnFailure; } @@ -74,7 +71,7 @@ abstract class TestDefinition { this.haltOnFailure = haltonfailure; } - String getFailureProperty() { + public String getFailureProperty() { return failureProperty; } @@ -86,7 +83,7 @@ abstract class TestDefinition { this.listeners.add(listener); } - List getListeners() { + public List getListeners() { return Collections.unmodifiableList(this.listeners); } @@ -94,7 +91,7 @@ abstract class TestDefinition { this.outputDir = dir; } - String getOutputDir() { + public String getOutputDir() { return this.outputDir; } @@ -110,54 +107,12 @@ abstract class TestDefinition { return this.forkDefinition; } - /** - * Create and return the {@link TestRequest TestRequests} for this test definition. This - * typically involves creating the JUnit test discovery request(s) and applying the necessary - * discovery selectors, filters and other necessary constructs. - * - * @return Returns the test requests - */ - List createTestRequests() { - // create a TestRequest and add necessary selectors, filters to it - final LauncherDiscoveryRequestBuilder requestBuilder = LauncherDiscoveryRequestBuilder.request(); - final TestRequest request = new TestRequest(this, requestBuilder); - addDiscoverySelectors(request); - addFilters(request); - return Collections.singletonList(request); - } - - /** - * Add necessary {@link org.junit.platform.engine.DiscoverySelector JUnit discovery selectors} to - * the {@code testRequest} - * - * @param testRequest The test request - */ - abstract void addDiscoverySelectors(final TestRequest testRequest); - - /** - * Add necessary {@link Filter JUnit filters} to the {@code testRequest} - * - * @param testRequest The test request - */ - void addFilters(final TestRequest testRequest) { - final LauncherDiscoveryRequestBuilder requestBuilder = testRequest.getDiscoveryRequest(); - // add any engine filters - final String[] enginesToInclude = this.getIncludeEngines(); - if (enginesToInclude != null && enginesToInclude.length > 0) { - requestBuilder.filters(EngineFilter.includeEngines(enginesToInclude)); - } - final String[] enginesToExclude = this.getExcludeEngines(); - if (enginesToExclude != null && enginesToExclude.length > 0) { - requestBuilder.filters(EngineFilter.excludeEngines(enginesToExclude)); - } - } - protected boolean shouldRun(final Project project) { final PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper(project); return propertyHelper.testIfCondition(this.ifProperty) && propertyHelper.testUnlessCondition(this.unlessProperty); } - String[] getIncludeEngines() { + public String[] getIncludeEngines() { return includeEngines == null ? new String[0] : split(this.includeEngines, ","); } @@ -165,7 +120,7 @@ abstract class TestDefinition { this.includeEngines = includeEngines; } - String[] getExcludeEngines() { + public String[] getExcludeEngines() { return excludeEngines == null ? new String[0] : split(this.excludeEngines, ","); } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/package-info.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/package-info.java new file mode 100644 index 000000000..773c7b1c5 --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/confined/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ +/** + * The classes/interfaces in this package must not + * have any compile time dependency on any of JUnit platform or + * engine classes/interfaces. They must not even have any + * compile time dependency on any classes/interfaces that belong to the + * {@link org.apache.tools.ant.taskdefs.optional.junitlauncher} package. + * + * @since Ant 1.10.6 + */ +package org.apache.tools.ant.taskdefs.optional.junitlauncher.confined; \ No newline at end of file diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/package-info.java b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/package-info.java new file mode 100644 index 000000000..df75b9bdb --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/package-info.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ +/** + * The classes/interfaces in this package are allowed + * to have compile time dependency on JUnit platform classes/interfaces. + * Furthermore classes/interfaces in this package are also + * allowed to have compile time dependency on the + * {@link org.apache.tools.ant.taskdefs.optional.junitlauncher.confined} + * package + * + * @since Ant 1.10.6 + */ +package org.apache.tools.ant.taskdefs.optional.junitlauncher; \ No newline at end of file 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 97a40c184..024a87bb5 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 @@ -20,6 +20,7 @@ package org.apache.tools.ant.taskdefs.optional.junitlauncher; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildFileRule; import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.JUnitLauncherTask; import org.apache.tools.ant.util.LoaderUtils; import org.example.junitlauncher.jupiter.JupiterSampleTest; import org.example.junitlauncher.vintage.AlwaysFailingJUnit4Test;