/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html * * 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 com.google.dart.engine; import junit.framework.AssertionFailedError; import junit.framework.Protectable; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestResult; import junit.framework.TestSuite; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; /** * Instances of the class {@code ExtendedTestSuite} implement a test suite that will run both normal * and failing tests. Normal tests are {@code public}, zero-argument, {@code void} methods whose * name starts with {@code "test"}. Failing tests are {@code public}, zero-argument, {@code void} * methods whose name starts with {@code "fail"}. Failing tests pass if they throw an exception and * fail if they do not. */ public class ExtendedTestSuite extends TestSuite { /** * Instances of the class {@code InvertingTestResult} wrap an existing test result to invert the * expectation for a test that is about to be run. */ private static class InvertingTestResult extends TestResult { /** * The test result being wrapped by this test result. */ private TestResult wrappedResult; /** * Initialize a newly created test result to report any failure to the given test result. * * @param wrappedResult the test result being wrapped by this test result */ public InvertingTestResult(TestResult wrappedResult) { this.wrappedResult = wrappedResult; } @Override public void endTest(Test test) { wrappedResult.endTest(test); } @Override public void runProtected(Test test, Protectable protectable) { try { protectable.protect(); } catch (ThreadDeath exception) { // don't catch ThreadDeath by accident throw exception; // Would like to use the more specific JUnit [AssertionFailedError] here, but // some tests (e.g. [SearchEngineImplTest]) use the [org.fest.assertions] // package, which throws plain Java [AssertionError]. } catch (AssertionError exception) { return; } catch (Throwable exception) { // Unexpected exceptions are still failures. wrappedResult.addFailure(test, new AssertionFailedError( "Expected assertion failure, but got other failure: " + exception)); return; } wrappedResult.addFailure(test, new AssertionFailedError( "Expected assertion failure, but passed")); } @Override public void startTest(Test test) { wrappedResult.startTest(test); } } /** * Initialize a newly created test suite to include all of the normal and failing tests defined by * the given class. * * @param testClass the class defining the test methods */ public ExtendedTestSuite(Class<? extends TestCase> testClass) { super(testClass); addFailingTestsFromTestCase(testClass); } /** * Initialize a newly created test suite to be empty but have the given name. * * @param name the name of the test suite */ public ExtendedTestSuite(String name) { super(name); } @Override public void addTestSuite(Class<? extends TestCase> testClass) { addTest(new ExtendedTestSuite(testClass)); } @Override public void runTest(Test test, TestResult result) { if (test instanceof TestCase && ((TestCase) test).getName().startsWith("fail")) { test.run(new InvertingTestResult(result)); } else { test.run(result); } } private void addFailingTestMethod(Method method, List<String> names, Class<?> testClass) { String name = method.getName(); if (names.contains(name)) { return; } if (!isPublicFailingTestMethod(method)) { if (isFailingTestMethod(method)) { addTest(warning("Test method isn't public: " + method.getName() + "(" + testClass.getCanonicalName() + ")")); } return; } names.add(name); addTest(createTest(testClass, name)); } private void addFailingTestsFromTestCase(final Class<?> testClass) { // // These conditions were tested and reported by the superclass. They are re-tested here to avoid // reporting them multiple times. // try { getTestConstructor(testClass); } catch (NoSuchMethodException exception) { return; } if (!Modifier.isPublic(testClass.getModifiers())) { return; } // // Add the actual tests. // Class<?> superclass = testClass; List<String> names = new ArrayList<String>(); while (Test.class.isAssignableFrom(superclass)) { for (Method each : superclass.getDeclaredMethods()) { addFailingTestMethod(each, names, testClass); } superclass = superclass.getSuperclass(); } } private boolean isFailingTestMethod(Method method) { return method.getParameterTypes().length == 0 && method.getName().startsWith("fail") && method.getReturnType().equals(Void.TYPE); } private boolean isPublicFailingTestMethod(Method method) { return isFailingTestMethod(method) && Modifier.isPublic(method.getModifiers()); } }