• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id$
20  */
21 
22 /*
23  *
24  * StylesheetTestlet.java
25  *
26  */
27 package org.apache.qetest.xsl;
28 
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.util.Hashtable;
32 import java.util.Iterator;
33 import java.util.Map;
34 import java.util.Properties;
35 
36 import org.apache.qetest.CheckService;
37 import org.apache.qetest.Datalet;
38 import org.apache.qetest.Logger;
39 import org.apache.qetest.QetestFactory;
40 import org.apache.qetest.QetestUtils;
41 import org.apache.qetest.TestletImpl;
42 import org.apache.qetest.xslwrapper.TransformWrapper;
43 import org.apache.qetest.xslwrapper.TransformWrapperFactory;
44 
45 /**
46  * Testlet for conformance testing of xsl stylesheet files.
47  *
48  * This class provides the default algorithm used for verifying
49  * Xalan's conformance to the XSLT spec.  It works in conjunction
50  * with StylesheetTestletDriver, which supplies the logic for
51  * choosing the testfiles to iterate over, and with
52  * TransformWrapper, which provides an XSL  processor- and
53  * method-independent way to process files (i.e. different
54  * flavors of TransformWrapper may be different products, as well
55  * as different processing models, like SAX, DOM or Streams).
56  *
57  * This class is broken up into common worker methods to make
58  * subclassing easier for alternate testing algorithm.
59  *
60  * @author Shane_Curcuru@lotus.com
61  * @version $Id$
62  */
63 public class StylesheetTestlet extends TestletImpl
64 {
65     // Initialize our classname for TestletImpl's main() method
66     static { thisClassName = "org.apache.qetest.xsl.StylesheetTestlet"; }
67 
68     // Initialize our defaultDatalet
69     { defaultDatalet = (Datalet)new StylesheetDatalet(); }
70 
71     /**
72      * Accesor method for a brief description of this test.
73      *
74      * @return String describing what this StylesheetTestlet does.
75      */
getDescription()76     public String getDescription()
77     {
78         return "StylesheetTestlet";
79     }
80 
81 
82     /**
83      * Run this StylesheetTestlet: execute it's test and return.
84      *
85      * @param Datalet to use as data point for the test.
86      */
execute(Datalet d)87     public void execute(Datalet d)
88 	{
89         // Ensure we have the correct kind of datalet
90         StylesheetDatalet datalet = null;
91         try
92         {
93             datalet = (StylesheetDatalet)d;
94         }
95         catch (ClassCastException e)
96         {
97             logger.checkErr("Datalet provided is not a StylesheetDatalet; cannot continue with " + d);
98             return;
99         }
100 
101         // Perform any other general setup needed
102         testletInit(datalet);
103         try
104         {
105             // Get a TransformWrapper of the appropriate flavor
106             TransformWrapper transformWrapper = getTransformWrapper(datalet);
107             // Transform our supplied input file...
108             testDatalet(datalet, transformWrapper);
109             transformWrapper = null;
110             // ...and compare with gold data
111             checkDatalet(datalet);
112         }
113         // Handle any exceptions from the testing
114         catch (Throwable t)
115         {
116             handleException(datalet, t);
117             return;
118         }
119 	}
120 
121 
122     /**
123      * Worker method to perform any pre-processing needed.
124      *
125      * @param datalet to test with
126      */
testletInit(StylesheetDatalet datalet)127     protected void testletInit(StylesheetDatalet datalet)
128     {
129         //@todo validate our Datalet - ensure it has valid
130         //  and/or existing files available.
131 
132         // Cleanup outName only if asked to - delete the file on disk
133         // Optimization: this takes extra time and often is not
134         //  needed, so only do this if the option is set
135         if ("true".equalsIgnoreCase(datalet.options.getProperty("deleteOutFile")))
136         {
137             try
138             {
139                 boolean btmp = (new File(datalet.outputName)).delete();
140                 logger.logMsg(Logger.TRACEMSG, "Deleting OutFile of::" + datalet.outputName
141                                      + " status: " + btmp);
142             }
143             catch (SecurityException se)
144             {
145                 logger.logMsg(Logger.WARNINGMSG, "Deleting OutFile of::" + datalet.outputName
146                                        + " threw: " + se.toString());
147             }
148         }
149     }
150 
151 
152     /**
153      * Worker method to get a TransformWrapper.
154      *
155      * @param datalet to test with
156      * @return TransformWrapper to use with this datalet
157      */
getTransformWrapper(StylesheetDatalet datalet)158     protected TransformWrapper getTransformWrapper(StylesheetDatalet datalet)
159     {
160         TransformWrapper transformWrapper = null;
161         try
162         {
163             transformWrapper = TransformWrapperFactory.newWrapper(datalet.flavor);
164             // Set our datalet's options as options in the wrapper
165             //@todo this is inefficient, since our datalet may
166             //  have many options that don't pertain to the wrapper,
167             //  but it does allow users to simply pass new options
168             //  without us having to change code
169             transformWrapper.newProcessor(datalet.options);
170         }
171         catch (Throwable t)
172         {
173             logger.logThrowable(Logger.ERRORMSG, t, getDescription() + " newWrapper/newProcessor threw");
174             logger.checkErr(getCheckDescription(datalet) + " newWrapper/newProcessor threw: " + t.toString());
175             return null;
176         }
177         return transformWrapper;
178     }
179 
180 
181     /**
182      * Worker method to actually perform the transform.
183      *
184      * Logs out applicable info; attempts to perform transformation.
185      *
186      * @param datalet to test with
187      * @param transformWrapper to have perform the transform
188      * @throws allows any underlying exception to be thrown
189      */
testDatalet(StylesheetDatalet datalet, TransformWrapper transformWrapper)190     protected void testDatalet(StylesheetDatalet datalet, TransformWrapper transformWrapper)
191             throws Exception
192     {
193         //@todo Should we log a custom logElement here instead?
194         logger.logMsg(Logger.TRACEMSG, "executing with: inputName=" + datalet.inputName
195                       + " xmlName=" + datalet.xmlName + " outputName=" + datalet.outputName
196                       + " goldName=" + datalet.goldName + " flavor="  + datalet.flavor
197                       + " paramName="  + datalet.paramName);
198 
199         // Optional: configure a test with XSLT parameters.
200         final File paramFile = new File(datalet.paramName);
201         if (paramFile.exists()) {
202             Properties params = new Properties();
203             final FileInputStream inStream = new FileInputStream(paramFile);
204             try {
205                 params.load(inStream);
206                 final Iterator iter = params.entrySet().iterator();
207                 while (iter.hasNext()) {
208                     Map.Entry entry = (Map.Entry) iter.next();
209                     transformWrapper.setParameter(null, entry.getKey().toString(), entry.getValue());
210                 }
211             } finally {
212                 inStream.close();
213             }
214         }
215 
216         // Simply have the wrapper do all the transforming
217         //  or processing for us - we handle either normal .xsl
218         //  stylesheet tests or just .xml embedded tests
219         long retVal = 0L;
220         if (null == datalet.inputName)
221         {
222             // presume it's an embedded test
223             long [] times = transformWrapper.transformEmbedded(datalet.xmlName, datalet.outputName);
224             retVal = times[TransformWrapper.IDX_OVERALL];
225         }
226         else
227         {
228             // presume it's a normal stylesheet test
229             long[] times = transformWrapper.transform(datalet.xmlName, datalet.inputName, datalet.outputName);
230             retVal = times[TransformWrapper.IDX_OVERALL];
231         }
232     }
233 
234 
235     /**
236      * Worker method to validate output file with gold.
237      *
238      * Logs out applicable info while validating output file.
239      *
240      * @param datalet to test with
241      * @throws allows any underlying exception to be thrown
242      */
checkDatalet(StylesheetDatalet datalet)243     protected void checkDatalet(StylesheetDatalet datalet)
244             throws Exception
245     {
246         // See if the datalet already has a fileChecker to use...
247         CheckService fileChecker = (CheckService)datalet.options.get("fileCheckerImpl");
248 
249         // ...if not, construct a default one with attributes
250         if (null == fileChecker) {
251 	    String fcName = datalet.options.getProperty("fileChecker");
252 	    Class fcClazz = QetestUtils.testClassForName(fcName,
253 							 QetestUtils.defaultPackages,
254 							 null);
255 	    if (null != fcClazz) {
256 		fileChecker = (CheckService) fcClazz.newInstance();
257 		fileChecker.applyAttributes(datalet.options);
258 	    }
259 	}
260 
261         if (null == fileChecker)
262         {
263             fileChecker = QetestFactory.newCheckService(logger, QetestFactory.TYPE_FILES);
264             // Apply any testing options to the fileChecker
265             fileChecker.applyAttributes(datalet.options);
266         }
267 
268         // Validate the file
269         if (Logger.PASS_RESULT
270             != fileChecker.check(logger,
271                                  new File(datalet.outputName),
272                                  new File(datalet.goldName),
273                                  getCheckDescription(datalet))
274            )
275         {
276             // Log a custom element with all the file refs
277             // Closely related to viewResults.xsl select='fileref"
278             //@todo check that these links are valid when base
279             //  paths are either relative or absolute!
280             Hashtable attrs = new Hashtable();
281             attrs.put("idref", (new File(datalet.inputName)).getName());
282             try
283             {
284                 attrs.put("baseref", System.getProperty("user.dir"));
285             }
286             catch (Exception e) { /* no-op, ignore */ }
287 
288             attrs.put("inputName", datalet.inputName);
289             attrs.put("xmlName", datalet.xmlName);
290             attrs.put("outputName", datalet.outputName);
291             attrs.put("goldName", datalet.goldName);
292             logger.logElement(Logger.STATUSMSG, "fileref", attrs, "Conformance test file references");
293         }
294     }
295 
296 
297     /**
298      * Worker method to validate or log exceptions thrown by testDatalet.
299      *
300      * Provided so subclassing is simpler; our implementation merely
301      * calls checkErr and logs the exception.
302      *
303      * @param datalet to test with
304      * @param e Throwable that was thrown
305      */
handleException(StylesheetDatalet datalet, Throwable t)306     protected void handleException(StylesheetDatalet datalet, Throwable t)
307     {
308         // Put the logThrowable first, so it appears before
309         //  the Fail record, and gets color-coded
310         logger.logThrowable(Logger.ERRORMSG, t, getDescription() + " " + datalet.getDescription());
311         logger.checkErr(getCheckDescription(datalet)
312                          + " threw: " + t.toString());
313     }
314 
315 
316     /**
317      * Worker method to construct a description.
318      *
319      * Simply concatenates useful info to override getDescription().
320      *
321      * @param datalet to test with
322      * @param e Throwable that was thrown
323      */
getCheckDescription(StylesheetDatalet datalet)324     protected String getCheckDescription(StylesheetDatalet datalet)
325     {
326         return getDescription()
327                 + "{" + datalet.flavor + "} "
328                 + datalet.getDescription();
329     }
330 }  // end of class StylesheetTestlet
331 
332