/*
* 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);
}
}