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: TestWithReferenceDocument.java 0000 2006-10-19 22:00:00Z ekuns $
17   */
18  package org.castor.xmlctf;
19  
20  import java.io.File;
21  import java.io.FileWriter;
22  import java.io.IOException;
23  import java.io.InputStream;
24  
25  import junit.framework.TestCase;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.castor.xmlctf.util.CTFUtils;
30  import org.exolab.castor.tests.framework.testDescriptor.FailureType;
31  import org.exolab.castor.tests.framework.testDescriptor.types.FailureStepType;
32  import org.exolab.castor.xml.XMLContext;
33  
34  /**
35   * Implements a test case that tests code written by the XML source generator.
36   * This class uses the generated source to read and write an XML document,
37   * comparing the XML document written against the reference document that was
38   * originally read in.
39   * <p>
40   * The test follows this sequence:
41   *
42   * <ol>
43   *   <li>Unmarshals the given input file (if any).</li>
44   *   <li>Compare the result object with the provided object model (if any).</li>
45   *   <li>Marshals the object to a file.</li>
46   *   <li>Unmarshals the created file.</li>
47   *   <li>Check that the result object is equal to the start object.</li>
48   * </ol>
49   *
50   * @author <a href="mailto:gignoux@kernelcenter.org">Sebastien Gignoux</a>
51   * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
52   * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a>
53   * @version $Revision: 0000 $ $Date: $
54   */
55  class TestWithReferenceDocument extends TestCase {
56      
57      private static final Log LOG = LogFactory.getLog(TestWithReferenceDocument.class);
58      
59      /** We add this fixed string to the end of our testcase name. */
60      private static final String REFERENCE = "_ReferenceDocument";
61  
62      /** We delegate all actions to this test case. */
63      private final XMLTestCase      _delegate;
64      /** Used only to retrieved the classloader. */
65      protected final CastorTestCase _test;
66      /** The failure object that is not null is the test intends to fail. */
67      protected final FailureType    _failure;
68      /** Class name of the ObjectModelBuilder. */
69      protected final String         _builderClassName;
70      /** Header of the name of all our output files ... marshaled and dumped. */
71      protected final String         _outputName;
72      /** Input file for test XML. (May be null if the builder class is provided.) */
73      protected final String         _inputName;
74      /** Gold file filename, really only useful if a class builder and no input file is given. */
75      protected final String         _goldFileName;
76  
77      /**
78       * Blank constructor for this test case.  This contructor is not useful, since
79       * no delegate test case is provided.
80       * @param name Name of our delegate test case
81       */
82      TestWithReferenceDocument(String name) {
83          super(name+REFERENCE);
84          throw new IllegalArgumentException("You cannot use the name-only constructor");
85      }
86  
87      /**
88       * Constructs a test case that when invoked will delegate to the provided
89       * test case.
90       * @param name Name of our delegate test case
91       * @param tc
92       */
93      TestWithReferenceDocument(String name, XMLTestCase tc) {
94          super(name+REFERENCE);
95          _delegate         = tc;
96          _test             = tc._test;
97          _failure          = tc._failure;
98          _outputName       = tc._name.replace(' ', '_') + "-testWithReferenceDocument.xml";
99          _builderClassName = tc._unitTest.getObjectBuilder();
100         _inputName        = tc._unitTest.getInput();
101 
102         // Gold File should be set to input file if no gold file is specified
103         // but some tests fail (genuine failures) so for now this is disabled.
104 
105         if (tc._unitTest.getGoldFile() != null) {
106             _goldFileName = tc._unitTest.getGoldFile();
107         } else {
108             _goldFileName = _inputName;
109         }
110     }
111 
112     /**
113      * Provides setup for our delegated test case, depending on the type of
114      * test case we are delegating for.
115      * @throws Exception if anything goes wrong during setup
116      */
117     protected void setUp() throws Exception {
118         if (_delegate == null) {
119             throw new IllegalStateException("No test specified to set up.");
120         }
121 
122         _delegate.setXMLContext(new XMLContext());
123         _delegate.setUp();
124 //        if (_delegate instanceof MarshallingFrameworkTestCase) {
125 //            ((MarshallingFrameworkTestCase)_delegate).setUp();
126 //        } else if (_delegate instanceof SourceGeneratorTestCase) {
127 //            ((SourceGeneratorTestCase)_delegate).setUp();
128 //        }
129     }
130 
131     /**
132      * Provides tear down for our delegated test case, depending on the type of
133      * test case we are delegating for.
134      * @throws Exception if anything goes wrong during teardown
135      */
136     protected void tearDown() throws Exception {
137         if (_delegate == null) {
138             throw new IllegalStateException("No test specified to tear down.");
139         }
140         _delegate.tearDown();
141 //        if (_delegate instanceof MarshallingFrameworkTestCase) {
142 //            ((MarshallingFrameworkTestCase)_delegate).tearDown();
143 //        } else if (_delegate instanceof SourceGeneratorTestCase) {
144 //            ((SourceGeneratorTestCase)_delegate).tearDown();
145 //        }
146     }
147 
148     /**
149      * Runs our test case using our delegate object where necessary.
150      * <p>
151      * FIXME: Fix this so it throws only specific and necessary exceptions
152      * @throws Exception if anything goes wrong
153      */
154     public void runTest() throws Exception { // FIXME - temporarily throws Exception
155         if (_delegate == null) {
156             throw new IllegalStateException("No test specified to be run.");
157         }
158 
159         verbose("\n------------------------------");
160         verbose("Test with reference documents");
161         verbose("------------------------------\n");
162         if (_delegate._skip) {
163             verbose("-->Skipping the test");
164             return;
165         }
166 
167         // 1. Get reference document from unmarshaler + input file (if input file is provided)
168         Object refUnmarshal;
169         try {
170             refUnmarshal = getUnmarshaledReference();
171         } catch (Exception e) {
172             if (_delegate._verbose) {
173                 LOG.error("Problem unmarshalling input file from disk", e);
174             }
175             if (!_delegate.checkExceptionWasExpected(e, FailureStepType.UNMARSHAL_REFERENCE)) {
176                 fail("Exception Unmarshaling from disk " + e);
177             }
178             return;
179         }
180 
181         // 2. Get reference document from builder (if builder is provided) -- should never fail
182         Object refGenerated = getBuilderReference();
183 
184         // 3. If we have two reference objects, make sure they are the same -- should never fail
185         if (refUnmarshal != null && refGenerated != null) {
186             compareReferenceObjects(refUnmarshal, refGenerated);
187         }
188 
189         // 4. Pick our reference object (at least one should be non-null)
190         final Object ref = (refUnmarshal != null) ? refUnmarshal : refGenerated;
191         if (ref == null) {
192             throw new Exception("There is no valid input file or hardcoded object in '" + _delegate._name + "'");
193         }
194 
195         // 5. Marshal our reference object to disk
196         File marshal_output;
197         try {
198             marshal_output = _delegate.testMarshal(ref, _outputName);
199         } catch (Exception e) {
200             if (!_delegate.checkExceptionWasExpected(e, FailureStepType.MARSHAL_TO_DISK)) {
201                 fail("Exception Unmarshaling from disk " + e);
202             }
203             return;
204         }
205 
206         if (_failure != null && _failure.getContent() && _failure.getFailureStep() != null &&
207             _failure.getFailureStep().equals(FailureStepType.MARSHAL_TO_DISK)) {
208             fail("Marshaling the reference document to disk was expected to fail, but succeeded");
209             return;
210         }
211 
212         // 6. Compare marshaled document with gold file (if one was provided)
213         if (_goldFileName != null && _goldFileName.length() > 0) {
214             int result = CTFUtils.compare(_delegate._outputRootFile + "/" +  _goldFileName, marshal_output.getAbsolutePath());
215             verbose("----> Compare marshaled document to gold file '" + _goldFileName + "': " + ((result == 0)?"OK":"### Failed ### "));
216 
217             final boolean expectedToFail = _failure != null && _failure.getContent()
218                     && _failure.getFailureStep() != null
219                     && _failure.getFailureStep().equals(FailureStepType.COMPARE_TO_REFERENCE);
220 
221             verbose("----> expectedToFail when comparing to reference: " + expectedToFail);
222             
223             if (_failure == null || !_failure.getContent()) {
224                 assertEquals("The Marshaled object differs from the gold file", 0, result);
225             } else if (expectedToFail) {
226                 assertTrue("The Marshaled object was expected to differ from the" +
227                            " gold file, but did not", result > 0);
228             }
229         }
230 
231         // 7. Marshal the Listener and compare it to the listener gold file, if any
232         compareListenerToItsGoldFile();
233 
234         // 8. Unmarshal the output file
235         Object unmarshaledOutput = null;
236         if (_builderClassName != null) {
237             try {
238                 unmarshaledOutput = _delegate.testUnmarshal(marshal_output);
239             } catch (Exception e) {
240                 LOG.error("Problem unmarshalling output from marshalling step", e);
241                 if (!_delegate.checkExceptionWasExpected(e, FailureStepType.SECOND_UNMARSHAL)) {
242                     fail("Exception Unmarshaling from disk " + e);
243                 }
244                 return;
245             }
246 
247             if (_failure != null && _failure.getContent() && _failure.getFailureStep() != null &&
248                     _failure.getFailureStep().equals(FailureStepType.SECOND_UNMARSHAL)) {
249                 fail("Second unmarshaling was expected to fail, but succeeded");
250                 return;
251             }
252 
253         }
254         // 9. Compare unmarshaled output file to ObjectModelBuilder if any.
255         // TODO: Fix the tests that fail this comparison!
256         // Right now many test classes (under xml/MasterTestSuite) do not override equals.
257         // We could check "(ref instanceof CastorTestable)" except that several srcgen
258         // tests fails this check.  (Probably bugs!)  For now we have this bogus
259         // _builderClassName check.  We ideally want to ALWAYS do this comparison.
260         if (_builderClassName != null && unmarshaledOutput != null) {
261             // the equals method must be overriden
262             boolean result  = unmarshaledOutput.equals(ref);
263             if (result == false) {
264                 verbose("Make sure the reference object model overrides Object#equals");
265             }
266             verbose("Compare to reference object: " + ((result)?"OK":" ### Failed ### "));
267 
268             final FailureStepType step = _failure != null ? _failure.getFailureStep() : null;
269             final boolean expectedToFail = _failure != null && _failure.getContent()
270                            && (step == null || step.equals(FailureStepType.SECOND_COMPARE));
271 
272             if (_failure == null || !_failure.getContent()) {
273                 assertTrue("The initial reference object and the one resulting of the " +
274                            "marshal/unmarshal process are different", result);
275             } else if (expectedToFail) {
276                 assertFalse("Comparing the reference object to the marshal+unmarshaled " +
277                             "one was expected to fail, but succeeded", result);
278             }
279 
280             if (expectedToFail ^ result) {
281                 return;
282             }
283         }
284 
285         if (_builderClassName != null) {
286             if (_failure != null && _failure.getContent()) {
287                 fail("The test with reference document was expected to fail, but passed");
288             }
289         }
290     }
291 
292     /**
293      * Returns a reference document as generated by unmarshaling the input
294      * document provided, if one was provided.
295      * @return a reference document from an unmarshaled input document
296      * @throws Exception if anything goes wrong loading the input document
297      */
298     private Object getUnmarshaledReference() throws Exception {
299         InputStream _input = null;
300         if (_inputName != null) {
301             _input = _test.getClassLoader().getResourceAsStream(_inputName);
302             assertNotNull("The input file '" + _inputName + "' cannot be found.", _input);
303         }
304 
305         verbose("--> Unmarshaling '" + _inputName  + "'\n");
306 
307         Object refUnmarshal = null;
308         if (_input != null) {
309             refUnmarshal = _delegate.testUnmarshal(_input);
310             _input.close();
311         }
312 
313         // If we didn't throw an exception, make sure we were supposed to succeed
314 
315         if (_failure != null && _failure.getContent() && _failure.getFailureStep() != null &&
316             _failure.getFailureStep().equals(FailureStepType.UNMARSHAL_REFERENCE)) {
317             fail("Unmarshaling the reference document was expected to fail, but succeeded");
318         }
319 
320         assertNotNull("Unmarshaling '" + _inputName + "' results in a NULL object.", refUnmarshal);
321 
322         return refUnmarshal;
323     }
324 
325     /**
326      * Returns a reference document as generated by the hard-coded
327      * ObjectModelBuilder.
328      *
329      * @return a reference document as generated by the hard-coded
330      *         ObjectModelBuilder
331      * @throws Exception if anything goes wrong executing creating the
332      *             ObjectModelBuilder or using it to create the reference
333      *             document.
334      */
335     private Object getBuilderReference() throws Exception {
336         Object generated = null;
337         if (_builderClassName != null) {
338             generated = _delegate.buildObjectModel(_builderClassName);
339             assertNotNull("The generated object with '" + _builderClassName + "' is null", generated);
340         }
341         return generated;
342     }
343 
344     /**
345      * Make sure the ObjectModelBuilder object and the unmarshaled input document
346      * created the same object.
347      * @param refUnmarshal reference object created from unmarshaling an input document
348      * @param refGenerated reference object created by an ObjectModelBuilder
349      * @throws IOException if we get an error dumping the objects to disk.
350      */
351     private void compareReferenceObjects(Object refUnmarshal, Object refGenerated) throws IOException {
352         //the object model must override the equals method.
353         boolean result = refGenerated.equals(refUnmarshal);
354         verbose("----> Compare unmarshaled document to reference object: " + ((result)?"OK":"### Failed ### "));
355         if (result == false) {
356             verbose("Make sure the reference object model overrides Object#equals");
357         }
358 
359         if (result == false && refGenerated instanceof CastorTestable) {
360             // try to dump the unmarshaled object and the reference object
361             FileWriter writer = new FileWriter(new File(_delegate._outputRootFile, _outputName + "-refModel.dump"));
362             writer.write(((CastorTestable)refGenerated).dumpFields());
363             writer.close();
364 
365             writer = new FileWriter(new File(_delegate._outputRootFile, _outputName + "-refUnmarshal.dump"));
366             writer.write(((CastorTestable)refUnmarshal).dumpFields());
367             writer.close();
368         }
369 
370         assertTrue("The unmarshaled reference object differs from the hardcoded reference object.", result);
371     }
372 
373     /**
374      * Tests that the listener saw what it was supposed to see. If we have a
375      * listener configured, marshal it to disk (now that it listened to the
376      * previous marshal) and compare it to the listener gold file, if any.
377      * <p>
378      * We have to unregister the listener before we do anything. When we marshal
379      * the MarshalListener, for every item marshaled the listener may grow and
380      * may have more to marshal, and thus we may end up in an endless loop. For
381      * example, a simple implementation of MarshalListener could log each
382      * pre/post marshal invocation on a Vector to allow for later comparisons.
383      * But this means that the object *being marshaled* keeps getting data added
384      * to it during the marshaling!
385      *
386      * @throws Exception if anything goes wrong during the test
387      */
388     private void compareListenerToItsGoldFile() throws Exception {
389         if (_delegate._listenerGoldFile == null || _delegate._listener == null) {
390             return;
391         }
392 
393         verbose("Compare listener to gold file: " + _delegate._listenerGoldFile);
394 
395         // Get the listener (before we unregister it!)
396         Object listener = _delegate._listener;
397 
398         // Unregister the listener
399         _delegate._listener = null;
400 
401         File outputFile;
402         try {
403             outputFile = _delegate.testMarshal(listener, "Listener-" + _outputName);
404         } catch (Exception e) {
405             if (!_delegate.checkExceptionWasExpected(e, FailureStepType.LISTENER_COMPARISON)) {
406                 fail("Exception Unmarshaling from disk " + e);
407             }
408             return;
409         }
410 
411         int result = CTFUtils.compare(_delegate._outputRootFile + "/" +  _delegate._listenerGoldFile, outputFile.getAbsolutePath());
412         verbose("----> Compare marshaled document to gold file '" + _delegate._listenerGoldFile + "': " + ((result == 0)?"OK":"### Failed ### "));
413 
414         if (_failure != null && _failure.getContent()) {
415             // Are we are supposed to fail AT THIS STEP?  If not, don't check
416             if (_failure.getFailureStep() != null &&
417                 _failure.getFailureStep().equals(FailureStepType.LISTENER_COMPARISON)) {
418                 assertTrue("The Marshaled Listener is supposed to differ from its gold file", result != 0);
419             }
420         } else {
421             assertEquals("The Marshaled Listener differs from its gold file", 0, result);
422         }
423     }
424 
425     private void verbose(String message) {
426         _delegate.verbose(message);
427     }
428 
429 }