package com.windowtester.test.util.junit; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Enumeration; import java.util.Vector; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestResult; import junit.framework.TestSuite; /******************************************************************************* * Copyright (c) 2012 Google, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Google, Inc. - initial API and implementation *******************************************************************************/ @SuppressWarnings("unchecked") public class PluggableTestSuite extends TestSuite { /** * ...as the moon sets over the early morning Merlin, Oregon * mountains, our intrepid adventurers type... */ static public Test createTest(Class theClass, String name) { Constructor constructor; try { constructor= getTestConstructor(theClass); } catch (NoSuchMethodException e) { return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"); } Object test; try { if (constructor.getParameterTypes().length == 0) { test= constructor.newInstance(new Object[0]); if (test instanceof TestCase) ((TestCase) test).setName(name); } else { test= constructor.newInstance(new Object[]{name}); } } catch (InstantiationException e) { return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")")); } catch (InvocationTargetException e) { return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")")); } catch (IllegalAccessException e) { return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")")); } return (Test) test; } /** * Gets a constructor which takes a single String as * its argument or a no arg constructor. */ public static Constructor getTestConstructor(Class theClass) throws NoSuchMethodException { Class[] args= { String.class }; try { return theClass.getConstructor(args); } catch (NoSuchMethodException e) { // fall through } return theClass.getConstructor(new Class[0]); } /** * Returns a test which will fail and log a warning message. */ public static Test warning(final String message) { return new TestCase("warning") { protected void runTest() { fail(message); } }; } /** * Returns a test which will pass and log a message. */ public static Test pass(String message) { return new TestCase(message) { protected void runTest() { } }; } /** * Converts the stack trace into a string */ private static String exceptionToString(Throwable t) { StringWriter stringWriter= new StringWriter(); PrintWriter writer= new PrintWriter(stringWriter); t.printStackTrace(writer); return stringWriter.toString(); } private String fName; private Vector fTests= new Vector(10); /** * Constructs an empty TestSuite. */ public PluggableTestSuite() { } /** * Constructs a TestSuite from the given class. Adds all the methods * starting with "test" as test cases to the suite. * Parts of this method was written at 2337 meters in the Hueffihuette, * Kanton Uri */ public PluggableTestSuite(final Class theClass) { fName= theClass.getName(); try { getTestConstructor(theClass); // Avoid generating multiple error messages } catch (NoSuchMethodException e) { addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()")); return; } if (!Modifier.isPublic(theClass.getModifiers())) { addTest(warning("Class "+theClass.getName()+" is not public")); return; } Class superClass= theClass; Vector names= new Vector(); while (Test.class.isAssignableFrom(superClass)) { Method[] methods= superClass.getDeclaredMethods(); for (int i= 0; i < methods.length; i++) { addTestMethod(methods[i], names, theClass); } superClass= superClass.getSuperclass(); } if (fTests.size() == 0) { addTest(warning("No tests found in "+theClass.getName())); } } /** * Constructs a TestSuite from the given class with the given name. * @see TestSuite#TestSuite(Class) */ public PluggableTestSuite(Class theClass, String name) { this(theClass); setName(name); } /** * Constructs an empty TestSuite. */ public PluggableTestSuite(String name) { setName(name); } /** * Constructs a TestSuite from the given array of classes. * @param classes */ public PluggableTestSuite (Class[] classes) { for (int i= 0; i < classes.length; i++) addTest(newSuite(classes[i])); } /** * Constructs a TestSuite from the given array of classes with the given name. * @see TestSuite#TestSuite(Class[]) */ public PluggableTestSuite(Class[] classes, String name) { this(classes); setName(name); } /** * Adds a test to the suite. */ public void addTest(Test test) { fTests.addElement(test); } /** * Adds the tests from the given class to the suite */ public void addTestSuite(Class testClass) { addTest(newSuite(testClass)); } protected PluggableTestSuite newSuite(Class testClass) { return new PluggableTestSuite(testClass); } /** * Counts the number of test cases that will be run by this test. */ public int countTestCases() { int count= 0; for (Enumeration e= tests(); e.hasMoreElements(); ) { Test test= (Test)e.nextElement(); count= count + test.countTestCases(); } return count; } /** * Returns the name of the suite. Not all * test suites have a name and this method * can return null. */ public String getName() { return fName; } /** * Runs the tests and collects their result in a TestResult. */ public void run(TestResult result) { for (Enumeration e= tests(); e.hasMoreElements(); ) { if (result.shouldStop() ) break; Test test= (Test)e.nextElement(); runTest(test, result); } } public void runTest(Test test, TestResult result) { test.run(result); } /** * Sets the name of the suite. * @param name The name to set */ public void setName(String name) { fName= name; } /** * Returns the test at the given index */ public Test testAt(int index) { return (Test)fTests.elementAt(index); } /** * Returns the number of tests in this suite */ public int testCount() { return fTests.size(); } /** * Returns the tests as an enumeration */ public Enumeration tests() { return fTests.elements(); } /** */ public String toString() { if (getName() != null) return getName(); return super.toString(); } private void addTestMethod(Method m, Vector names, Class theClass) { String name= m.getName(); if (names.contains(name)) return; if (! isPublicTestMethod(m)) { if (isTestMethod(m)) addTest(warning("Test method isn't public: "+m.getName())); if (!isEnabled(m)) addTest(pass("Test disabled: " + m.getName())); return; } names.addElement(name); addTest(createTest(theClass, name)); } private boolean isPublicTestMethod(Method m) { return isTestMethod(m) && Modifier.isPublic(m.getModifiers()); } public boolean isTestMethod(Method m) { return hasTestSignature(m) && isEnabled(m); } protected boolean isEnabled(Method m) { return true; } protected boolean hasTestSignature(Method m) { String name= m.getName(); Class[] parameters= m.getParameterTypes(); Class returnType= m.getReturnType(); return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE); } }