blob: 5f570eb5adc89b1a3d3520dc4ef25566c0435e80 [file] [log] [blame]
/*
* 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;
}
}
}