Index: src/main/org/codehaus/groovy/vmplugin/v5/TestNgUtils.java =================================================================== --- src/main/org/codehaus/groovy/vmplugin/v5/TestNgUtils.java Wed Dec 26 18:50:37 EST 2007 +++ src/main/org/codehaus/groovy/vmplugin/v5/TestNgUtils.java Wed Dec 26 18:50:37 EST 2007 @@ -0,0 +1,87 @@ +/* + * 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 TestNG tests. + * + * @author Paul King + * + */ +public class TestNgUtils { + + /** + * Utility method to check via reflection if the parsed class appears to be a TestNG test. + * + * @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 realIsTestNgTest(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"); + Method[] methods = scriptClass.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + Annotation annotation = method.getAnnotation(testAnnotationClass); + if (annotation != null) { + isTest = true; + break; + } + } + } catch (ClassNotFoundException e) { + // fall through + } + } catch (Throwable e) { + // fall through + } + return isTest ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * Utility method to run a TestNG test. + * + * @param scriptClass the class we want to run as a test + * @return the result of running the test + */ + static Object realRunTestNgTest(Class scriptClass) { + // invoke through reflection to eliminate mandatory TestNG jar dependency + + try { + Object testng = InvokerHelper.invokeConstructorOf("org.testng.TestNG", new Object[]{}); + InvokerHelper.invokeMethod(testng, "setTestClasses", new Object[]{scriptClass}); + Object listener = InvokerHelper.invokeConstructorOf("org.testng.TestListenerAdapter", new Object[]{}); + InvokerHelper.invokeMethod(testng, "addListener", new Object[]{listener}); + Object result = InvokerHelper.invokeMethod(testng, "run", new Object[]{}); + return result; + } catch (ClassNotFoundException e) { + throw new GroovyRuntimeException("Error running TestNG test."); + } + } +} Index: src/main/groovy/lang/GroovyShell.java =================================================================== --- src/main/groovy/lang/GroovyShell.java (revision 10016) +++ src/main/groovy/lang/GroovyShell.java Wed Dec 26 18:46:23 EST 2007 @@ -247,14 +247,22 @@ 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 JUnit 3.8.x test, run it with an appropriate runner + if (isJUnit3Test(scriptClass)) { + return runJUnit3Test(scriptClass); } + // if it's a JUnit 4.x test, run it with an appropriate runner + if (isJUnit4Test(scriptClass)) { + return runJUnit4Test(scriptClass); + } + // if it's a TestNG tst, run it with an appropriate runner + if (isTestNgTest(scriptClass)) { + return runTestNgTest(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, TestNG test or extend GroovyTestCase, \n" + "- or implement the Runnable interface."); } } @@ -267,7 +275,7 @@ // first, fetch the constructor taking String[] as parameter constructor = scriptClass.getConstructor(new Class[]{(new String[]{}).getClass()}); try { - // instanciate a runnable and run it + // instantiate a runnable and run it runnable = (Runnable) constructor.newInstance(new Object[]{args}); } catch (Throwable t) { reason = t; @@ -277,7 +285,7 @@ // otherwise, find the default constructor constructor = scriptClass.getConstructor(new Class[]{}); try { - // instanciate a runnable and run it + // instantiate a runnable and run it runnable = (Runnable) constructor.newInstance(new Object[]{}); } catch (Throwable t) { reason = t; @@ -302,7 +310,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 +319,32 @@ } } + private Object runJUnit4Test(Class scriptClass) { + try { + return InvokerHelper.invokeStaticMethod("org.codehaus.groovy.vmplugin.v5.JUnit4Utils", + "realRunJUnit4Test", new Object[]{scriptClass}); + } catch (ClassNotFoundException e) { + throw new GroovyRuntimeException("Failed to run the JUnit 4 test."); + } + } + + private Object runTestNgTest(Class scriptClass) { + try { + return InvokerHelper.invokeStaticMethod("org.codehaus.groovy.vmplugin.v5.TestNgUtils", + "realRunTestNgTest", new Object[]{scriptClass}); + } catch (ClassNotFoundException e) { + throw new GroovyRuntimeException("Failed to run the TestNG test."); + } + } + /** - * Utility method to check through reflection if the parsed class extends TestCase. + * 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 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 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; @@ -338,6 +365,63 @@ } /** + * 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) { + // 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 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."); + } + return isTest; + } + + /** + * 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 + * @return true if the class appears to be a test + */ + private boolean isTestNgTest(Class scriptClass) { + 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 TestNG test + boolean isTest = false; + + try { + if (InvokerHelper.invokeStaticMethod("org.codehaus.groovy.vmplugin.v5.TestNgUtils", + "realIsTestNgTest", new Object[]{scriptClass, this.loader}) == Boolean.TRUE) { + isTest = true; + }; + } catch (ClassNotFoundException e) { + throw new GroovyRuntimeException("Failed to invoke the TestNG helper class."); + } + return isTest; + } + + /** * 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 18:18:13 EST 2007 +++ src/main/org/codehaus/groovy/vmplugin/v5/JUnit4Utils.java Wed Dec 26 18:18:13 EST 2007 @@ -0,0 +1,99 @@ +/* + * 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 test. + * + * @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 realIsJUnit4Test(Class scriptClass, GroovyClassLoader loader) { + // check if there are appropriate class or method annotations + // that suggest we have a JUnit 4 test + boolean isTest = false; + try { + try { + Class runWithAnnotationClass = loader.loadClass("org.junit.runner.RunWith"); + Annotation annotation = scriptClass.getAnnotation(runWithAnnotationClass); + if (annotation != null) { + isTest = 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) { + isTest = true; + break; + } + } + } + } catch (ClassNotFoundException e) { + // fall through + } + } catch (Throwable e) { + // fall through + } + return isTest ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * Utility method to run a JUnit4 test. + * + * @param scriptClass the class we want to run as a test + * @return the result of running the test + */ + static Object realRunJUnit4Test(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."); + } + } +}