View Javadoc
1   /*
2    * Copyright 2006 Edward Kuns
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   *
14   * $Id: TestWithCustomTest.java 0000 2006-10-19 22:00:00Z ekuns $
15   */
16  package org.castor.xmlctf;
17  
18  import java.util.Iterator;
19  import java.util.List;
20  
21  import junit.framework.TestCase;
22  
23  import org.exolab.castor.tests.framework.testDescriptor.ConfigurationType;
24  import org.exolab.castor.tests.framework.testDescriptor.CustomTest;
25  import org.exolab.castor.tests.framework.testDescriptor.FailureType;
26  import org.exolab.castor.tests.framework.testDescriptor.types.FailureStepType;
27  import org.exolab.castor.xml.XMLContext;
28  
29  /**
30   * Implements a test case that tests code written by the XML source generator. This class uses a
31   * user-provided test class to test the generated source.
32   * <p>
33   * Each user-provided test is allowed to return a Boolean, either a primitive or a java.lang.Boolean
34   * -- it does not matter. If the user-provided test returns a Boolean and it is false, then the test
35   * is considered to have failed. If the user-provided test throws <i>or returns</i> a Throwable, it
36   * is considered to have failed. If the user-provided test returns <b>anything else</b> (including
37   * void) then the test is considered to have passed.
38   * <p>
39   * Note: Returning Throwable is a little bit cleaner than throwing an Exception, but either is
40   * acceptable as a sign of test failure. This is because when a Throwable is returned, if
41   * -printStack is in effect, then the CORRECT stack trace can be displayed and not a stack dump from
42   * the refective invocation.
43   * <p>
44   * There is no requirement that the user-provided test implement any interface, nor any requirement
45   * that the user-provided test return anything at all. However, a test that returns "void" and that
46   * never throws an Exception is not a very useful test as it can never fail.
47   *
48   * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a>
49   * @version $Revision: 0000 $ $Date: $
50   */
51  class TestWithCustomTest extends TestCase {
52    /** We add this fixed string to the end of our testcase name. */
53    private static final String CUSTOM = "_CustomTest";
54  
55    /** We delegate all actions to this test case. */
56    private final XMLTestCase _delegate;
57  
58    /** The failure object that is not null is the test intends to fail. */
59    protected final FailureType _failure;
60    /** True if the test is supposed to return failure or throw an Exception. */
61    protected final boolean _failureExpected;
62  
63    /**
64     * Blank constructor for this test case. This contructor is not useful, since no delegate test
65     * case is provided.
66     *
67     * @param name Name of our delegate test case
68     */
69    TestWithCustomTest(final String name) {
70      super(name + CUSTOM);
71      throw new IllegalArgumentException("You cannot use the name-only constructor");
72    }
73  
74    /**
75     * Constructs a test case that when invoked will delegate to the provided test case.
76     * 
77     * @param name Name of our delegate test case
78     * @param tc
79     */
80    TestWithCustomTest(final String name, final XMLTestCase tc) {
81      super(name + CUSTOM);
82      _delegate = tc;
83      _failure = tc._failure;
84      _failureExpected = _failure != null && _failure.getContent();
85    }
86  
87    /**
88     * Provides setup for our delegated test case, depending on the type of test case we are
89     * delegating for.
90     * 
91     * @throws Exception if anything goes wrong during setup
92     */
93    protected void setUp() throws Exception {
94      _delegate.setXMLContext(new XMLContext());
95  
96      if (_delegate instanceof MarshallingFrameworkTestCase) {
97        ((MarshallingFrameworkTestCase) _delegate).setUp();
98      } else if (_delegate instanceof SourceGeneratorTestCase) {
99        ((SourceGeneratorTestCase) _delegate).setUp();
100     }
101   }
102 
103   /**
104    * Provides tear down for our delegated test case, depending on the type of test case we are
105    * delegating for.
106    * 
107    * @throws Exception if anything goes wrong during teardown
108    */
109   protected void tearDown() throws Exception {
110     if (_delegate instanceof MarshallingFrameworkTestCase) {
111       ((MarshallingFrameworkTestCase) _delegate).tearDown();
112     } else if (_delegate instanceof SourceGeneratorTestCase) {
113       ((SourceGeneratorTestCase) _delegate).tearDown();
114     }
115   }
116 
117   /**
118    * Runs our test case using our delegate object where necessary.
119    * 
120    * @throws Exception when anything goes wrong (this is temporary)
121    */
122   public void runTest() {
123     verbose("\n------------------------------");
124     verbose("Run the custom test case");
125     verbose("------------------------------\n");
126     if (_delegate._skip) {
127       verbose("-->Skipping the test");
128       return;
129     }
130 
131     List returnValues = null;
132 
133     try {
134       CustomTest customTest = _delegate._unitTest.getCustomTest();
135       ConfigurationType testConfig = customTest.getMethods();
136       Object object = getTestObject(customTest.getTestClass());
137       returnValues = _delegate.invokeEnumeratedMethods(object, testConfig);
138     } catch (Exception e) {
139       if (!_delegate.checkExceptionWasExpected(e, FailureStepType.CUSTOM_TEST)) {
140         fail("Exception running the custom test " + e);
141       }
142       return;
143     }
144 
145     // Loop over all our return values ... any FALSE or Throwable means a failure
146     int count = 0;
147     boolean testFailed = false;
148 
149     for (Iterator i = returnValues.iterator(); i.hasNext(); count++) {
150       Object o = i.next();
151 
152       // If this test returned false and we DID NOT expect to fail, error!!!
153       if (o instanceof Boolean && !((Boolean) o).booleanValue() && !_failureExpected) {
154         // Mark failure, complain, but keep checking so we give ALL complaints
155         testFailed = true;
156         System.out
157             .println("Custom test #" + count + " was expected to succeed, but returned false");
158       }
159 
160       // If this test *returned* (not threw) an Exception, consider that a failure
161       if (o instanceof Throwable && !_failureExpected) {
162         testFailed = true;
163         System.out
164             .println("Custom test #" + count + " was expected to succeed, but returned Throwable");
165         if (XMLTestCase._printStack) {
166           ((Throwable) o).printStackTrace();
167         }
168       }
169     }
170 
171     // Did we fail to meet our expected result, either success or failure?
172     if (testFailed ^ _failureExpected) {
173       if (testFailed) {
174         fail("The custom test failed");
175       } else {
176         fail("The custom test was expected to fail, but succeeded");
177       }
178     }
179   }
180 
181   /**
182    * Gets an instance of our test object, as configured.
183    * 
184    * @param testClassName name of the test class
185    * @return an instance of our test object
186    * @throws ClassNotFoundException when the test object's class cannot be found
187    * @throws IllegalAccessException when the test object's constructor is private or protected
188    * @throws InstantiationException when the test object is abstract or an interface
189    */
190   protected Object getTestObject(final String testClassName)
191       throws ClassNotFoundException, IllegalAccessException, InstantiationException {
192     Class testObject = null;
193     if (_delegate._test.getClassLoader() != null) {
194       testObject = _delegate._test.getClassLoader().loadClass(testClassName);
195     } else {
196       testObject = this.getClass().getClassLoader().loadClass(testClassName);
197     }
198     return testObject.newInstance();
199   }
200 
201   /**
202    * print the message if in verbose mode.
203    * 
204    * @param message the message to print
205    */
206   private void verbose(final String message) {
207     _delegate.verbose(message);
208   }
209 
210 }