• 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  * XHTFileCheckService.java
25  *
26  */
27 package org.apache.qetest.xsl;
28 
29 import java.io.File;
30 import java.io.PrintWriter;
31 import java.io.StringWriter;
32 import java.util.Enumeration;
33 import java.util.Hashtable;
34 import java.util.Properties;
35 
36 import org.apache.qetest.CheckService;
37 import org.apache.qetest.ConsoleLogger;
38 import org.apache.qetest.Logger;
39 import org.apache.qetest.XMLFileLogger;
40 
41 /**
42  * Uses an XML/HTML/Text diff comparator to check or diff two files.
43  * @see #check(Logger logger, Object actual, Object reference, String msg, String id)
44  * @author Shane_Curcuru@lotus.com
45  * @version $Id$
46  */
47 public class XHTFileCheckService implements CheckService
48 {
49 
50     /** XHTComparator tool to diff two files. */
51     protected XHTComparator comparator = new XHTComparator();
52 
53     /** Stores last checkFile calls printed output. */
54     private StringWriter sw = null;
55 
56     /**
57      * Compare two objects for equivalence, and return appropriate result.
58      * Note that the order of actual, reference is important
59      * important in determining the result.
60      * <li>Typically:
61      * <ul>any unexpected Exceptions thrown -> ERRR_RESULT</ul>
62      * <ul>actual does not exist -> FAIL_RESULT</ul>
63      * <ul>reference does not exist -> AMBG_RESULT</ul>
64      * <ul>actual is equivalent to reference -> PASS_RESULT</ul>
65      * <ul>actual is not equivalent to reference -> FAIL_RESULT</ul>
66      * </li>
67      * Equvalence is first checked by parsing both files as XML to
68      * a DOM; if that has problems, we parse as HTML to a DOM; if
69      * that has problems, we create a DOM with a single text node
70      * after reading the file as text.  We then compare the two DOM
71      * trees for equivalence. Note that in XML mode differences in
72      * the XML header itself (i.e. standalone=no/yes) are not caught,
73      * and will still report a pass (this is a defect in our
74      * comparison method).
75      * Side effect: every call to check() fills some additional
76      * info about how the check() call was processed which is then
77      * returned from getExtendedInfo() - this happens no matter what
78      * the result of the check() call was.
79      *
80      * @param logger to dump any output messages to
81      * @param actual (current) Object to check
82      * @param reference (gold, or expected) Object to check against
83      * @param description of what you're checking
84      * @param msg comment to log out with this test point
85      * @param id ID tag to log out with this test point
86      * @return Logger.*_RESULT code denoting status; each method may
87      * define it's own meanings for pass, fail, ambiguous, etc.
88      */
XHTFileCheckService()89 	public  XHTFileCheckService()
90 	{
91 	  //No-op
92 	}
93 
check(Logger logger, Object actual, Object reference, String msg, String id)94     public int check(Logger logger, Object actual, Object reference,
95                      String msg, String id)
96     {
97         // Create our 'extended info' stuff now, so it will
98         //  always reflect the most recent call to this method
99         sw = new StringWriter();
100         PrintWriter pw = new PrintWriter(sw);
101 
102         if (((null == actual) || (null == reference )))
103         {
104             pw.println("XHTFileCheckService actual or reference was null!");
105             pw.flush();
106             logFileCheckElem(logger, "null", "null", msg, id, sw.toString());
107             logger.checkErr(msg, id);
108             return logger.ERRR_RESULT;
109         }
110         if (!((actual instanceof File) & (reference instanceof File)))
111         {
112             // Must have File objects to continue
113             pw.println("XHTFileCheckService only takes File objects!");
114             pw.flush();
115             logFileCheckElem(logger, actual.toString(), reference.toString(), msg, id, sw.toString());
116             logger.checkErr(msg, id);
117             return logger.ERRR_RESULT;
118         }
119 
120         File actualFile = (File) actual;
121         File referenceFile = (File) reference;
122 
123         // Fail if Actual file doesn't exist or is 0 len
124         if ((!actualFile.exists()) || (actualFile.length() == 0))
125         {
126             pw.println("actual(" + actualFile.toString() + ") did not exist or was 0 len");
127             pw.flush();
128             logFileCheckElem(logger, actualFile.toString(), referenceFile.toString(), msg, id, sw.toString());
129             logger.checkFail(msg, id);
130             return logger.FAIL_RESULT;
131         }
132 
133         // Ambiguous if gold file doesn't exist or is 0 len
134         if ((!referenceFile.exists()) || (referenceFile.length() == 0))
135         {
136             pw.println("reference(" + referenceFile.toString() + ") did not exist or was 0 len");
137             pw.flush();
138             logFileCheckElem(logger, actualFile.toString(), referenceFile.toString(), msg, id, sw.toString());
139             logger.checkAmbiguous(msg, id);
140             return Logger.AMBG_RESULT;
141         }
142 
143         boolean warning[] = new boolean[1];
144         warning[0] = false;
145         boolean isEqual = false;
146 
147         // Inefficient code to get around very rare spurious exception:
148         // java.io.IOException: The process cannot access the file because it is being used by another process
149 	    //    at java.io.Win32FileSystem.canonicalize(Native Method)
150 	    //    at java.io.File.getCanonicalPath(File.java:442)
151 	    //    at org.apache.qetest.xsl.XHTFileCheckService.check(XHTFileCheckService.java:181)
152         // So get filenames first separately, then call comparator
153         String referenceFileName = referenceFile.getAbsolutePath();
154         String actualFileName = actualFile.getAbsolutePath();
155         try
156         {
157             referenceFileName = referenceFile.getCanonicalPath();
158             // Occasional spurious exception happens here, perhaps
159             //  because sometimes the previous transform or whatever
160             //  hasn't quite closed the actualFile yet
161             actualFileName = actualFile.getCanonicalPath();
162         }
163         catch (Exception e) { /* no-op, ignore */ }
164 
165         try
166         {
167             // Note calling order (gold, act) is different than checkFiles()
168             isEqual = comparator.compare(referenceFileName,
169                                          actualFileName, pw,
170                                          warning, attributes);
171             // Side effect: fills in pw/sw with info about the comparison
172         }
173         catch (Throwable t)
174         {
175             // Add any exception info to pw/sw; this will automatically
176             //  get logged out later on via logFileCheckElem
177             pw.println("XHTFileCheckService threw: " + t.toString());
178             t.printStackTrace(pw);
179             isEqual = false;
180         }
181 
182         // If not equal at all, fail
183         if (!isEqual)
184         {
185             pw.println("XHTFileCheckService files were not equal");
186             pw.flush();
187             logFileCheckElem(logger, actualFile.toString(), referenceFile.toString(), msg, id, sw.toString());
188             logger.checkFail(msg, id);
189             return Logger.FAIL_RESULT;
190         }
191         // If whitespace-only diffs, then pass/fail based on allowWhitespaceDiff
192         else if (warning[0])
193         {
194             pw.println("XHTFileCheckService whitespace diff warning!");
195             pw.flush();
196             if (allowWhitespaceDiff)
197             {
198                 logger.logMsg(Logger.STATUSMSG, "XHTFileCheckService whitespace diff warning, passing!");
199                 logger.checkPass(msg, id);
200                 return Logger.PASS_RESULT;
201             }
202             else
203             {
204                 logFileCheckElem(logger, actualFile.toString(), referenceFile.toString(), msg, id,
205                         "XHTFileCheckService whitespace diff warning, failing!\n" + sw.toString());
206                 logger.checkFail(msg, id);
207                 return Logger.FAIL_RESULT;
208             }
209         }
210         // Otherwise we were completely equal, so pass
211         else
212         {
213             pw.println("XHTFileCheckService files were equal");
214             pw.flush();
215             // For pass case, we *dont* call logFileCheckElem
216             logger.checkPass(msg, id);
217             return Logger.PASS_RESULT;
218         }
219     }
220 
221     /**
222      * Logs a custom element about the current check() call.
223      * <pre>
224      * <fileCheck level="40"
225      * reference="tests\conf-gold\match\match16.out"
226      * reportedBy="XHTFileCheckService"
227      * actual="results-alltest\dom\match\match16.out"
228      * >
229      * StylesheetTestlet match16.xsl(null)
230      * XHTFileCheckService threw: java.io.IOException: The process cannot access the file because it is being used by another process
231      * java.io.IOException: The process cannot access the file because it is being used by another process
232      * 	at java.io.Win32FileSystem.canonicalize(Native Method)
233      *     etc...
234      * XHTFileCheckService files were not equal
235      *
236      * </fileCheck>
237      * </pre>
238      * @param logger to dump any output messages to
239      * @param name of actual (current) File to check
240      * @param name of reference (gold, or expected) File to check against
241      * @param msg comment to log out with this test point
242      * @param id to log out with this test point
243      * @param additional log info from PrintWriter/StringWriter
244      */
logFileCheckElem(Logger logger, String actualFile, String referenceFile, String msg, String id, String logs)245     protected void logFileCheckElem(Logger logger,
246             String actualFile, String referenceFile,
247             String msg, String id, String logs)
248     {
249         Hashtable attrs = new Hashtable();
250         attrs.put("actual", actualFile);
251         attrs.put("reference", referenceFile);
252         attrs.put("reportedBy", "XHTFileCheckService");
253         try
254         {
255             attrs.put("baseref", System.getProperty("user.dir"));
256         }
257         catch (Exception e) { /* no-op, ignore */ }
258         String elementBody = msg + "(" + id + ") \n" + logs;
259         // HACK: escapeString(elementBody) so that it's legal XML
260         //  for cases where we have XML output.  This isn't
261         //  necessarily a 'hack', I'm just not sure what the
262         //  cleanest place to put this is (here or some sort
263         //  of intelligent logic in XMLFileLogger)
264         elementBody = XMLFileLogger.escapeString(elementBody);
265 		logger.logElement(Logger.STATUSMSG, "fileCheck", attrs, elementBody);
266     }
267 
268     /**
269      * Compare two objects for equivalence, and return appropriate result.
270      *
271      * @see #check(Logger logger, Object actual, Object reference, String msg, String id)
272      * @param logger to dump any output messages to
273      * @param actual (current) File to check
274      * @param reference (gold, or expected) File to check against
275      * @param description of what you're checking
276      * @param msg comment to log out with this test point
277      * @return Logger.*_RESULT code denoting status; each method may
278      * define it's own meanings for pass, fail, ambiguous, etc.
279      */
check(Logger logger, Object actual, Object reference, String msg)280     public int check(Logger logger, Object actual, Object reference,
281                      String msg)
282     {
283         return check(logger, actual, reference, msg, null);
284     }
285 
286     /**
287      * Prefix to all attrs we understand.
288      * Note: design-wise, it would be better to have these constants
289      * in the XHTComparator class, since we know we're tightly bound
290      * to them anyways, and they shouldn't really be bound to us.
291      * But for my current purposes, it's simpler to put them here
292      * for documentation purposes.
293      */
294     public static final String URN_XHTFILECHECKSERVICE = "urn:XHTFileCheckService:";
295 
296     /** Whether whitespace differences will cause a fail or not.  */
297     public static final String ALLOW_WHITESPACE_DIFF = URN_XHTFILECHECKSERVICE + "allowWhitespaceDiff";
298 
299     /** If we should call parser.setValidating().  */
300     public static final String SETVALIDATING = URN_XHTFILECHECKSERVICE + "setValidating";
301 
302     /** If we should call parser.setIgnoringElementContentWhitespace().  */
303     public static final String SETIGNORINGELEMENTCONTENTWHITESPACE = URN_XHTFILECHECKSERVICE + "setIgnoringElementContentWhitespace";
304 
305     /** If we should call parser.setExpandEntityReferences().  */
306     public static final String SETEXPANDENTITYREFERENCES = URN_XHTFILECHECKSERVICE + "setExpandEntityReferences";
307 
308     /** If we should call parser.setIgnoringComments().  */
309     public static final String SETIGNORINGCOMMENTS = URN_XHTFILECHECKSERVICE + "setIgnoringComments";
310 
311     /** If we should call parser.setCoalescing().  */
312     public static final String SETCOALESCING = URN_XHTFILECHECKSERVICE + "setCoalescing";
313 
314     /**
315      * Whether whitespace differences will cause a fail or not.
316      * setAttribute("allow-whitespace-diff", true|false)
317      * true=whitespace-only diff will pass;
318      * false, whitespace-only diff will fail
319      */
320     protected boolean allowWhitespaceDiff = false;  // default; backwards compatible
321 
322     /**
323      * Properties/Hash of parser-like attributes that have been set.
324      */
325     protected Properties attributes = null;
326 
327     /**
328      * Allows the user to set specific attributes on the testing
329      * utility or it's underlying product object under test.
330      *
331      * Supports basic JAXP DocumentBuilder attributes, plus our own
332      * ALLOW_WHITESPACE_DIFF attribute.
333      *
334      * @param name The name of the attribute.
335      * @param value The value of the attribute.
336      * @throws IllegalArgumentException thrown if the underlying
337      * implementation doesn't recognize the attribute and wants to
338      * inform the user of this fact.
339      */
setAttribute(String name, Object value)340     public void setAttribute(String name, Object value)
341         throws IllegalArgumentException
342     {
343         // Check for our own attributes first
344         if (ALLOW_WHITESPACE_DIFF.equals(name))
345         {
346             try
347             {
348                 allowWhitespaceDiff = (new Boolean((String)value)).booleanValue();
349             }
350             catch (Throwable t)
351             {
352                 // If it's an illegal value or type, ignore it
353             }
354         }
355         else
356         {
357             if (null == attributes)
358             {
359                 attributes = new Properties();
360             }
361             attributes.put(name, value);
362         }
363     }
364 
365     /**
366      * Allows the user to set specific attributes on the testing
367      * utility or it's underlying product object under test.
368      *
369      * This method should attempt to set any applicable attributes
370      * found in the given attrs onto itself, and will ignore any and
371      * all attributes it does not recognize.  It should never
372      * throw exceptions.  This method will overwrite any previous
373      * attributes that were set.
374      * This method will only set values that are Strings findable
375      * by the Properties.getProperty() method.
376      *
377      * Future Work: this could be optimized by simply setting our
378      * Properties block to default from the passed-in one, but for
379      * now instead it only copies over the explicit values that
380      * we think are applicable.
381      *
382      * @param attrs Props of various name, value attrs.
383      */
applyAttributes(Properties attrs)384     public void applyAttributes(Properties attrs)
385     {
386         attributes = null;
387         for (Enumeration names = attrs.propertyNames();
388                 names.hasMoreElements(); /* no increment portion */ )
389         {
390             String key = (String)names.nextElement();
391             if (key.startsWith(URN_XHTFILECHECKSERVICE))
392             {
393                 setAttribute(key, attrs.getProperty(key));
394             }
395         }
396     }
397 
398     /**
399      * Allows the user to retrieve specific attributes on the testing
400      * utility or it's underlying product object under test.
401      *
402      * See applyAttributes for some limitations.
403      *
404      * @param name The name of the attribute.
405      * @return value of supported attributes or null if not recognized.
406      * @throws IllegalArgumentException thrown if the underlying
407      * implementation doesn't recognize the attribute and wants to
408      * inform the user of this fact.
409      */
getAttribute(String name)410     public Object getAttribute(String name)
411         throws IllegalArgumentException
412     {
413         // Check for our own attributes first
414         if (ALLOW_WHITESPACE_DIFF.equals(name))
415         {
416             return new Boolean(allowWhitespaceDiff);
417         }
418         else if (null != attributes)
419         {
420             return attributes.get(name);
421         }
422         else
423             return null;
424     }
425 
426     /**
427      * Description of what this testing utility does.
428      *
429      * @return String description of extension
430      */
getDescription()431     public String getDescription()
432     {
433         return ("Uses an XML/HTML/Text diff comparator to check or diff two files.");
434     }
435 
436     /**
437      * Gets extended information about the last check call.
438      * This info is filled in for every call to check() with brief
439      * descriptions of what happened; will return
440      * <code>XHTFileCheckService-no-info-available</code> if
441      * check() has never been called.
442      * @return String describing any additional info about the
443      * last two files that were checked
444      */
getExtendedInfo()445     public String getExtendedInfo()
446     {
447 
448         if (sw != null)
449             return sw.toString();
450         else
451             return "XHTFileCheckService-no-info-available";
452     }
453 
454     /**
455      * Main method to run test from the command line - can be left alone.
456      * @param args command line argument array
457      */
main(String[] args)458     public  static void main(String[] args)
459     {
460 		if (args.length < 2)
461 		{
462 			System.out.println("	Please provide two files to compare");
463 		}
464 		else
465 		{
466 			ConsoleLogger log = new ConsoleLogger();
467 			XHTFileCheckService app = new XHTFileCheckService();
468 			System.out.println("\nThank you for using XHTFileCheckService");
469 			System.out.println( app.getDescription() );
470 			System.out.println("We hope your results are satisfactory");
471 			System.out.println("\n" + args[0] + "  " + args[1]);
472 
473 			File fAct = new File(args[0]);
474 			File fExp = new File(args[1]);
475 
476 			try
477 			{
478 				app.check(log, fAct, fExp, "Check");
479 			}
480 			catch (Exception e)
481 			{
482 			   System.out.println ("main() caught unexpected Exception");
483 			}
484 		}
485     }
486 }  // end of class XHTFileCheckService
487 
488