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