| /* |
| * 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 groovy.util; |
| |
| import groovy.lang.Closure; |
| import groovy.lang.GroovyRuntimeException; |
| import groovy.lang.GroovyShell; |
| |
| import java.util.logging.Logger; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| |
| import junit.framework.TestCase; |
| |
| import org.codehaus.groovy.runtime.InvokerHelper; |
| |
| /** |
| * A default JUnit TestCase in Groovy. This provides a number of helper methods |
| * plus avoids the JUnit restriction of requiring all test* methods to be void |
| * return type. |
| * |
| * @author <a href="mailto:bob@werken.com">bob mcwhirter</a> |
| * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> |
| * @author Dierk Koenig (the notYetImplemented feature, changes to shouldFail) |
| * @version $Revision$ |
| */ |
| public class GroovyTestCase extends TestCase { |
| |
| protected static Logger log = Logger.getLogger(GroovyTestCase.class.getName()); |
| private static int counter; |
| private boolean useAgileDoxNaming = false; |
| |
| public GroovyTestCase() { |
| } |
| |
| /** |
| * Overload the getName() method to make the test cases look more like AgileDox |
| * (thanks to Joe Walnes for this tip!) |
| */ |
| public String getName() { |
| if (useAgileDoxNaming) { |
| return super.getName().substring(4).replaceAll("([A-Z])", " $1").toLowerCase(); |
| } |
| else { |
| return super.getName(); |
| } |
| } |
| |
| public String getMethodName() { |
| return super.getName(); |
| } |
| |
| /** |
| * Asserts that the arrays are equivalent and contain the same values |
| * |
| * @param expected |
| * @param value |
| */ |
| protected void assertArrayEquals(Object[] expected, Object[] value) { |
| String message = |
| "expected array: " + InvokerHelper.toString(expected) + " value array: " + InvokerHelper.toString(value); |
| assertNotNull(message + ": expected should not be null", expected); |
| assertNotNull(message + ": value should not be null", value); |
| assertEquals(message, expected.length, value.length); |
| for (int i = 0, size = expected.length; i < size; i++) { |
| assertEquals("value[" + i + "] when " + message, expected[i], value[i]); |
| } |
| } |
| |
| /** |
| * Asserts that the array of characters has a given length |
| * |
| * @param length expected length |
| * @param array the array |
| */ |
| protected void assertLength(int length, char[] array) { |
| assertEquals(length, array.length); |
| } |
| |
| /** |
| * Asserts that the array of ints has a given length |
| * |
| * @param length expected length |
| * @param array the array |
| */ |
| protected void assertLength(int length, int[] array) { |
| assertEquals(length, array.length); |
| } |
| |
| /** |
| * Asserts that the array of objects has a given length |
| * |
| * @param length expected length |
| * @param array the array |
| */ |
| protected void assertLength(int length, Object[] array) { |
| assertEquals(length, array.length); |
| } |
| |
| /** |
| * Asserts that the array of characters contains a given char |
| * |
| * @param expected expected character to be found |
| * @param array the array |
| */ |
| protected void assertContains(char expected, char[] array) { |
| for (int i = 0; i < array.length; ++i) { |
| if (array[i] == expected) { |
| return; |
| } |
| } |
| |
| StringBuffer message = new StringBuffer(); |
| |
| message.append(expected).append(" not in {"); |
| |
| for (int i = 0; i < array.length; ++i) { |
| message.append("'").append(array[i]).append("'"); |
| |
| if (i < (array.length - 1)) { |
| message.append(", "); |
| } |
| } |
| |
| message.append(" }"); |
| |
| fail(message.toString()); |
| } |
| |
| /** |
| * Asserts that the array of ints contains a given int |
| * |
| * @param expected expected int |
| * @param array the array |
| */ |
| protected void assertContains(int expected, int[] array) { |
| for (int i = 0; i < array.length; ++i) { |
| if (array[i] == expected) { |
| return; |
| } |
| } |
| |
| StringBuffer message = new StringBuffer(); |
| |
| message.append(expected).append(" not in {"); |
| |
| for (int i = 0; i < array.length; ++i) { |
| message.append("'").append(array[i]).append("'"); |
| |
| if (i < (array.length - 1)) { |
| message.append(", "); |
| } |
| } |
| |
| message.append(" }"); |
| |
| fail(message.toString()); |
| } |
| |
| /** |
| * Asserts that the value of toString() on the given object matches the |
| * given text string |
| * |
| * @param value the object to be output to the console |
| * @param expected the expected String representation |
| */ |
| protected void assertToString(Object value, String expected) { |
| Object console = InvokerHelper.invokeMethod(value, "toString", null); |
| assertEquals("toString() on value: " + value, expected, console); |
| } |
| |
| /** |
| * Asserts that the value of inspect() on the given object matches the |
| * given text string |
| * |
| * @param value the object to be output to the console |
| * @param expected the expected String representation |
| */ |
| protected void assertInspect(Object value, String expected) { |
| Object console = InvokerHelper.invokeMethod(value, "inspect", null); |
| assertEquals("inspect() on value: " + value, expected, console); |
| } |
| |
| /** |
| * Asserts that the script runs without any exceptions |
| * |
| * @param script the script that should pass without any exception thrown |
| */ |
| protected void assertScript(final String script) throws Exception { |
| GroovyShell shell = new GroovyShell(); |
| shell.evaluate(script, getTestClassName()); |
| } |
| |
| protected String getTestClassName() { |
| return "TestScript" + getMethodName() + (counter++) + ".groovy"; |
| } |
| |
| /** |
| * Asserts that the given code closure fails when it is evaluated |
| * |
| * @param code |
| * @return the message of the thrown Throwable |
| */ |
| protected String shouldFail(Closure code) { |
| boolean failed = false; |
| String result = null; |
| try { |
| code.call(); |
| } |
| catch (Throwable e) { |
| failed = true; |
| result = e.getMessage(); |
| } |
| assertTrue("Closure " + code + " should have failed", failed); |
| return result; |
| } |
| |
| /** |
| * Asserts that the given code closure fails when it is evaluated |
| * and that a particular exception is thrown. |
| * |
| * @param clazz the class of the expected exception |
| * @param code the closure that should fail |
| * @return the message of the expected Throwable |
| */ |
| protected String shouldFail(Class clazz, Closure code) { |
| Throwable th = null; |
| try { |
| code.call(); |
| } catch (GroovyRuntimeException gre) { |
| th = gre; |
| while (th.getCause()!=null && th.getCause()!=gre){ // if wrapped, find the root cause |
| th=th.getCause(); |
| if (th!=gre && (th instanceof GroovyRuntimeException)) { |
| gre = (GroovyRuntimeException) th; |
| } |
| } |
| } catch (Throwable e) { |
| th = e; |
| } |
| |
| if (th==null) { |
| fail("Closure " + code + " should have failed with an exception of type " + clazz.getName()); |
| } else if (! clazz.isInstance(th)) { |
| fail("Closure " + code + " should have failed with an exception of type " + clazz.getName() + ", instead got Exception " + th); |
| } |
| return th.getMessage(); |
| } |
| |
| /** |
| * Returns a copy of a string in which all EOLs are \n. |
| */ |
| protected String fixEOLs( String value ) |
| { |
| return value.replaceAll( "(\\r\\n?)|\n", "\n" ); |
| } |
| |
| /** |
| * Runs the calling JUnit test again and fails only if it unexpectedly runs.<br/> |
| * This is helpful for tests that don't currently work but should work one day, |
| * when the tested functionality has been implemented.<br/> |
| * The right way to use it is: |
| * <pre> |
| * public void testXXX() { |
| * if (GroovyTestCase.notYetImplemented(this)) return; |
| * ... the real (now failing) unit test |
| * } |
| * </pre> |
| * Idea copied from HtmlUnit (many thanks to Marc Guillemot). |
| * Future versions maybe available in the JUnit distro. |
| * The purpose of providing a 'static' version is such that you can use the |
| * feature even if not subclassing GroovyTestCase. |
| * @return <false> when not itself already in the call stack |
| */ |
| public static boolean notYetImplemented(TestCase caller) { |
| if (notYetImplementedFlag.get() != null) { |
| return false; |
| } |
| notYetImplementedFlag.set(Boolean.TRUE); |
| |
| final Method testMethod = findRunningJUnitTestMethod(caller.getClass()); |
| try { |
| log.info("Running " + testMethod.getName() + " as not yet implemented"); |
| testMethod.invoke(caller, new Class[] {}); |
| fail(testMethod.getName() + " is marked as not yet implemented but passes unexpectedly"); |
| } |
| catch (final Exception e) { |
| log.info(testMethod.getName() + " fails which is expected as it is not yet implemented"); |
| // method execution failed, it is really "not yet implemented" |
| } |
| finally { |
| notYetImplementedFlag.set(null); |
| } |
| return true; |
| } |
| |
| /** |
| * Convenience method for subclasses of GroovyTestCase, identical to |
| * <pre> GroovyTestCase.notYetImplemented(this); </pre>. |
| * @see #notYetImplemented(junit.framework.TestCase) |
| * @return <false> when not itself already in the call stack |
| */ |
| public boolean notYetImplemented() { |
| return notYetImplemented(this); |
| } |
| |
| /** |
| * From JUnit. Finds from the call stack the active running JUnit test case |
| * @return the test case method |
| * @throws RuntimeException if no method could be found. |
| */ |
| private static Method findRunningJUnitTestMethod(Class caller) { |
| final Class[] args = new Class[] {}; |
| |
| // search the inial junit test |
| final Throwable t = new Exception(); |
| for (int i=t.getStackTrace().length-1; i>=0; --i) { |
| final StackTraceElement element = t.getStackTrace()[i]; |
| if (element.getClassName().equals(caller.getName())) { |
| try { |
| final Method m = caller.getMethod(element.getMethodName(), args); |
| if (isPublicTestMethod(m)) { |
| return m; |
| } |
| } |
| catch (final Exception e) { |
| // can't access, ignore it |
| } |
| } |
| } |
| throw new RuntimeException("No JUnit test case method found in call stack"); |
| } |
| |
| |
| /** |
| * From Junit. Test if the method is a junit test. |
| * @param method the method |
| * @return <code>true</code> if this is a junit test. |
| */ |
| private static boolean isPublicTestMethod(final Method method) { |
| final String name = method.getName(); |
| final Class[] parameters = method.getParameterTypes(); |
| final Class returnType = method.getReturnType(); |
| |
| return parameters.length == 0 && name.startsWith("test") |
| && returnType.equals(Void.TYPE) |
| && Modifier.isPublic(method.getModifiers()); |
| } |
| |
| private static final ThreadLocal notYetImplementedFlag = new ThreadLocal(); |
| } |