Index: src/main/groovy/lang/GroovyShell.java =================================================================== --- src/main/groovy/lang/GroovyShell.java (revision 10016) +++ src/main/groovy/lang/GroovyShell.java Wed Dec 26 06:09:02 EST 2007 @@ -247,14 +247,18 @@ if (Runnable.class.isAssignableFrom(scriptClass)) { return runRunnable(scriptClass, args); } - // if it's a unit test extending GroovyTestCase, run it with JUnit's TextRunner - if (isUnitTestCase(scriptClass)) { - return runTest(scriptClass); + // if it's a unit test extending JUnit 3.8.x's TestCase, run it with an appropriate TextRunner + if (isJUnit3TestCase(scriptClass)) { + return runJUnit3Test(scriptClass); } + // if it's a JUnit 4.x's TestCase, run it with an appropriate TextRunner + if (isJUnit4TestCase(scriptClass)) { + return runJUnit4Test(scriptClass); + } throw new GroovyRuntimeException("This script or class could not be run.\n" + "It should either: \n" + "- have a main method, \n" + - "- be a class extending JUnit's TestCase, \n" + + "- be a JUnit Test or GroovyTestCase, \n" + "- or implement the Runnable interface."); } } @@ -302,7 +306,7 @@ * * @param scriptClass the class to be run as a unit test */ - private Object runTest(Class scriptClass) { + private 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}); @@ -311,13 +315,22 @@ } } + private Object runJUnit4Test(Class scriptClass) { + try { + return InvokerHelper.invokeStaticMethod("org.codehaus.groovy.vmplugin.v5.JUnit4Utils", + "realRunJUnit4TestCase", new Object[]{scriptClass}); + } catch (ClassNotFoundException e) { + throw new GroovyRuntimeException("Failed to run the unit test."); + } + } + /** - * Utility method to check through reflection if the parsed class extends TestCase. + * Utility method to check through reflection if the class extends JUnit 3.8.x's TestCase. * - * @param scriptClass the class we want to know if it extends TestCase - * @return true if the class extends junit.framework.TestCase + * @param scriptClass the class we want to check + * @return true if the class appears to be a test */ - private boolean isUnitTestCase(Class scriptClass) { + private boolean isJUnit3TestCase(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; @@ -338,6 +351,34 @@ } /** + * Utility method to check via reflection if the parsed class appears to be a JUnit4 TestCase. + * + * @param scriptClass the class we want to check + * @return true if the class appears to be a test + */ + private boolean isJUnit4TestCase(Class scriptClass) { + // if we are running under Java 1.4 don't bother trying to check + char version = System.getProperty("java.version").charAt(2); + if (version < '5') { + return false; + } + + // check if there are appropriate class or method annotations + // that suggest we have a JUnit 4 test + boolean isUnitTestCase = false; + + try { + if (InvokerHelper.invokeStaticMethod("org.codehaus.groovy.vmplugin.v5.JUnit4Utils", + "realIsJUnit4TestCase", new Object[]{scriptClass, this.loader}) == Boolean.TRUE) { + isUnitTestCase = true; + }; + } catch (ClassNotFoundException e) { + throw new GroovyRuntimeException("Failed to run the unit test."); + } + return isUnitTestCase; + } + + /** * Runs the given script text with command line arguments * * @param scriptText is the text content of the script Index: src/main/org/codehaus/groovy/vmplugin/v5/JUnit4Utils.java =================================================================== --- src/main/org/codehaus/groovy/vmplugin/v5/JUnit4Utils.java Wed Dec 26 06:01:22 EST 2007 +++ src/main/org/codehaus/groovy/vmplugin/v5/JUnit4Utils.java Wed Dec 26 06:01:22 EST 2007 @@ -0,0 +1,98 @@ +/* + * Copyright 2003-2007 the original author or authors. + * + * Licensed 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.codehaus.groovy.vmplugin.v5; + +import groovy.lang.GroovyClassLoader; +import groovy.lang.GroovyRuntimeException; +import groovy.lang.GroovySystem; +import org.codehaus.groovy.runtime.InvokerHelper; + +import java.util.List; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + * java 5 code for working with JUnit 4 Tests + * @author Paul King + * + */ +public class JUnit4Utils { + + /** + * Utility method to check via reflection if the parsed class appears to be a JUnit4 TestCase. + * + * @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 + */ + static Boolean realIsJUnit4TestCase(Class scriptClass, GroovyClassLoader loader) { + // check if there are appropriate class or method annotations + // that suggest we have a JUnit 4 test + boolean isUnitTestCase = false; + try { + try { + Class runWithAnnotationClass = loader.loadClass("org.junit.runner.RunWith"); + Annotation annotation = scriptClass.getAnnotation(runWithAnnotationClass); + if (annotation != null) { + isUnitTestCase = true; + } else { + Class testAnnotationClass = loader.loadClass("org.junit.Test"); + Method[] methods = scriptClass.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + annotation = method.getAnnotation(testAnnotationClass); + if (annotation != null) { + isUnitTestCase = true; + break; + } + } + } + } catch (ClassNotFoundException e) { + // fall through + } + } catch (Throwable e) { + // fall through + } + return isUnitTestCase ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * Utility method to run a JUnit4 TestCase. + * + * @param scriptClass the class we want to run as a test + * @return the result of running the test + */ + static Object realRunJUnit4TestCase(Class scriptClass) { + // invoke through reflection to eliminate mandatory JUnit 4 Jar dependency + + try { + Object result = InvokerHelper.invokeStaticMethod("org.junit.runner.JUnitCore", + "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 (int i = 0; i < failures.size(); i++) { + Object f = failures.get(i); + 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."); + } + } +}