View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Intalio, Inc.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Intalio, Inc. Exolab is a registered
23   *    trademark of Intalio, Inc.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2002-2003 (C) Intalio, Inc. All Rights Reserved.
42   *
43   * $Id: CastorTestCase.java 6787 2007-01-29 06:00:49Z ekuns $
44   */
45  package org.castor.xmlctf;
46  
47  import java.io.File;
48  import java.io.IOException;
49  import java.io.InputStream;
50  import java.io.InputStreamReader;
51  import java.net.MalformedURLException;
52  import java.net.URL;
53  import java.net.URLClassLoader;
54  import java.util.jar.JarFile;
55  
56  import junit.framework.Test;
57  import junit.framework.TestCase;
58  import junit.framework.TestSuite;
59  
60  import org.castor.xmlctf.util.FileServices;
61  import org.exolab.castor.tests.framework.testDescriptor.MarshallingTest;
62  import org.exolab.castor.tests.framework.testDescriptor.OnlySourceGenerationTest;
63  import org.exolab.castor.tests.framework.testDescriptor.SchemaTest;
64  import org.exolab.castor.tests.framework.testDescriptor.SourceGeneratorTest;
65  import org.exolab.castor.tests.framework.testDescriptor.TestDescriptor;
66  import org.exolab.castor.tests.framework.testDescriptor.TestDescriptorChoice;
67  import org.exolab.castor.tests.framework.testDescriptor.UnitTestCase;
68  import org.exolab.castor.xml.MarshalException;
69  import org.exolab.castor.xml.ValidationException;
70  
71  /**
72   * Abstracts a test case in the CTF (Castor Test Framework). A CTF test case can
73   * be driven by a directory or by a JAR file.
74   *
75   * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
76   * @version $Revision: 6787 $ $Date: 2004-03-08 17:23:25 -0700 (Mon, 08 Mar 2004) $
77   */
78  public class CastorTestCase extends TestCase {
79  
80      /** An unknown type of Castor test case. */
81      public static final short UNKNOWN   = -1;
82      /** A directory-based Castor test case. */
83      public static final short DIRECTORY = 0;
84      /** A JAR-based Castor test case. */
85      public static final short JAR       = 1;
86  
87      /** Name of the resource for the test descriptor XML document. */
88      public static final String TEST_DESCRIPTOR = "TestDescriptor.xml";
89      /** Name of the resource for the test descriptor XML document if a JAR file is used. */
90      private static final String TEST_DESCRIPTOR_JAR = "META-INF/TestDescriptor.xml";
91      /** File separator for this system. */
92      private static final String FILE_SEPARATOR = System.getProperty("file.separator");
93      /** Java version of the JVM we are running in. */
94      private static final float JAVA_VERSION = Float.parseFloat(System.getProperty("java.specification.version"));
95      /** True if we desire a lot of information on what is happening during the test. */
96      private static final boolean VERBOSE;
97  
98      static {
99          String v = System.getProperty(TestCaseAggregator.VERBOSE_PROPERTY);
100         VERBOSE = (v != null && v.equals("true"));
101         v = null;
102     }
103 
104     /**
105      * True if we dump the stack trace that is generated from any validation
106      * exception or marshal exception caused by reading the XML Test
107      * Description.
108      */
109     private static boolean _printStack;
110 
111     static {
112         String v = System.getProperty(TestCaseAggregator.PRINT_STACK_TRACE);
113         _printStack = (v != null && v.equals("true"));
114         v = null;
115     }
116 
117     /** Indicates whether or not the output root directory has been compiled. */
118     private boolean _compiled = false;
119     /** Class loader to use for the jar. */
120     private ClassLoader _loader;
121     /** The test descriptor from the jar. */
122     private TestDescriptor _testDescriptor;
123     /** The file that contains the tests. This can either be a directory or a jar file. */
124     private final File _testFile;
125     /** The Type of the test (directory or jar). */
126     private final short _type;
127     /** Place where the temporary files and other output are created. */
128     private final File _outputRootFile;
129     /** String containing the directory path from the test root to here. */
130     private final String _directoryToHere;
131 
132     /**
133      * Constructs a CTF test case given only a test case name.
134      * @param name the name of the test case
135      */
136     public CastorTestCase(final String name) {
137         super(name);
138         _testFile        = null;
139         _outputRootFile  = null;
140         _directoryToHere = "";
141         _type            = UNKNOWN;
142     }
143 
144     /**
145      * Constructs a CTF test case given a File (either a JAR file or a
146      * directory) and a directory where temporary files will be placed. The test
147      * case name will be derived from the file (JAR or directory) name.
148      *
149      * @param file Either a directory containing TestDescriptor.xml or a JAR
150      *            file containing META-INF/TestDescriptor.xml
151      * @param directoryToHere directory path leading to the current test.
152      * @param outputRoot Directory where temporary files and output will go.
153      */
154     public CastorTestCase(final File file, final String directoryToHere, final String outputRoot) {
155         super(directoryToHere);
156         _directoryToHere = directoryToHere;
157 
158         if (file.isDirectory()) {
159             _type = DIRECTORY;
160             _outputRootFile = new File(outputRoot + FILE_SEPARATOR);
161         } else {
162             _type = JAR;
163             try {
164                 new JarFile(file);
165                 String fileName = file.getName();
166                 fileName = fileName.substring(0, fileName.lastIndexOf("."));
167                 _outputRootFile = new File(outputRoot + FILE_SEPARATOR + fileName);
168             } catch (java.util.zip.ZipException e) {
169                 throw new IllegalStateException(file.getAbsolutePath() + " is not a valid JAR file.");
170             } catch (java.io.IOException ie) {
171                 throw new IllegalStateException(file.getAbsolutePath() + " is not a valid JAR file.");
172             }
173         }
174 
175         // Append to our current class loader the directory or JAR containing our test case
176         try {
177             URL[] urlList = {file.toURL()};
178             _loader =  new URLClassLoader(urlList, this.getClass().getClassLoader());
179         } catch (MalformedURLException urle) {
180              //should never happen--> failure before
181              urle.printStackTrace();
182         }
183         _testFile = file;
184         _outputRootFile.mkdirs();
185     }
186 
187     public ClassLoader getClassLoader() {
188         return _loader;
189     }
190 
191     public File getTestFile() {
192         return _testFile;
193     }
194 
195     public short getType() {
196         return _type;
197     }
198 
199     public String getDirectoryToHere() {
200         return _directoryToHere;
201     }
202 
203     public File getOutputRootFile() {
204         return _outputRootFile;
205     }
206 
207     /**
208      * Returns a boolean that when true indicates the output directory has been
209      * compiled. This is useful for preventing the compilation of a directory
210      * multiple times when more than one test case exists in a given directory.
211      *
212      * @return true when the output root directory has already been compiled.
213      */
214     public boolean isDirectoryCompiled() {
215         return _compiled;
216     } //-- isDirectoryCompiled
217 
218    /**
219      * Sets the ClassLoader to use for loading the resources for this test case.
220      *
221      * @param loader the class loader to use
222      */
223     public void setClassLoader(final ClassLoader loader) {
224         _loader = loader;
225     }
226 
227     /**
228      * Sets a flag to indicate the output directory has been compiled. This
229      * prevents compiling a directory multiple times unnecessarily when more
230      * than one test case exists in a given directory.
231      *
232      * @param compiled true if the output directory for this test case has been
233      *            compiled
234      */
235     public void setDirectoryCompiled(final boolean compiled) {
236         _compiled = compiled;
237     } //-- setDirectoryCompiled
238 
239     /**
240      * Assembles and returns a test suite containing all known tests.
241      *
242      * @return A non-null test suite if we can load the test descriptor
243      */
244     public Test suite() {
245         final InputStream descriptor;
246         if (_type == JAR) {
247             descriptor = _loader.getResourceAsStream(TEST_DESCRIPTOR_JAR);
248         } else {
249             descriptor = _loader.getResourceAsStream(TEST_DESCRIPTOR);
250         }
251 
252         if (descriptor == null) {
253             verbose("test '" + _testFile.getName() + "' has no TestDescriptor.xml");
254             return null;
255         }
256 
257         try {
258             _testDescriptor = TestDescriptor.unmarshal(new InputStreamReader(descriptor));
259         } catch (ValidationException ve) {
260             verbose("Error reading: " + _testFile.getAbsolutePath());
261             verbose("-> " + ve.toString());
262             if (_printStack) {
263                 ve.printStackTrace(System.out);
264             }
265             fail(ve.toString());
266         } catch (MarshalException me) {
267             verbose("Error reading: " + _testFile.getAbsolutePath());
268             verbose("-> " + me.toString());
269             if (_printStack) {
270                 me.printStackTrace(System.out);
271             }
272             fail(me.toString());
273         } finally {
274             try {
275                 descriptor.close();
276             } catch (IOException e) {
277                 // ignore
278             }
279         }
280 
281         if (_testDescriptor.hasMinimumJavaVersion()) {
282             // Get minimum Java version & convert to our Canonical form
283             float minVersion = _testDescriptor.getMinimumJavaVersion();
284             if (minVersion >= 5F && minVersion < 10F) {
285                 minVersion = 1.0F + (minVersion / 10F);
286             }
287             if (JAVA_VERSION < minVersion) {
288                 verbose("-->Test requires at minimum Java " + minVersion + ", but we are running Java " + JAVA_VERSION);
289                 verbose("-->Skipping the test");
290                 return null;
291             }
292         }
293 
294         if (_testDescriptor.hasMaximumJavaVersion()) {
295             // Get maximum Java version & convert to our Canonical form
296             float maxVersion = _testDescriptor.getMaximumJavaVersion();
297             if (maxVersion >= 5F && maxVersion < 10F) {
298                 maxVersion = 1.0F + (maxVersion / 10F);
299             }
300             if (JAVA_VERSION > maxVersion) {
301                 verbose("-->Test is designed to run up to Java " + maxVersion + ", but we are running Java " + JAVA_VERSION);
302                 verbose("-->Skipping the test");
303                 return null;
304             }
305         }
306 
307         final String suiteName = _directoryToHere + _testDescriptor.getName();
308         final TestSuite suite = new TestSuite(suiteName);
309         verbose("Creating '" + suiteName + "' test suite");
310 
311         TestDescriptorChoice choice = _testDescriptor.getTestDescriptorChoice();
312         MarshallingTest marshallingTests      = choice.getMarshallingTest();
313         SourceGeneratorTest sourceGenTests    = choice.getSourceGeneratorTest();
314         SchemaTest schemaTests                = choice.getSchemaTest();
315         OnlySourceGenerationTest genOnlyTests = choice.getOnlySourceGenerationTest();
316 
317         if (marshallingTests != null) {
318             setUpMarshallingTests(suiteName, suite, marshallingTests);
319         }
320         if (sourceGenTests != null) {
321             setUpSourceGeneratorTests(suiteName, suite, sourceGenTests);
322         }
323         if (schemaTests != null) {
324             setUpSchemaTests(suiteName, suite, schemaTests);
325         }
326         if (genOnlyTests != null) {
327             setUpGenerationOnlyTests(suiteName, suite, genOnlyTests);
328         }
329 
330         return suite;
331     }
332 
333     /**
334      * Loops over all Marshalling tests from our TestDescriptor.xml, configures
335      * each test and adds it to our suite.
336      *
337      * @param suiteName Test Suite name
338      * @param suite the Test Suite to add all unit tests to
339      * @param mar a collection of Marshalling Unit Tests
340      */
341     private void setUpMarshallingTests(final String suiteName, final TestSuite suite,
342                                        final MarshallingTest mar) {
343         for (int i = 0; i < mar.getUnitTestCaseCount(); ++i) {
344             UnitTestCase tc = mar.getUnitTestCase(i);
345             MarshallingFrameworkTestCase mftc = new MarshallingFrameworkTestCase(this, tc, mar);
346             mftc._configuration = mar.getConfiguration();
347             mftc.setTestSuiteName(suiteName);
348             suite.addTest(mftc.suite());
349         }
350     }
351 
352     /**
353      * Loops over all Source Generation tests from our TestDescriptor.xml,
354      * configures each test and adds it to our suite.
355      *
356      * @param suiteName Test Suite name
357      * @param suite the Test Suite to add all unit tests to
358      * @param sg a collection of Source Generation Unit Tests
359      */
360     private void setUpSourceGeneratorTests(final String suiteName, final TestSuite suite,
361                                            final SourceGeneratorTest sg) {
362         for (int i = 0; i < sg.getUnitTestCaseCount(); ++i) {
363             UnitTestCase tc = sg.getUnitTestCase(i);
364             SourceGeneratorTestCase sgtc = new SourceGeneratorTestCase(this, tc, sg);
365             sgtc.setTestSuiteName(suiteName);
366             suite.addTest(sgtc.suite());
367         }
368     }
369 
370     /**
371      * Loops over all Schema tests from our TestDescriptor.xml, configures each
372      * test and adds it to our suite.
373      *
374      * @param suiteName Test Suite name
375      * @param suite the Test Suite to add all unit tests to
376      * @param schemaTest a collection of Schema Unit Tests
377      */
378     private void setUpSchemaTests(final String suiteName, final TestSuite suite,
379                                   final SchemaTest schemaTest) {
380         for (int i = 0; i < schemaTest.getUnitTestCaseCount(); i++) {
381             UnitTestCase tc = schemaTest.getUnitTestCase(i);
382             // Little trick: getUnitTestCaseChoice should not be null at this point
383             String name = tc.getUnitTestCaseChoice().getSchema();
384             if (name.equals("*")) {
385                 File[] list = _testFile.listFiles();
386                 for (int j = 0; j < list.length; ++j) {
387                     String fileName = list[j].getName();
388                     // FIXME:  It would be better to use a file filter and to make
389                     // sure our SchemaReader can read this file
390                     if (fileName.endsWith(FileServices.XSD)) {
391                         makeIndividualSchemaTest(suiteName, suite, tc, fileName);
392                     }
393                 }
394             } else {
395                 makeIndividualSchemaTest(suiteName, suite, tc, name);
396             }
397         }
398     }
399 
400     /**
401      * Loops over all Only-Source-Generation tests from our TestDescriptor.xml,
402      * configures each test and adds it to our suite.
403      *
404      * @param suiteName Test Suite name
405      * @param suite the Test Suite to add all unit tests to
406      * @param sg a collection of Source Generation Unit Tests
407      */
408     private void setUpGenerationOnlyTests(final String suiteName, final TestSuite suite,
409                                           final OnlySourceGenerationTest sg) {
410         for (int i = 0; i < sg.getUnitTestCaseCount(); ++i) {
411             UnitTestCase tc = sg.getUnitTestCase(i);
412             OnlySourceGenerationTestCase sgtc = new OnlySourceGenerationTestCase(this, tc, sg);
413             sgtc.setTestSuiteName(suiteName);
414             suite.addTest(sgtc.suite());
415         }
416     }
417 
418     /**
419      * Makes an individual Schema test and adds it to our Test Suite.
420      *
421      * @param suiteName Test Suite name
422      * @param suite the Test Suite to add all unit tests to
423      * @param tc our Test Case
424      * @param name Schema name
425      */
426     private void makeIndividualSchemaTest(final String suiteName, final TestSuite suite,
427                                           final UnitTestCase tc, final String name) {
428         tc.setName(suiteName + '#' + name);
429         SchemaTestCase stc = new SchemaTestCase(this, tc);
430         stc.setSchemaName(name);
431         suite.addTest(stc);
432     }
433 
434     /**
435      * Prints the provided message if verbose is true.
436      *
437      * @param message The message to display if verbose is true.
438      */
439     private void verbose(final String message) {
440         if (VERBOSE) {
441             System.out.println(message);
442         }
443     }
444 
445 }