You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by jw...@apache.org on 2017/07/01 15:54:59 UTC
groovy git commit: GROOVY-8197: Make JUnit3/4 GroovyRunners (closes
#564)
Repository: groovy
Updated Branches:
refs/heads/master 85af40b3e -> a2fb91d60
GROOVY-8197: Make JUnit3/4 GroovyRunners (closes #564)
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/a2fb91d6
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/a2fb91d6
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/a2fb91d6
Branch: refs/heads/master
Commit: a2fb91d60ff6a917a86d1fd0f7266206dc0508fc
Parents: 85af40b
Author: John Wagenleitner <jw...@apache.org>
Authored: Sat Jul 1 08:45:32 2017 -0700
Committer: John Wagenleitner <jw...@apache.org>
Committed: Sat Jul 1 08:46:51 2017 -0700
----------------------------------------------------------------------
src/main/groovy/grape/GrapeIvy.groovy | 46 +-
src/main/groovy/lang/GroovyShell.java | 151 +------
src/main/groovy/lang/GroovySystem.java | 9 +-
.../apache/groovy/plugin/DefaultRunners.java | 218 ++++++++++
.../org/apache/groovy/plugin/GroovyRunner.java | 49 +++
.../groovy/plugin/GroovyRunnerRegistry.java | 436 +++++++++++++++++++
.../codehaus/groovy/plugin/GroovyRunner.java | 10 +-
.../groovy/vmplugin/v5/JUnit4Utils.java | 4 +-
.../plugin/GroovyRunnerRegistryTest.groovy | 192 ++++++++
subprojects/groovy-testng/build.gradle | 1 +
.../groovy/plugin/testng/TestNgRunner.java | 86 ++++
.../codehaus/groovy/testng/TestNgRunner.java | 74 +---
.../org.apache.groovy.plugin.GroovyRunner | 20 +
.../org.codehaus.groovy.plugins.Runners | 16 -
.../plugin/testng/TestNgRunnerTest.groovy | 41 ++
15 files changed, 1114 insertions(+), 239 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/src/main/groovy/grape/GrapeIvy.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/grape/GrapeIvy.groovy b/src/main/groovy/grape/GrapeIvy.groovy
index c7f7b45..f3e0958 100644
--- a/src/main/groovy/grape/GrapeIvy.groovy
+++ b/src/main/groovy/grape/GrapeIvy.groovy
@@ -18,6 +18,9 @@
*/
package groovy.grape
+import org.apache.groovy.plugin.GroovyRunner
+import org.apache.groovy.plugin.GroovyRunnerRegistry
+
import java.util.regex.Pattern
import org.apache.ivy.Ivy
import org.apache.ivy.core.cache.ResolutionCacheManager
@@ -62,6 +65,9 @@ class GrapeIvy implements GrapeEngine {
static final int DEFAULT_DEPTH = 3
+ private static final String METAINF_PREFIX = 'META-INF/services/'
+ private static final String RUNNER_PROVIDER_CONFIG = GroovyRunner.class.getName()
+
private final exclusiveGrabArgs = [
['group', 'groupId', 'organisation', 'organization', 'org'],
['module', 'artifactId', 'artifact'],
@@ -258,11 +264,18 @@ class GrapeIvy implements GrapeEngine {
for (URI uri in uris) {
loader.addURL(uri.toURL())
}
+ boolean runnerServicesFound = false
for (URI uri in uris) {
//TODO check artifact type, jar vs library, etc
File file = new File(uri)
processCategoryMethods(loader, file)
- processOtherServices(loader, file)
+ Collection<String> services = processMetaInfServices(loader, file)
+ if (!runnerServicesFound) {
+ runnerServicesFound = services.contains(RUNNER_PROVIDER_CONFIG)
+ }
+ }
+ if (runnerServicesFound) {
+ GroovyRunnerRegistry.getInstance().load(loader)
}
} catch (Exception e) {
// clean-up the state first
@@ -313,20 +326,44 @@ class GrapeIvy implements GrapeEngine {
}
void processOtherServices(ClassLoader loader, File f) {
+ processMetaInfServices(loader, f) // ignore result
+ }
+
+ /**
+ * Searches the given File for known service provider
+ * configuration files to process.
+ *
+ * @param loader used to locate service provider files
+ * @param f ZipFile in which to search for services
+ * @return a collection of service provider files that were found
+ */
+ private Collection<String> processMetaInfServices(ClassLoader loader, File f) {
+ List<String> services = new ArrayList<>()
try {
ZipFile zf = new ZipFile(f)
- ZipEntry serializedCategoryMethods = zf.getEntry("META-INF/services/org.codehaus.groovy.runtime.SerializedCategoryMethods")
+ String providerConfig = 'org.codehaus.groovy.runtime.SerializedCategoryMethods'
+ ZipEntry serializedCategoryMethods = zf.getEntry(METAINF_PREFIX + providerConfig)
if (serializedCategoryMethods != null) {
+ services.add(providerConfig)
processSerializedCategoryMethods(zf.getInputStream(serializedCategoryMethods))
}
- ZipEntry pluginRunners = zf.getEntry("META-INF/services/org.codehaus.groovy.plugins.Runners")
+ // TODO: remove in a future release (replaced by GroovyRunnerRegistry)
+ providerConfig = 'org.codehaus.groovy.plugins.Runners'
+ ZipEntry pluginRunners = zf.getEntry(METAINF_PREFIX + providerConfig)
if (pluginRunners != null) {
+ services.add(providerConfig)
processRunners(zf.getInputStream(pluginRunners), f.getName(), loader)
}
+ // GroovyRunners are loaded per ClassLoader using a ServiceLoader so here
+ // it only needs to be indicated that the service provider file was found
+ if (zf.getEntry(METAINF_PREFIX + RUNNER_PROVIDER_CONFIG) != null) {
+ services.add(RUNNER_PROVIDER_CONFIG)
+ }
} catch(ZipException ignore) {
// ignore files we can't process, e.g. non-jar/zip artifacts
// TODO log a warning
}
+ return services
}
void processSerializedCategoryMethods(InputStream is) {
@@ -336,9 +373,10 @@ class GrapeIvy implements GrapeEngine {
}
void processRunners(InputStream is, String name, ClassLoader loader) {
+ GroovyRunnerRegistry registry = GroovyRunnerRegistry.getInstance()
is.text.readLines()*.trim().findAll{ !it.isEmpty() && it[0] != '#' }.each {
try {
- GroovySystem.RUNNER_REGISTRY[name] = loader.loadClass(it).newInstance()
+ registry[name] = loader.loadClass(it).newInstance()
} catch (Exception ex) {
throw new IllegalStateException("Error registering runner class '" + it + "'", ex)
}
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/src/main/groovy/lang/GroovyShell.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/lang/GroovyShell.java b/src/main/groovy/lang/GroovyShell.java
index 1d881ae..7b404cb 100644
--- a/src/main/groovy/lang/GroovyShell.java
+++ b/src/main/groovy/lang/GroovyShell.java
@@ -21,9 +21,10 @@ package groovy.lang;
import groovy.ui.GroovyMain;
import groovy.security.GroovyCodeSourcePermission;
+import org.apache.groovy.plugin.GroovyRunner;
+import org.apache.groovy.plugin.GroovyRunnerRegistry;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilerConfiguration;
-import org.codehaus.groovy.plugin.GroovyRunner;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.InvokerInvocationException;
@@ -36,7 +37,6 @@ import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.List;
-import java.util.Map;
/**
* Represents a groovy shell capable of running arbitrary groovy scripts
@@ -290,21 +290,9 @@ public class GroovyShell extends GroovyObjectSupport {
if (Runnable.class.isAssignableFrom(scriptClass)) {
return runRunnable(scriptClass, args);
}
- // if it's a JUnit 3.8.x test, run it with an appropriate runner
- if (isJUnit3Test(scriptClass)) {
- return runJUnit3Test(scriptClass);
- }
- // if it's a JUnit 3.8.x test suite, run it with an appropriate runner
- if (isJUnit3TestSuite(scriptClass)) {
- return runJUnit3TestSuite(scriptClass);
- }
- // if it's a JUnit 4.x test, run it with an appropriate runner
- if (isJUnit4Test(scriptClass)) {
- return runJUnit4Test(scriptClass);
- }
- for (Map.Entry<String, GroovyRunner> entry : GroovySystem.RUNNER_REGISTRY.entrySet()) {
- GroovyRunner runner = entry.getValue();
- if (runner != null && runner.canRun(scriptClass, this.loader)) {
+ GroovyRunnerRegistry runnerRegistry = GroovyRunnerRegistry.getInstance();
+ for (GroovyRunner runner : runnerRegistry) {
+ if (runner.canRun(scriptClass, this.loader)) {
return runner.run(scriptClass, this.loader);
}
}
@@ -314,11 +302,12 @@ public class GroovyShell extends GroovyObjectSupport {
"- be a JUnit test or extend GroovyTestCase,\n" +
"- implement the Runnable interface,\n" +
"- or be compatible with a registered script runner. Known runners:\n";
- if (GroovySystem.RUNNER_REGISTRY.isEmpty()) {
+ if (runnerRegistry.isEmpty()) {
message += " * <none>";
- }
- for (Map.Entry<String, GroovyRunner> entry : GroovySystem.RUNNER_REGISTRY.entrySet()) {
- message += " * " + entry.getKey() + "\n";
+ } else {
+ for (String key : runnerRegistry.keySet()) {
+ message += " * " + key + "\n";
+ }
}
throw new GroovyRuntimeException(message);
}
@@ -362,126 +351,6 @@ public class GroovyShell extends GroovyObjectSupport {
}
/**
- * Run the specified class extending TestCase as a unit test.
- * This is done through reflection, to avoid adding a dependency to the JUnit framework.
- * Otherwise, developers embedding Groovy and using GroovyShell to load/parse/compile
- * groovy scripts and classes would have to add another dependency on their classpath.
- *
- * @param scriptClass the class to be run as a unit test
- */
- private static Object runJUnit3Test(Class scriptClass) {
- try {
- Object testSuite = InvokerHelper.invokeConstructorOf("junit.framework.TestSuite", new Object[]{scriptClass});
- return InvokerHelper.invokeStaticMethod("junit.textui.TestRunner", "run", new Object[]{testSuite});
- } catch (ClassNotFoundException e) {
- throw new GroovyRuntimeException("Failed to run the unit test. JUnit is not on the Classpath.", e);
- }
- }
-
- /**
- * Run the specified class extending TestSuite as a unit test.
- * This is done through reflection, to avoid adding a dependency to the JUnit framework.
- * Otherwise, developers embedding Groovy and using GroovyShell to load/parse/compile
- * groovy scripts and classes would have to add another dependency on their classpath.
- *
- * @param scriptClass the class to be run as a unit test
- */
- private static Object runJUnit3TestSuite(Class scriptClass) {
- try {
- Object testSuite = InvokerHelper.invokeStaticMethod(scriptClass, "suite", new Object[]{});
- return InvokerHelper.invokeStaticMethod("junit.textui.TestRunner", "run", new Object[]{testSuite});
- } catch (ClassNotFoundException e) {
- throw new GroovyRuntimeException("Failed to run the unit test. JUnit is not on the Classpath.", e);
- }
- }
-
- private Object runJUnit4Test(Class scriptClass) {
- try {
- return InvokerHelper.invokeStaticMethod("org.codehaus.groovy.vmplugin.v5.JUnit4Utils",
- "realRunJUnit4Test", new Object[]{scriptClass, this.loader});
- } catch (ClassNotFoundException e) {
- throw new GroovyRuntimeException("Failed to run the JUnit 4 test.", e);
- }
- }
-
- /**
- * Utility method to check through reflection if the class appears to be a
- * JUnit 3.8.x test, i.e. checks if it extends JUnit 3.8.x's TestCase.
- *
- * @param scriptClass the class we want to check
- * @return true if the class appears to be a test
- */
- private boolean isJUnit3Test(Class scriptClass) {
- // check if the parsed class is a GroovyTestCase,
- // so that it is possible to run it as a JUnit test
- boolean isUnitTestCase = false;
- try {
- try {
- Class testCaseClass = this.loader.loadClass("junit.framework.TestCase");
- // if scriptClass extends testCaseClass
- if (testCaseClass.isAssignableFrom(scriptClass)) {
- isUnitTestCase = true;
- }
- } catch (ClassNotFoundException e) {
- // fall through
- }
- } catch (Throwable e) {
- // fall through
- }
- return isUnitTestCase;
- }
-
- /**
- * Utility method to check through reflection if the class appears to be a
- * JUnit 3.8.x test suite, i.e. checks if it extends JUnit 3.8.x's TestSuite.
- *
- * @param scriptClass the class we want to check
- * @return true if the class appears to be a test
- */
- private boolean isJUnit3TestSuite(Class scriptClass) {
- // check if the parsed class is a TestSuite,
- // so that it is possible to run it as a JUnit test
- boolean isUnitTestSuite = false;
- try {
- try {
- Class testSuiteClass = this.loader.loadClass("junit.framework.TestSuite");
- // if scriptClass extends TestSuiteClass
- if (testSuiteClass.isAssignableFrom(scriptClass)) {
- isUnitTestSuite = true;
- }
- } catch (ClassNotFoundException e) {
- // fall through
- }
- } catch (Throwable e) {
- // fall through
- }
- return isUnitTestSuite;
- }
-
- /**
- * Utility method to check via reflection if the parsed class appears to be a JUnit4
- * test, i.e. checks whether it appears to be using the relevant JUnit 4 annotations.
- *
- * @param scriptClass the class we want to check
- * @return true if the class appears to be a test
- */
- private boolean isJUnit4Test(Class scriptClass) {
- // check if there are appropriate class or method annotations
- // that suggest we have a JUnit 4 test
- boolean isTest = false;
-
- try {
- if (InvokerHelper.invokeStaticMethod("org.codehaus.groovy.vmplugin.v5.JUnit4Utils",
- "realIsJUnit4Test", new Object[]{scriptClass, this.loader}) == Boolean.TRUE) {
- isTest = true;
- }
- } catch (ClassNotFoundException e) {
- throw new GroovyRuntimeException("Failed to invoke the JUnit 4 helper class.", e);
- }
- return isTest;
- }
-
- /**
* Runs the given script text with command line arguments
*
* @param scriptText is the text content of the script
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/src/main/groovy/lang/GroovySystem.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/lang/GroovySystem.java b/src/main/groovy/lang/GroovySystem.java
index 0a41035..cb5ea98 100644
--- a/src/main/groovy/lang/GroovySystem.java
+++ b/src/main/groovy/lang/GroovySystem.java
@@ -18,12 +18,12 @@
*/
package groovy.lang;
-import org.codehaus.groovy.plugin.GroovyRunner;
+import org.apache.groovy.plugin.GroovyRunner;
+import org.apache.groovy.plugin.GroovyRunnerRegistry;
import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
import org.codehaus.groovy.util.ReferenceBundle;
import org.codehaus.groovy.util.ReleaseInfo;
-import java.util.HashMap;
import java.util.Map;
public final class GroovySystem {
@@ -49,8 +49,11 @@ public final class GroovySystem {
/**
* Reference to the Runtime Registry to be used by the Groovy run-time system to find classes capable of running scripts
+ *
+ * @deprecated use {@link GroovyRunnerRegistry}
*/
- public static final Map<String, GroovyRunner> RUNNER_REGISTRY = new HashMap<String, GroovyRunner>();
+ @Deprecated
+ public static final Map<String, GroovyRunner> RUNNER_REGISTRY = GroovyRunnerRegistry.getInstance();
private static boolean keepJavaMetaClasses=false;
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/src/main/org/apache/groovy/plugin/DefaultRunners.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/groovy/plugin/DefaultRunners.java b/src/main/org/apache/groovy/plugin/DefaultRunners.java
new file mode 100644
index 0000000..5f570eb
--- /dev/null
+++ b/src/main/org/apache/groovy/plugin/DefaultRunners.java
@@ -0,0 +1,218 @@
+/*
+ * 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.groovy.plugin;
+
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyRuntimeException;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * Provides access to built-in {@link GroovyRunner} instances
+ * for the registry. These instances should be accessed via
+ * the registry and not used directly.
+ */
+final class DefaultRunners {
+
+ /*
+ * These runners were originally included directly in GroovyShell.
+ * Since they are part of core they are added directly to the
+ * GroovyRunnerRegistry rather than via a provider configuration
+ * file in META-INF/services. If any of these runners are moved
+ * out to a submodule then they should be registered using the
+ * provider configuration file (see groovy-testng).
+ *
+ * These are internal classes and not meant to be referenced
+ * outside of the GroovyRunnerRegistry.
+ */
+
+ private static final GroovyRunner JUNIT3_TEST = new Junit3TestRunner();
+ private static final GroovyRunner JUNIT3_SUITE = new Junit3SuiteRunner();
+ private static final GroovyRunner JUNIT4_TEST = new Junit4TestRunner();
+
+ private DefaultRunners() {
+ }
+
+ static GroovyRunner junit3TestRunner() {
+ return JUNIT3_TEST;
+ }
+
+ static GroovyRunner junit3SuiteRunner() {
+ return JUNIT3_SUITE;
+ }
+
+ static GroovyRunner junit4TestRunner() {
+ return JUNIT4_TEST;
+ }
+
+ private static class Junit3TestRunner implements GroovyRunner {
+ /**
+ * Utility method to check through reflection if the class appears to be a
+ * JUnit 3.8.x test, i.e. checks if it extends JUnit 3.8.x's TestCase.
+ *
+ * @param scriptClass the class we want to check
+ * @param loader the class loader
+ * @return true if the class appears to be a test
+ */
+ @Override
+ public boolean canRun(Class<?> scriptClass, GroovyClassLoader loader) {
+ try {
+ Class<?> testCaseClass = loader.loadClass("junit.framework.TestCase");
+ return testCaseClass.isAssignableFrom(scriptClass);
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+ /**
+ * Run the specified class extending TestCase as a unit test.
+ * This is done through reflection, to avoid adding a dependency to the JUnit framework.
+ * Otherwise, developers embedding Groovy and using GroovyShell to load/parse/compile
+ * groovy scripts and classes would have to add another dependency on their classpath.
+ *
+ * @param scriptClass the class to be run as a unit test
+ * @param loader the class loader
+ */
+ @Override
+ public Object run(Class<?> scriptClass, GroovyClassLoader loader) {
+ try {
+ Object testSuite = InvokerHelper.invokeConstructorOf("junit.framework.TestSuite", new Object[]{scriptClass});
+ return InvokerHelper.invokeStaticMethod("junit.textui.TestRunner", "run", new Object[]{testSuite});
+ } catch (ClassNotFoundException e) {
+ throw new GroovyRuntimeException("Failed to run the unit test. JUnit is not on the Classpath.", e);
+ }
+ }
+ }
+
+ private static class Junit3SuiteRunner implements GroovyRunner {
+ /**
+ * Utility method to check through reflection if the class appears to be a
+ * JUnit 3.8.x test suite, i.e. checks if it extends JUnit 3.8.x's TestSuite.
+ *
+ * @param scriptClass the class we want to check
+ * @param loader the class loader
+ * @return true if the class appears to be a test
+ */
+ @Override
+ public boolean canRun(Class<?> scriptClass, GroovyClassLoader loader) {
+ try {
+ Class<?> testSuiteClass = loader.loadClass("junit.framework.TestSuite");
+ return testSuiteClass.isAssignableFrom(scriptClass);
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+ /**
+ * Run the specified class extending TestSuite as a unit test.
+ * This is done through reflection, to avoid adding a dependency to the JUnit framework.
+ * Otherwise, developers embedding Groovy and using GroovyShell to load/parse/compile
+ * groovy scripts and classes would have to add another dependency on their classpath.
+ *
+ * @param scriptClass the class to be run as a unit test
+ * @param loader the class loader
+ */
+ @Override
+ public Object run(Class<?> scriptClass, GroovyClassLoader loader) {
+ try {
+ Object testSuite = InvokerHelper.invokeStaticMethod(scriptClass, "suite", new Object[]{});
+ return InvokerHelper.invokeStaticMethod("junit.textui.TestRunner", "run", new Object[]{testSuite});
+ } catch (ClassNotFoundException e) {
+ throw new GroovyRuntimeException("Failed to run the unit test. JUnit is not on the Classpath.", e);
+ }
+ }
+ }
+
+ private static class Junit4TestRunner implements GroovyRunner {
+ /**
+ * Utility method to check via reflection if the parsed class appears to be a JUnit4
+ * test, i.e. checks whether it appears to be using the relevant JUnit 4 annotations.
+ *
+ * @param scriptClass the class we want to check
+ * @param loader the class loader
+ * @return true if the class appears to be a test
+ */
+ @Override
+ public boolean canRun(Class<?> scriptClass, GroovyClassLoader loader) {
+ return hasRunWithAnnotation(scriptClass, loader)
+ || hasTestAnnotatedMethod(scriptClass, loader);
+ }
+
+ /**
+ * Run the specified class extending TestCase as a unit test.
+ * This is done through reflection, to avoid adding a dependency to the JUnit framework.
+ * Otherwise, developers embedding Groovy and using GroovyShell to load/parse/compile
+ * groovy scripts and classes would have to add another dependency on their classpath.
+ *
+ * @param scriptClass the class to be run as a unit test
+ * @param loader the class loader
+ */
+ @Override
+ public Object run(Class<?> scriptClass, GroovyClassLoader loader) {
+ try {
+ Class<?> junitCoreClass = loader.loadClass("org.junit.runner.JUnitCore");
+ Object result = InvokerHelper.invokeStaticMethod(junitCoreClass,
+ "runClasses", new Object[]{scriptClass});
+ System.out.print("JUnit 4 Runner, Tests: " + InvokerHelper.getProperty(result, "runCount"));
+ System.out.print(", Failures: " + InvokerHelper.getProperty(result, "failureCount"));
+ System.out.println(", Time: " + InvokerHelper.getProperty(result, "runTime"));
+ List<?> failures = (List<?>) InvokerHelper.getProperty(result, "failures");
+ for (Object f : failures) {
+ System.out.println("Test Failure: " + InvokerHelper.getProperty(f, "description"));
+ System.out.println(InvokerHelper.getProperty(f, "trace"));
+ }
+ return result;
+ } catch (ClassNotFoundException e) {
+ throw new GroovyRuntimeException("Error running JUnit 4 test.", e);
+ }
+ }
+
+ private static boolean hasRunWithAnnotation(Class<?> scriptClass, ClassLoader loader) {
+ try {
+ @SuppressWarnings("unchecked")
+ Class<? extends Annotation> runWithAnnotationClass =
+ (Class<? extends Annotation>)loader.loadClass("org.junit.runner.RunWith");
+ return scriptClass.isAnnotationPresent(runWithAnnotationClass);
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+ private static boolean hasTestAnnotatedMethod(Class<?> scriptClass, ClassLoader loader) {
+ try {
+ @SuppressWarnings("unchecked")
+ Class<? extends Annotation> testAnnotationClass =
+ (Class<? extends Annotation>) loader.loadClass("org.junit.Test");
+ Method[] methods = scriptClass.getMethods();
+ for (Method method : methods) {
+ if (method.isAnnotationPresent(testAnnotationClass)) {
+ return true;
+ }
+ }
+ } catch (Throwable e) {
+ // fall through
+ }
+ return false;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/src/main/org/apache/groovy/plugin/GroovyRunner.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/groovy/plugin/GroovyRunner.java b/src/main/org/apache/groovy/plugin/GroovyRunner.java
new file mode 100644
index 0000000..283d092
--- /dev/null
+++ b/src/main/org/apache/groovy/plugin/GroovyRunner.java
@@ -0,0 +1,49 @@
+/*
+ * 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.groovy.plugin;
+
+import groovy.lang.GroovyClassLoader;
+
+/**
+ * Classes which can run scripts should implement this interface.
+ *
+ * @since 2.5.0
+ */
+public interface GroovyRunner {
+
+ /**
+ * Returns {@code true} if this runner is able to
+ * run the given class.
+ *
+ * @param scriptClass class to run
+ * @param loader used to locate classes and resources
+ * @return true if given class can be run, else false
+ */
+ boolean canRun(Class<?> scriptClass, GroovyClassLoader loader);
+
+ /**
+ * Runs the given class.
+ *
+ * @param scriptClass class to run
+ * @param loader used to locate classes and resources
+ * @return result of running the class
+ */
+ Object run(Class<?> scriptClass, GroovyClassLoader loader);
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/src/main/org/apache/groovy/plugin/GroovyRunnerRegistry.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/groovy/plugin/GroovyRunnerRegistry.java b/src/main/org/apache/groovy/plugin/GroovyRunnerRegistry.java
new file mode 100644
index 0000000..43ed8a4
--- /dev/null
+++ b/src/main/org/apache/groovy/plugin/GroovyRunnerRegistry.java
@@ -0,0 +1,436 @@
+/*
+ * 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.groovy.plugin;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Registry of services that implement the {@link GroovyRunner} interface.
+ * <p>
+ * This registry makes use of the {@link ServiceLoader} facility. The
+ * preferred method for registering new {@link GroovyRunner} providers
+ * is to place them in a provider-configuration file in the resource
+ * directory {@code META-INF/services}. The preferred method for accessing
+ * the registered runners is by making use of the {@code Iterable}
+ * interface using an enhanced for-loop.
+ * <p>
+ * For compatibility with previous versions, this registry implements the
+ * {@link Map} interface. All {@code null} keys and values will be ignored
+ * and no exception thrown, except where noted.
+ *
+ * @since 2.5.0
+ */
+public class GroovyRunnerRegistry implements Map<String, GroovyRunner>, Iterable<GroovyRunner> {
+
+ /*
+ * Implementation notes
+ *
+ * GroovySystem stores a static reference to this instance so it is
+ * important to make it fast to create as possible. GroovyRunners are
+ * only used to run scripts that GroovyShell does not already know how
+ * to run so defer service loading until requested via the iterator or
+ * map access methods.
+ *
+ * The Map interface is for compatibility with the original definition
+ * of GroovySystem.RUNNER_REGISTRY. At some point it would probably
+ * make sense to dispense with associating a String key with a runner
+ * and provide register/unregister methods instead of the Map
+ * interface.
+ */
+
+ private static final GroovyRunnerRegistry INSTANCE = new GroovyRunnerRegistry();
+
+ private static final Logger LOG = Logger.getLogger(GroovyRunnerRegistry.class.getName());
+
+ // Lazily initialized and loaded, should be accessed internally using getMap()
+ private volatile Map<String, GroovyRunner> runnerMap;
+
+ private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
+ private final Lock readLock = rwLock.readLock();
+ private final Lock writeLock = rwLock.writeLock();
+
+ /**
+ * Returns a reference to the one and only registry instance.
+ *
+ * @return registry instance
+ */
+ public static GroovyRunnerRegistry getInstance() {
+ return INSTANCE;
+ }
+
+ // package-private for use in testing to avoid calling ServiceLoader.load
+ GroovyRunnerRegistry(Map<? extends String, ? extends GroovyRunner> runners) {
+ // Preserve insertion order
+ runnerMap = new LinkedHashMap<>();
+ putAll(runners);
+ }
+
+ private GroovyRunnerRegistry() {
+ }
+
+ /**
+ * Lazily initialize and load the backing Map. A {@link LinkedHashMap}
+ * is used to preserve insertion order.
+ * <p>
+ * Do not call while holding a read lock.
+ *
+ * @return backing registry map
+ */
+ private Map<String, GroovyRunner> getMap() {
+ Map<String, GroovyRunner> map = runnerMap;
+ if (map == null) {
+ writeLock.lock();
+ try {
+ if ((map = runnerMap) == null) {
+ runnerMap = map = new LinkedHashMap<>();
+ loadDefaultRunners();
+ load(null);
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+ return map;
+ }
+
+ private void loadDefaultRunners() {
+ register(DefaultRunners.junit3TestRunner());
+ register(DefaultRunners.junit3SuiteRunner());
+ register(DefaultRunners.junit4TestRunner());
+ }
+
+ /**
+ * Loads {@link GroovyRunner} instances using the {@link ServiceLoader} facility.
+ *
+ * @param classLoader used to locate provider-configuration files and classes
+ */
+ public void load(ClassLoader classLoader) {
+ Map<String, GroovyRunner> map = runnerMap; // direct read
+ if (map == null) {
+ map = getMap(); // initialize and load (recursive call), result ignored
+ if (classLoader == null) {
+ // getMap() already loaded using a null classloader
+ return;
+ }
+ }
+ try {
+ if (classLoader == null) {
+ classLoader = Thread.currentThread().getContextClassLoader();
+ }
+ load0(classLoader);
+ } catch (SecurityException se) {
+ LOG.log(Level.WARNING, "Failed to get the context ClassLoader", se);
+ } catch (ServiceConfigurationError sce) {
+ LOG.log(Level.WARNING, "Failed to load GroovyRunner services from ClassLoader " + classLoader, sce);
+ }
+ }
+
+ private void load0(ClassLoader classLoader) {
+ ServiceLoader<GroovyRunner> serviceLoader = ServiceLoader.load(GroovyRunner.class, classLoader);
+ for (GroovyRunner runner : serviceLoader) {
+ register(runner);
+ }
+ }
+
+ /**
+ * Registers the given instance with the registry. This is
+ * equivalent to {@link #put(String, GroovyRunner)} with a
+ * {@code key} being set to {@code runner.getClass().getName()}.
+ *
+ * @param runner the instance to add to the registry
+ */
+ private void register(GroovyRunner runner) {
+ put(runner.getClass().getName(), runner);
+ }
+
+ /**
+ * Returns an iterator for all runners that are registered.
+ * The returned iterator is a snapshot of the registry at
+ * the time the iterator is created. This iterator does not
+ * support removal.
+ *
+ * @return iterator for all registered runners
+ */
+ @Override
+ public Iterator<GroovyRunner> iterator() {
+ return values().iterator();
+ }
+
+ /**
+ * Returns the number of registered runners.
+ *
+ * @return number of registered runners
+ */
+ @Override
+ public int size() {
+ Map<String, GroovyRunner> map = getMap();
+ readLock.lock();
+ try {
+ return map.size();
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Returns {@code true} if the registry contains no runners, else
+ * {@code false}.
+ *
+ * @return {@code true} if no runners are registered
+ */
+ @Override
+ public boolean isEmpty() {
+ Map<String, GroovyRunner> map = getMap();
+ readLock.lock();
+ try {
+ return map.isEmpty();
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Returns {@code true} if a runner was registered with the
+ * specified key.
+ *
+ * @param key for the registered runner
+ * @return {@code true} if a runner was registered with given key
+ */
+ @Override
+ public boolean containsKey(Object key) {
+ if (key == null) {
+ return false;
+ }
+ Map<String, GroovyRunner> map = getMap();
+ readLock.lock();
+ try {
+ return map.containsKey(key);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Returns {@code true} if registry contains the given
+ * runner instance.
+ *
+ * @param runner instance of a GroovyRunner
+ * @return {@code true} if the given runner is registered
+ */
+ @Override
+ public boolean containsValue(Object runner) {
+ if (runner == null) {
+ return false;
+ }
+ Map<String, GroovyRunner> map = getMap();
+ readLock.lock();
+ try {
+ return map.containsValue(runner);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Returns the registered runner for the specified key.
+ *
+ * @param key used to lookup the runner
+ * @return the runner registered with the given key
+ */
+ @Override
+ public GroovyRunner get(Object key) {
+ if (key == null) {
+ return null;
+ }
+ Map<String, GroovyRunner> map = getMap();
+ readLock.lock();
+ try {
+ return map.get(key);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Registers a runner with the specified key.
+ *
+ * @param key to associate with the runner
+ * @param runner the runner to register
+ * @return the previously registered runner for the given key,
+ * if no runner was previously registered for the key
+ * then {@code null}
+ */
+ @Override
+ public GroovyRunner put(String key, GroovyRunner runner) {
+ if (key == null || runner == null) {
+ return null;
+ }
+ Map<String, GroovyRunner> map = getMap();
+ writeLock.lock();
+ try {
+ return map.put(key, runner);
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ /**
+ * Removes a registered runner from the registry.
+ *
+ * @param key of the runner to remove
+ * @return the runner instance that was removed, if no runner
+ * instance was removed then {@code null}
+ */
+ @Override
+ public GroovyRunner remove(Object key) {
+ if (key == null) {
+ return null;
+ }
+ Map<String, GroovyRunner> map = getMap();
+ writeLock.lock();
+ try {
+ return map.remove(key);
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ /**
+ * Adds all entries from the given Map to the registry.
+ * Any entries in the provided Map that contain a {@code null}
+ * key or value will be ignored.
+ *
+ * @param m entries to add to the registry
+ * @throws NullPointerException if the given Map is {@code null}
+ */
+ @Override
+ public void putAll(Map<? extends String, ? extends GroovyRunner> m) {
+ writeLock.lock();
+ try {
+ for (Map.Entry<? extends String, ? extends GroovyRunner> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ /**
+ * Clears all registered runners from the registry.
+ */
+ @Override
+ public void clear() {
+ Map<String, GroovyRunner> map = getMap();
+ writeLock.lock();
+ try {
+ map.clear();
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ /**
+ * Set of all keys associated with registered runners.
+ * This is a snapshot of the registry and any subsequent
+ * registry changes will not be reflected in the set.
+ *
+ * @return an unmodifiable set of keys for registered runners
+ */
+ @Override
+ public Set<String> keySet() {
+ Map<String, GroovyRunner> map = getMap();
+ readLock.lock();
+ try {
+ if (map.isEmpty()) {
+ return Collections.emptySet();
+ }
+ return Collections.unmodifiableSet(new LinkedHashSet<>(map.keySet()));
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Returns a collection of all registered runners.
+ * This is a snapshot of the registry and any subsequent
+ * registry changes will not be reflected in the collection.
+ *
+ * @return an unmodifiable collection of registered runner instances
+ */
+ @Override
+ public Collection<GroovyRunner> values() {
+ Map<String, GroovyRunner> map = getMap();
+ readLock.lock();
+ try {
+ if (map.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableCollection(new ArrayList<>(map.values()));
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Returns a set of entries for registered runners.
+ * This is a snapshot of the registry and any subsequent
+ * registry changes will not be reflected in the set.
+ *
+ * @return an unmodifiable set of registered runner entries
+ */
+ @Override
+ public Set<Entry<String, GroovyRunner>> entrySet() {
+ Map<String, GroovyRunner> map = getMap();
+ readLock.lock();
+ try {
+ if (map.isEmpty()) {
+ return Collections.emptySet();
+ }
+ return Collections.unmodifiableSet(new LinkedHashSet<>(map.entrySet()));
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ @Override
+ public String toString() {
+ Map<String, GroovyRunner> map = getMap();
+ readLock.lock();
+ try {
+ return map.toString();
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/src/main/org/codehaus/groovy/plugin/GroovyRunner.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/plugin/GroovyRunner.java b/src/main/org/codehaus/groovy/plugin/GroovyRunner.java
index 5530716..40bd257 100644
--- a/src/main/org/codehaus/groovy/plugin/GroovyRunner.java
+++ b/src/main/org/codehaus/groovy/plugin/GroovyRunner.java
@@ -18,13 +18,11 @@
*/
package org.codehaus.groovy.plugin;
-import groovy.lang.GroovyClassLoader;
-
/**
* Classes which can run scripts should implement this interface.
+ *
+ * @deprecated use {@link org.apache.groovy.plugin.GroovyRunner}
*/
-public interface GroovyRunner {
- boolean canRun(Class scriptClass, GroovyClassLoader loader);
-
- Object run(Class scriptClass, GroovyClassLoader loader);
+@Deprecated
+public interface GroovyRunner extends org.apache.groovy.plugin.GroovyRunner {
}
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/src/main/org/codehaus/groovy/vmplugin/v5/JUnit4Utils.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/vmplugin/v5/JUnit4Utils.java b/src/main/org/codehaus/groovy/vmplugin/v5/JUnit4Utils.java
index 96318de..7d6afe8 100644
--- a/src/main/org/codehaus/groovy/vmplugin/v5/JUnit4Utils.java
+++ b/src/main/org/codehaus/groovy/vmplugin/v5/JUnit4Utils.java
@@ -17,7 +17,6 @@
* under the License.
*/
package org.codehaus.groovy.vmplugin.v5;
-// TODO M12N move this to groovy-test
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyRuntimeException;
@@ -29,7 +28,10 @@ import java.lang.reflect.Method;
/**
* Java 5 code for working with JUnit 4 tests.
+ *
+ * @deprecated use {@link org.apache.groovy.plugin.GroovyRunnerRegistry}
*/
+@Deprecated
public class JUnit4Utils {
/**
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/src/test/org/apache/groovy/plugin/GroovyRunnerRegistryTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/org/apache/groovy/plugin/GroovyRunnerRegistryTest.groovy b/src/test/org/apache/groovy/plugin/GroovyRunnerRegistryTest.groovy
new file mode 100644
index 0000000..36b8cd7
--- /dev/null
+++ b/src/test/org/apache/groovy/plugin/GroovyRunnerRegistryTest.groovy
@@ -0,0 +1,192 @@
+/*
+ * 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.groovy.plugin
+
+import static java.util.Collections.emptyMap
+
+class GroovyRunnerRegistryTest extends GroovyTestCase {
+
+ Map<String, GroovyRunner> knownRunners = [
+ Junit3TestRunner: DefaultRunners.junit3TestRunner(),
+ Junit3SuiteRunner: DefaultRunners.junit3SuiteRunner(),
+ Junit4TestRunner: DefaultRunners.junit4TestRunner()
+ ]
+
+ GroovyRunnerRegistry registry = new GroovyRunnerRegistry(knownRunners)
+
+ void testServiceLoaderFindsKnownRunners() {
+ GroovyRunnerRegistry reg = GroovyRunnerRegistry.getInstance()
+ assert reg.@runnerMap == null
+ reg.load(null)
+ assert reg.@runnerMap.size() == 3
+ for (runner in reg) {
+ knownRunners.remove(runner.getClass().getSimpleName())
+ }
+ assert knownRunners.isEmpty()
+ }
+
+ void testCustomRunner() {
+ DummyRunner customRunner = new DummyRunner()
+ GroovyRunnerRegistry realRegistry = GroovyRunnerRegistry.getInstance()
+ realRegistry.put('DummyRunner', customRunner)
+ try {
+ def result = new GroovyShell().run('class DummyClass {}', 'DummyClass.groovy', [])
+ assert result == 'DummyClass was run'
+ } finally {
+ realRegistry.remove('DummyRunner')
+ }
+ }
+
+ void testLegacyCustomRunner() {
+ LegacyDummyRunner customRunner = new LegacyDummyRunner()
+ GroovyRunnerRegistry realRegistry = GroovyRunnerRegistry.getInstance()
+ realRegistry.put('LegacyDummyRunner', customRunner)
+ try {
+ def result = new GroovyShell().run('class LegacyDummyClass {}', 'LegacyDummyClass.groovy', [])
+ assert result == 'LegacyDummyClass was run'
+ } finally {
+ realRegistry.remove('LegacyDummyRunner')
+ }
+ }
+
+ void testSize() {
+ assert registry.size() == knownRunners.size()
+ }
+
+ void testIsEmpty() {
+ assert !registry.isEmpty()
+ assert new GroovyRunnerRegistry(emptyMap()).isEmpty()
+ }
+
+ void testContainsKey() {
+ assert registry.containsKey('Junit4TestRunner')
+ assert !registry.containsKey(null)
+ }
+
+ void testContainsValue() {
+ assert registry.containsValue(knownRunners.get('Junit4TestRunner'))
+ assert !registry.containsValue(null)
+ }
+
+ void testGet() {
+ assert registry.get('Junit4TestRunner') == knownRunners.get('Junit4TestRunner')
+ assert !registry.get(null)
+ }
+
+ void testPut() {
+ DummyRunner runner = new DummyRunner()
+ registry.put('DummyRunner', runner)
+
+ assert registry.get('DummyRunner').is(runner)
+ assert registry.put('DummyRunner', new DummyRunner()).is(runner)
+
+ assert !registry.put(null, runner)
+ assert !registry.put('DummyRunner', null)
+ assert !registry.put(null, null)
+ }
+
+ void testRemove() {
+ DummyRunner runner = new DummyRunner()
+ registry.put('DummyRunner', runner)
+
+ assert registry.remove('DummyRunner').is(runner)
+ assert !registry.remove('NotExistsRunner')
+ assert !registry.remove(null)
+ }
+
+ void testClear() {
+ assert registry.size() == knownRunners.size()
+ registry.clear()
+ assert registry.size() == 0
+ }
+
+ void testPutAll() {
+ shouldFail(NullPointerException) {
+ registry.putAll(null as Map<String, GroovyRunner>)
+ }
+
+ Map<String, GroovyRunner> map = ['Dummy': new DummyRunner(), 'Dummy2': null]
+ map[null] = new DummyRunner()
+
+ assert registry.size() == knownRunners.size()
+ registry.putAll(map)
+ assert registry.size() == knownRunners.size() + 1
+ }
+
+ void testAsIterable() {
+ Iterator itr = registry.iterator()
+ assert itr.hasNext()
+ assert itr.next() == knownRunners['Junit3TestRunner']
+ shouldFail(UnsupportedOperationException) {
+ itr.remove()
+ }
+ }
+
+ void testValues() {
+ Collection<GroovyRunner> values = registry.values()
+ assert values.size() == 3
+ assert values.contains(knownRunners['Junit3TestRunner'])
+ shouldFail(UnsupportedOperationException) {
+ values.remove(knownRunners['Junit3TestRunner'])
+ }
+ }
+
+ void testKeySet() {
+ Set<String> keySet = registry.keySet()
+ assert keySet.size() == 3
+ assert keySet.contains('Junit3TestRunner')
+ shouldFail(UnsupportedOperationException) {
+ keySet.remove('Junit3TestRunner')
+ }
+ }
+
+ void testEntrySet() {
+ Set<Map.Entry<String, GroovyRunner>> entries = registry.entrySet()
+ assert entries.size() == 3
+ assert entries.find { it.key == 'Junit3TestRunner' }
+ shouldFail(UnsupportedOperationException) {
+ entries.remove(entries.find { it.key == 'Junit3TestRunner' })
+ }
+ }
+
+ static class DummyRunner implements GroovyRunner {
+ @Override
+ boolean canRun(Class<?> scriptClass, GroovyClassLoader loader) {
+ return scriptClass.getSimpleName() == 'DummyClass'
+ }
+
+ @Override
+ Object run(Class<?> scriptClass, GroovyClassLoader loader) {
+ return 'DummyClass was run'
+ }
+ }
+
+ static class LegacyDummyRunner implements org.codehaus.groovy.plugin.GroovyRunner {
+ @Override
+ boolean canRun(Class<?> scriptClass, GroovyClassLoader loader) {
+ return scriptClass.getSimpleName() == 'LegacyDummyClass'
+ }
+
+ @Override
+ Object run(Class<?> scriptClass, GroovyClassLoader loader) {
+ return 'LegacyDummyClass was run'
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/subprojects/groovy-testng/build.gradle
----------------------------------------------------------------------
diff --git a/subprojects/groovy-testng/build.gradle b/subprojects/groovy-testng/build.gradle
index c359af0..20ebb90 100644
--- a/subprojects/groovy-testng/build.gradle
+++ b/subprojects/groovy-testng/build.gradle
@@ -22,4 +22,5 @@ dependencies {
// exclude 'optional' beanshell even though testng's pom doesn't say optional
exclude(group: 'org.beanshell', module: 'bsh')
}
+ testCompile project(':groovy-test')
}
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/subprojects/groovy-testng/src/main/java/org/apache/groovy/plugin/testng/TestNgRunner.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-testng/src/main/java/org/apache/groovy/plugin/testng/TestNgRunner.java b/subprojects/groovy-testng/src/main/java/org/apache/groovy/plugin/testng/TestNgRunner.java
new file mode 100644
index 0000000..e9f0b94
--- /dev/null
+++ b/subprojects/groovy-testng/src/main/java/org/apache/groovy/plugin/testng/TestNgRunner.java
@@ -0,0 +1,86 @@
+/*
+ * 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.groovy.plugin.testng;
+
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyRuntimeException;
+import org.apache.groovy.plugin.GroovyRunner;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+/**
+ * Integration code for running TestNG tests in Groovy.
+ */
+public class TestNgRunner implements GroovyRunner {
+
+ /**
+ * Utility method to check via reflection if the parsed class appears to be a TestNG
+ * test, i.e. checks whether it appears to be using the relevant TestNG annotations.
+ *
+ * @param scriptClass the class we want to check
+ * @param loader the GroovyClassLoader to use to find classes
+ * @return true if the class appears to be a test
+ */
+ @Override
+ public boolean canRun(Class<?> scriptClass, GroovyClassLoader loader) {
+ try {
+ @SuppressWarnings("unchecked")
+ Class<? extends Annotation> testAnnotationClass =
+ (Class<? extends Annotation>) loader.loadClass("org.testng.annotations.Test");
+ if (scriptClass.isAnnotationPresent(testAnnotationClass)) {
+ return true;
+ } else {
+ Method[] methods = scriptClass.getMethods();
+ for (Method method : methods) {
+ if (method.isAnnotationPresent(testAnnotationClass)) {
+ return true;
+ }
+ }
+ }
+ } catch (Throwable e) {
+ // fall through
+ }
+ return false;
+ }
+
+ /**
+ * Utility method to run a TestNG test.
+ *
+ * @param scriptClass the class we want to run as a test
+ * @param loader the class loader to use
+ * @return the result of running the test
+ */
+ @Override
+ public Object run(Class<?> scriptClass, GroovyClassLoader loader) {
+ try {
+ Class<?> testNGClass = loader.loadClass("org.testng.TestNG");
+ Object testng = InvokerHelper.invokeConstructorOf(testNGClass, new Object[]{});
+ InvokerHelper.invokeMethod(testng, "setTestClasses", new Object[]{scriptClass});
+ Class<?> listenerClass = loader.loadClass("org.testng.TestListenerAdapter");
+ Object listener = InvokerHelper.invokeConstructorOf(listenerClass, new Object[]{});
+ InvokerHelper.invokeMethod(testng, "addListener", new Object[]{listener});
+ return InvokerHelper.invokeMethod(testng, "run", new Object[]{});
+ } catch (ClassNotFoundException e) {
+ throw new GroovyRuntimeException("Error running TestNG test.", e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/subprojects/groovy-testng/src/main/java/org/codehaus/groovy/testng/TestNgRunner.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-testng/src/main/java/org/codehaus/groovy/testng/TestNgRunner.java b/subprojects/groovy-testng/src/main/java/org/codehaus/groovy/testng/TestNgRunner.java
index da49839..ecb7be6 100644
--- a/subprojects/groovy-testng/src/main/java/org/codehaus/groovy/testng/TestNgRunner.java
+++ b/subprojects/groovy-testng/src/main/java/org/codehaus/groovy/testng/TestNgRunner.java
@@ -18,77 +18,15 @@
*/
package org.codehaus.groovy.testng;
-import groovy.lang.GroovyClassLoader;
-import groovy.lang.GroovyRuntimeException;
import org.codehaus.groovy.plugin.GroovyRunner;
-import org.codehaus.groovy.runtime.InvokerHelper;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
/**
* Integration code for running TestNG tests in Groovy.
+ *
+ * @deprecated use {@link org.apache.groovy.plugin.testng.TestNgRunner}
*/
-public class TestNgRunner implements GroovyRunner {
-
- /**
- * Utility method to check via reflection if the parsed class appears to be a TestNG
- * test, i.e. checks whether it appears to be using the relevant TestNG annotations.
- *
- * @param scriptClass the class we want to check
- * @param loader the GroovyClassLoader to use to find classes
- * @return true if the class appears to be a test
- */
- @SuppressWarnings("unchecked")
- public boolean canRun(Class scriptClass, GroovyClassLoader loader) {
- // check if there are appropriate class or method annotations
- // that suggest we have a TestNG test
- boolean isTest = false;
- try {
- try {
- Class testAnnotationClass = loader.loadClass("org.testng.annotations.Test");
- Annotation annotation = scriptClass.getAnnotation(testAnnotationClass);
- if (annotation != null) {
- isTest = true;
- } else {
- Method[] methods = scriptClass.getMethods();
- for (Method method : methods) {
- annotation = method.getAnnotation(testAnnotationClass);
- if (annotation != null) {
- isTest = true;
- break;
- }
- }
- }
- } catch (ClassNotFoundException e) {
- // fall through
- }
- } catch (Throwable e) {
- // fall through
- }
- return isTest;
- }
-
- /**
- * Utility method to run a TestNG test.
- *
- * @param scriptClass the class we want to run as a test
- * @param loader the class loader to use
- * @return the result of running the test
- */
- public Object run(Class scriptClass, GroovyClassLoader loader) {
- // invoke through reflection to eliminate mandatory TestNG jar dependency
- try {
- Class testNGClass = loader.loadClass("org.testng.TestNG");
- Object testng = InvokerHelper.invokeConstructorOf(testNGClass, new Object[]{});
- InvokerHelper.invokeMethod(testng, "setTestClasses", new Object[]{scriptClass});
- Class listenerClass = loader.loadClass("org.testng.TestListenerAdapter");
- Object listener = InvokerHelper.invokeConstructorOf(listenerClass, new Object[]{});
- InvokerHelper.invokeMethod(testng, "addListener", new Object[]{listener});
- return InvokerHelper.invokeMethod(testng, "run", new Object[]{});
- } catch (ClassNotFoundException e) {
- throw new GroovyRuntimeException("Error running TestNG test.", e);
- }
- }
-
+@Deprecated
+public class TestNgRunner
+ extends org.apache.groovy.plugin.testng.TestNgRunner
+ implements GroovyRunner {
}
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/subprojects/groovy-testng/src/main/resources/META-INF/services/org.apache.groovy.plugin.GroovyRunner
----------------------------------------------------------------------
diff --git a/subprojects/groovy-testng/src/main/resources/META-INF/services/org.apache.groovy.plugin.GroovyRunner b/subprojects/groovy-testng/src/main/resources/META-INF/services/org.apache.groovy.plugin.GroovyRunner
new file mode 100644
index 0000000..146b7e9
--- /dev/null
+++ b/subprojects/groovy-testng/src/main/resources/META-INF/services/org.apache.groovy.plugin.GroovyRunner
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+org.apache.groovy.plugin.testng.TestNgRunner
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/subprojects/groovy-testng/src/main/resources/META-INF/services/org.codehaus.groovy.plugins.Runners
----------------------------------------------------------------------
diff --git a/subprojects/groovy-testng/src/main/resources/META-INF/services/org.codehaus.groovy.plugins.Runners b/subprojects/groovy-testng/src/main/resources/META-INF/services/org.codehaus.groovy.plugins.Runners
deleted file mode 100644
index 9505ad8..0000000
--- a/subprojects/groovy-testng/src/main/resources/META-INF/services/org.codehaus.groovy.plugins.Runners
+++ /dev/null
@@ -1,16 +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.
-
-org.codehaus.groovy.testng.TestNgRunner
http://git-wip-us.apache.org/repos/asf/groovy/blob/a2fb91d6/subprojects/groovy-testng/src/test/groovy/org/apache/groovy/plugin/testng/TestNgRunnerTest.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-testng/src/test/groovy/org/apache/groovy/plugin/testng/TestNgRunnerTest.groovy b/subprojects/groovy-testng/src/test/groovy/org/apache/groovy/plugin/testng/TestNgRunnerTest.groovy
new file mode 100644
index 0000000..0b23015
--- /dev/null
+++ b/subprojects/groovy-testng/src/test/groovy/org/apache/groovy/plugin/testng/TestNgRunnerTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * 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.groovy.plugin.testng
+
+class TestNgRunnerTest extends GroovyShellTestCase {
+
+ void testRunWithTestNg() {
+ String test = '''
+ class F {
+ @org.testng.annotations.Test
+ void m() {
+ org.testng.Assert.assertEquals(1,1)
+ }
+ }
+ '''
+ shell.run(test, 'F.groovy', [])
+ }
+
+ void testTestNgRunnerListedInRunnerList() {
+ assert shouldFail(GroovyRuntimeException) {
+ shell.run('class F {}', 'F.groovy', [])
+ }.contains('* ' + TestNgRunner.class.getName())
+ }
+
+}