/* * Copyright 2006 Edward Kuns * * 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. * * $Id: TestWithCustomTest.java 0000 2006-10-19 22:00:00Z ekuns $ */ package org.castor.xmlctf; import java.util.Iterator; import java.util.List; import junit.framework.TestCase; import org.exolab.castor.tests.framework.testDescriptor.ConfigurationType; import org.exolab.castor.tests.framework.testDescriptor.CustomTest; import org.exolab.castor.tests.framework.testDescriptor.FailureType; import org.exolab.castor.tests.framework.testDescriptor.types.FailureStepType; import org.exolab.castor.xml.XMLContext; /** * Implements a test case that tests code written by the XML source generator. * This class uses a user-provided test class to test the generated source. * <p> * Each user-provided test is allowed to return a Boolean, either a primitive or * a java.lang.Boolean -- it does not matter. If the user-provided test returns * a Boolean and it is false, then the test is considered to have failed. If the * user-provided test throws <i>or returns</i> a Throwable, it is considered to * have failed. If the user-provided test returns <b>anything else</b> * (including void) then the test is considered to have passed. * <p> * Note: Returning Throwable is a little bit cleaner than throwing an Exception, * but either is acceptable as a sign of test failure. This is because when a * Throwable is returned, if -printStack is in effect, then the CORRECT stack * trace can be displayed and not a stack dump from the refective invocation. * <p> * There is no requirement that the user-provided test implement any interface, * nor any requirement that the user-provided test return anything at all. * However, a test that returns "void" and that never throws an Exception is not * a very useful test as it can never fail. * * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a> * @version $Revision: 0000 $ $Date: $ */ class TestWithCustomTest extends TestCase { /** We add this fixed string to the end of our testcase name. */ private static final String CUSTOM = "_CustomTest"; /** We delegate all actions to this test case. */ private final XMLTestCase _delegate; /** The failure object that is not null is the test intends to fail. */ protected final FailureType _failure; /** True if the test is supposed to return failure or throw an Exception. */ protected final boolean _failureExpected; /** * Blank constructor for this test case. This contructor is not useful, * since no delegate test case is provided. * * @param name * Name of our delegate test case */ TestWithCustomTest(final String name) { super(name + CUSTOM); throw new IllegalArgumentException("You cannot use the name-only constructor"); } /** * Constructs a test case that when invoked will delegate to the provided * test case. * @param name Name of our delegate test case * @param tc */ TestWithCustomTest(final String name, final XMLTestCase tc) { super(name + CUSTOM); _delegate = tc; _failure = tc._failure; _failureExpected = _failure != null && _failure.getContent(); } /** * Provides setup for our delegated test case, depending on the type of * test case we are delegating for. * @throws Exception if anything goes wrong during setup */ protected void setUp() throws Exception { _delegate.setXMLContext(new XMLContext()); if (_delegate instanceof MarshallingFrameworkTestCase) { ((MarshallingFrameworkTestCase)_delegate).setUp(); } else if (_delegate instanceof SourceGeneratorTestCase) { ((SourceGeneratorTestCase)_delegate).setUp(); } } /** * Provides tear down for our delegated test case, depending on the type of * test case we are delegating for. * @throws Exception if anything goes wrong during teardown */ protected void tearDown() throws Exception { if (_delegate instanceof MarshallingFrameworkTestCase) { ((MarshallingFrameworkTestCase)_delegate).tearDown(); } else if (_delegate instanceof SourceGeneratorTestCase) { ((SourceGeneratorTestCase)_delegate).tearDown(); } } /** * Runs our test case using our delegate object where necessary. * @throws Exception when anything goes wrong (this is temporary) */ public void runTest() { verbose("\n------------------------------"); verbose("Run the custom test case"); verbose("------------------------------\n"); if (_delegate._skip) { verbose("-->Skipping the test"); return; } List returnValues = null; try { CustomTest customTest = _delegate._unitTest.getCustomTest(); ConfigurationType testConfig = customTest.getMethods(); Object object = getTestObject(customTest.getTestClass()); returnValues = _delegate.invokeEnumeratedMethods(object, testConfig); } catch (Exception e) { if (!_delegate.checkExceptionWasExpected(e, FailureStepType.CUSTOM_TEST)) { fail("Exception running the custom test " + e); } return; } // Loop over all our return values ... any FALSE or Throwable means a failure int count = 0; boolean testFailed = false; for (Iterator i = returnValues.iterator(); i.hasNext(); count++) { Object o = i.next(); // If this test returned false and we DID NOT expect to fail, error!!! if (o instanceof Boolean && !((Boolean)o).booleanValue() && !_failureExpected) { // Mark failure, complain, but keep checking so we give ALL complaints testFailed = true; System.out.println("Custom test #" + count + " was expected to succeed, but returned false"); } // If this test *returned* (not threw) an Exception, consider that a failure if (o instanceof Throwable && !_failureExpected) { testFailed = true; System.out.println("Custom test #" + count + " was expected to succeed, but returned Throwable"); if (XMLTestCase._printStack) { ((Throwable)o).printStackTrace(); } } } // Did we fail to meet our expected result, either success or failure? if (testFailed ^ _failureExpected) { if (testFailed) { fail("The custom test failed"); } else { fail("The custom test was expected to fail, but succeeded"); } } } /** * Gets an instance of our test object, as configured. * @param testClassName name of the test class * @return an instance of our test object * @throws ClassNotFoundException when the test object's class cannot be found * @throws IllegalAccessException when the test object's constructor is private or protected * @throws InstantiationException when the test object is abstract or an interface */ protected Object getTestObject(final String testClassName) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class testObject = null; if (_delegate._test.getClassLoader() != null) { testObject = _delegate._test.getClassLoader().loadClass(testClassName); } else { testObject = this.getClass().getClassLoader().loadClass(testClassName); } return testObject.newInstance(); } /** * print the message if in verbose mode. * @param message the message to print */ private void verbose(final String message) { _delegate.verbose(message); } }