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