• 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 package org.apache.qetest;
23 
24 import org.apache.test.android.AndroidFileUtils;
25 
26 import java.io.File;
27 import java.io.IOException;
28 import java.lang.reflect.Method;
29 import java.util.Hashtable;
30 
31 /**
32  * Static utility class for both general-purpose testing methods
33  * and a few XML-specific methods.
34  * Also provides a simplistic Test/Testlet launching helper
35  * functionality.  Simply execute this class from the command
36  * line with a full or partial classname (in the org.apache.qetest
37  * area, obviously) and we'll load and execute that class instead.
38  * @author shane_curcuru@lotus.com
39  * @version $Id$
40  */
41 public abstract class QetestUtils
42 {
43     // abstract class cannot be instantiated
44 
45     /**
46      * Utility method to translate a String filename to URL.
47      *
48      * Note: This method is not necessarily proven to get the
49      * correct URL for every possible kind of filename; it should
50      * be improved.  It handles the most common cases that we've
51      * encountered when running Conformance tests on Xalan.
52      * Also note, this method does not handle other non-file:
53      * flavors of URLs at all.
54      *
55      * If the name is null, return null.
56      * If the name starts with a common URI scheme (namely the ones
57      * found in the examples of RFC2396), then simply return the
58      * name as-is (the assumption is that it's already a URL)
59      * Otherwise we attempt (cheaply) to convert to a file:/// URL.
60      *
61      * @param String local path\filename of a file
62      * @return a file:/// URL, the same string if it appears to
63      * already be a URL, or null if error
64      */
filenameToURL(String filename)65     public static String filenameToURL(String filename)
66     {
67         // null begets null - something like the commutative property
68         if (null == filename)
69             return null;
70 
71         // Don't translate a string that already looks like a URL
72         if (isCommonURL(filename))
73             return filename;
74 
75         // Android-added: Look up the file in the java resources.
76         String androidUrl = AndroidFileUtils.getInputFileUrlString(filename);
77         if (androidUrl != null) {
78             return androidUrl;
79         }
80 
81         File f = new File(filename);
82         String tmp = null;
83         try
84         {
85             // This normally gives a better path
86             tmp = f.getCanonicalPath();
87         }
88         catch (IOException ioe)
89         {
90             // But this can be used as a backup, for cases
91             //  where the file does not exist, etc.
92             tmp = f.getAbsolutePath();
93         }
94 
95         // URLs must explicitly use only forward slashes
96         if (File.separatorChar == '\\') {
97             tmp = tmp.replace('\\', '/');
98         }
99         // Note the presumption that it's a file reference
100         // Ensure we have the correct number of slashes at the
101         //  start: we always want 3 /// if it's absolute
102         //  (which we should have forced above)
103         if (tmp.startsWith("/"))
104             return "file://" + tmp;
105         else
106             return "file:///" + tmp;
107 
108     }
109 
110 
111     /**
112      * Utility method to find a relative path.
113      *
114      * <p>Attempt to find a relative path based from the current
115      * directory (usually user.dir property).</p>
116      *
117      * <p>If the name is null, return null.  If the name starts
118      * with a common URI scheme (namely the ones
119      * found in the examples of RFC2396), then simply return
120      * the name itself (future work could attempt to detect
121      * file: protocols if needed).</p>
122      *
123      * @param String local path\filename of a file
124      * @return a local path\file that is relative; if we can't
125      * find one, we return the original name
126      */
filenameToRelative(String filename)127     public static String filenameToRelative(String filename)
128     {
129         // null begets null - something like the commutative property
130         if (null == filename)
131             return null;
132 
133         // Don't translate a string that already looks like a URL
134         if (isCommonURL(filename))
135             return filename;
136 
137         String base = null;
138         try
139         {
140             File userdir = new File(System.getProperty("user.dir"));
141             // Note: use CanonicalPath, since this ensures casing
142             //  will be identical between the two files
143             base = userdir.getCanonicalPath();
144         }
145         catch (Exception e)
146         {
147             // If we can't detect this, we can't determine
148             //  relativeness, so just return the name
149             return filename;
150         }
151         File f = new File(filename);
152         String tmp = null;
153         try
154         {
155             tmp = f.getCanonicalPath();
156         }
157         catch (IOException ioe)
158         {
159             tmp = f.getAbsolutePath();
160         }
161 
162         // If it's not relative to the base, just return as-is
163         //  (note: this may not be the answer you expect)
164         if (!tmp.startsWith(base))
165             return tmp;
166 
167         // Strip off the base
168         tmp = tmp.substring(base.length());
169         // Also strip off any beginning file separator, since we
170         //  don't want it to be mistaken for an absolute path
171         if (tmp.startsWith(File.separator))
172             return tmp.substring(1);
173         else
174             return tmp;
175     }
176 
177 
178     /**
179      * Worker method to detect common absolute URLs.
180      *
181      * @param s String path\filename or URL (or any, really)
182      * @return true if s starts with a common URI scheme (namely
183      * the ones found in the examples of RFC2396); false otherwise
184      */
isCommonURL(String s)185     protected static boolean isCommonURL(String s)
186     {
187         if (null == s)
188             return false;
189 
190         if (s.startsWith("file:")
191             || s.startsWith("http:")
192             || s.startsWith("ftp:")
193             || s.startsWith("gopher:")
194             || s.startsWith("mailto:")
195             || s.startsWith("news:")
196             || s.startsWith("telnet:")
197            )
198             return true;
199         else
200             return false;
201     }
202 
203 
204     /**
205      * Utility method to get a testing Class object.
206      * This is mainly a bit of syntactic sugar to allow users
207      * to specify only the end parts of a package.classname
208      * and still have it loaded.  It basically does a
209      * Class.forName() search, starting with the provided
210      * classname, and if not found, searching through a list
211      * of root packages to try to find the class.
212      *
213      * Note the inherent danger when there are same-named
214      * classes in different packages, where the behavior will
215      * depend on the order of searchPackages.
216      *
217      * Commonly called like:
218      * <code>testClassForName("PerformanceTestlet",
219      * new String[] {"org.apache.qetest", "org.apache.qetest.xsl" },
220      * "org.apache.qetest.StylesheetTestlet");</code>
221      *
222      * @param String classname FQCN or partially specified classname
223      * that you wish to load
224      * @param String[] rootPackages a list of packages to search
225      * for the classname specified in array order; if null then
226      * we don't search any additional packages
227      * @param String defaultClassname a default known-good FQCN to
228      * return if the classname was not found
229      *
230      * @return Class object asked for if one found by combining
231      * clazz with one of the rootPackages; if none, a Class of
232      * defaultClassname; or null if an error occoured
233      */
testClassForName(String classname, String[] rootPackages, String defaultClassname)234     public static Class testClassForName(String classname,
235                                          String[] rootPackages,
236                                          String defaultClassname)
237     {
238         // Ensure we have a valid classname, and try it
239         if ((null != classname) && (classname.length() > 0))
240         {
241             // Try just the specified classname, in case it's a FQCN
242             try
243             {
244                 return Class.forName(classname);
245             }
246             catch (Exception e)
247             {
248                 /* no-op, fall through */
249             }
250 
251             // Now combine each of the rootPackages with the classname
252             //  and see if one of them gets loaded
253             if (null != rootPackages)
254             {
255                 for (int i = 0; i < rootPackages.length; i++)
256                 {
257                     try
258                     {
259                         return Class.forName(rootPackages[i] + "." + classname);
260                     }
261                     catch (Exception e)
262                     {
263                         /* no-op, continue */
264                     }
265                 } // end for
266             } // end if rootPackages...
267         } // end if classname...
268 
269         // If we fell out here, try the defaultClassname
270         try
271         {
272             return Class.forName(defaultClassname);
273         }
274         catch (Exception e)
275         {
276             // You can't always get you what you want
277             return null;
278         }
279     }
280 
281 
282     /**
283      * Utility method to get a class name of a test.
284      * This is mainly a bit of syntactic sugar built on
285      * top of testClassForName.
286      *
287      * @param String classname FQCN or partially specified classname
288      * that you wish to load
289      * @param String[] rootPackages a list of packages to search
290      * for the classname specified in array order; if null then
291      * we don't search any additional packages
292      * @param String defaultClassname a default known-good FQCN to
293      * return if the classname was not found
294      *
295      * @return name of class that testClassForName returns;
296      * or null if an error occoured
297      */
testClassnameForName(String classname, String[] rootPackages, String defaultClassname)298     public static String testClassnameForName(String classname,
299                                          String[] rootPackages,
300                                          String defaultClassname)
301     {
302         Class clazz = testClassForName(classname, rootPackages, defaultClassname);
303         if (null == clazz)
304             return null;
305         else
306             return clazz.getName();
307     }
308 
309 
310     /**
311      * Utility method to create a unique runId.
312      *
313      * This is used to construct a theoretically unique Id for
314      * each run of a test script.  It is used later in some results
315      * analysis stylesheets to create comparative charts showing
316      * differences in results and timing data from one run of
317      * a test to another.
318      *
319      * Current format: MMddHHmm[;baseId]
320      * where baseId is not used if null.
321      *
322      * @param String Id base to start with
323      *
324      * @return String Id to use; will include a timestamp
325      */
createRunId(String baseId)326     public static String createRunId(String baseId)
327     {
328         java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat ("MMddHHmm");
329         if (null != baseId)
330             //return formatter.format(new java.util.Date())+ ";" + baseId;
331             return baseId + ":" + formatter.format(new java.util.Date());
332         else
333             return formatter.format(new java.util.Date());
334     }
335 
336 
337     /**
338      * Utility method to get info about the environment.
339      *
340      * This is a simple way to get a Hashtable about the current
341      * JVM's environment from either Xalan's EnvironmentCheck
342      * utility or from org.apache.env.Which.
343      *
344      * @return Hashtable with info about the environment
345      */
getEnvironmentHash()346     public static Hashtable getEnvironmentHash()
347     {
348         Hashtable hash = new Hashtable();
349         // Attempt to use Which, which will be better supported
350         Class clazz = testClassForName("org.apache.env.Which", null, null);
351 
352         try
353         {
354             if (null != clazz)
355             {
356                 // Call Which's method to fill hash
357                 final Class whichSignature[] =
358                         { Hashtable.class, String.class, String.class };
359                 Method which = clazz.getMethod("which", whichSignature);
360                 String projects = "";
361                 String options = "";
362                 Object whichArgs[] = { hash, projects, options };
363                 which.invoke(null, whichArgs);
364             }
365             else
366             {
367                 // Use Xalan's EnvironmentCheck
368                 clazz = testClassForName("org.apache.xalan.xslt.EnvironmentCheck", null, null);
369                 if (null != clazz)
370                 {
371                     Object envCheck = clazz.newInstance();
372                     final Class getSignature[] = { };
373                     Method getHash = clazz.getMethod("getEnvironmentHash", getSignature);
374 
375                     Object getArgs[] = { }; // empty
376                     hash = (Hashtable)getHash.invoke(envCheck, getArgs);
377                 }
378             }
379         }
380         catch (Throwable t)
381         {
382             hash.put("FATAL-ERROR", "QetestUtils.getEnvironmentHash no services available; " + t.toString());
383             t.printStackTrace();
384         }
385         return hash;
386     }
387 
388 
389     /**
390      * Main method to run from the command line - this acts
391      * as a cheap launching mechanisim for Xalan tests.
392      *
393      * Simply finds the class specified in the first argument,
394      * instantiates one, and passes it any remaining command
395      * line arguments we were given.
396      * The primary motivation here is to provide a simpler
397      * command line for inexperienced  users.  You can either
398      * pass the FQCN, or just the classname and it will still
399      * get run.  Note the one danger is the order of package
400      * lookups and the potential for the wrong class to run
401      * when we have identically named classes in different
402      * packages - but this will usually work!
403      *
404      * @param args command line argument array
405      */
main(String[] args)406     public static void main(String[] args)
407     {
408         if (args.length < 1)
409         {
410             System.err.println("QetestUtils.main() ERROR in usage: must have at least one arg: classname [options]");
411             return;
412         }
413 
414         // Get the class specified by the first arg...
415         Class clazz = QetestUtils.testClassForName(
416                 args[0], defaultPackages, null); // null = no default class
417         if (null == clazz)
418         {
419             System.err.println("QetestUtils.main() ERROR: Could not find class:" + args[0]);
420             return;
421         }
422 
423         try
424         {
425             // ...find the main() method...
426             Class[] parameterTypes = new Class[1];
427             parameterTypes[0] = java.lang.String[].class;
428             java.lang.reflect.Method main = clazz.getMethod("main", parameterTypes);
429 
430             // ...copy over our remaining cmdline args...
431             final String[] appArgs = new String[(args.length) == 1 ? 0 : args.length - 1];
432             if (args.length > 1)
433             {
434                 System.arraycopy(args, 1,
435                                  appArgs, 0,
436                                  args.length - 1);
437             }
438 
439             // ...and execute the method!
440             Object[] mainArgs = new Object[1];
441             mainArgs[0] = appArgs;
442             main.invoke(null, mainArgs);
443         }
444         catch (Throwable t)
445         {
446             System.err.println("QetestUtils.main() ERROR: running " + args[0]
447                     + ".main() threw: " + t.toString());
448             t.printStackTrace();
449         }
450     }
451 
452 
453     /**
454      * Default list of packages for xml-xalan tests.
455      * Technically this is Xalan-specific and should really be
456      * in some other directory, but I'm being lazy tonight.
457      * This looks for Xalan-related tests in the following
458      * packages in <b>this order</b>:
459      * <ul>
460      * <li>org.apache.qetest.xsl</li>
461      * <li>org.apache.qetest.xalanj2</li>
462      * <li>org.apache.qetest.trax</li>
463      * <li>org.apache.qetest.trax.dom</li>
464      * <li>org.apache.qetest.trax.sax</li>
465      * <li>org.apache.qetest.trax.stream</li>
466      * <li>org.apache.qetest.xslwrapper</li>
467      * <li>org.apache.qetest.xalanj1</li>
468      * <li>org.apache.qetest</li>
469      * <li>org.apache.qetest.qetesttest</li>
470      * </ul>
471      * Note the normal naming convention for automated tests
472      * is either *Test.java or *Testlet.java; although this is
473      * not required, it will make it easier to write simple
474      * test discovery mechanisims.
475      */
476     public static final String[] defaultPackages =
477     {
478         "org.apache.qetest.xsl",
479         "org.apache.qetest.xalanj2",
480         "org.apache.qetest.trax",
481         "org.apache.qetest.trax.dom",
482         "org.apache.qetest.trax.sax",
483         "org.apache.qetest.trax.stream",
484         "org.apache.qetest.xslwrapper",
485         "org.apache.qetest.dtm",
486         "org.apache.qetest.xalanj1",
487         "org.apache.qetest",
488         "org.apache.qetest.qetesttest"
489     };
490 
491 }
492