/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Id$ */ package org.apache.qetest; import org.apache.test.android.AndroidFileUtils; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.util.Hashtable; /** * Static utility class for both general-purpose testing methods * and a few XML-specific methods. * Also provides a simplistic Test/Testlet launching helper * functionality. Simply execute this class from the command * line with a full or partial classname (in the org.apache.qetest * area, obviously) and we'll load and execute that class instead. * @author shane_curcuru@lotus.com * @version $Id$ */ public abstract class QetestUtils { // abstract class cannot be instantiated /** * Utility method to translate a String filename to URL. * * Note: This method is not necessarily proven to get the * correct URL for every possible kind of filename; it should * be improved. It handles the most common cases that we've * encountered when running Conformance tests on Xalan. * Also note, this method does not handle other non-file: * flavors of URLs at all. * * If the name is null, return null. * If the name starts with a common URI scheme (namely the ones * found in the examples of RFC2396), then simply return the * name as-is (the assumption is that it's already a URL) * Otherwise we attempt (cheaply) to convert to a file:/// URL. * * @param String local path\filename of a file * @return a file:/// URL, the same string if it appears to * already be a URL, or null if error */ public static String filenameToURL(String filename) { // null begets null - something like the commutative property if (null == filename) return null; // Don't translate a string that already looks like a URL if (isCommonURL(filename)) return filename; // Android-added: Look up the file in the java resources. String androidUrl = AndroidFileUtils.getInputFileUrlString(filename); if (androidUrl != null) { return androidUrl; } File f = new File(filename); String tmp = null; try { // This normally gives a better path tmp = f.getCanonicalPath(); } catch (IOException ioe) { // But this can be used as a backup, for cases // where the file does not exist, etc. tmp = f.getAbsolutePath(); } // URLs must explicitly use only forward slashes if (File.separatorChar == '\\') { tmp = tmp.replace('\\', '/'); } // Note the presumption that it's a file reference // Ensure we have the correct number of slashes at the // start: we always want 3 /// if it's absolute // (which we should have forced above) if (tmp.startsWith("/")) return "file://" + tmp; else return "file:///" + tmp; } /** * Utility method to find a relative path. * *
Attempt to find a relative path based from the current * directory (usually user.dir property).
* *If the name is null, return null. If the name starts * with a common URI scheme (namely the ones * found in the examples of RFC2396), then simply return * the name itself (future work could attempt to detect * file: protocols if needed).
* * @param String local path\filename of a file * @return a local path\file that is relative; if we can't * find one, we return the original name */ public static String filenameToRelative(String filename) { // null begets null - something like the commutative property if (null == filename) return null; // Don't translate a string that already looks like a URL if (isCommonURL(filename)) return filename; String base = null; try { File userdir = new File(System.getProperty("user.dir")); // Note: use CanonicalPath, since this ensures casing // will be identical between the two files base = userdir.getCanonicalPath(); } catch (Exception e) { // If we can't detect this, we can't determine // relativeness, so just return the name return filename; } File f = new File(filename); String tmp = null; try { tmp = f.getCanonicalPath(); } catch (IOException ioe) { tmp = f.getAbsolutePath(); } // If it's not relative to the base, just return as-is // (note: this may not be the answer you expect) if (!tmp.startsWith(base)) return tmp; // Strip off the base tmp = tmp.substring(base.length()); // Also strip off any beginning file separator, since we // don't want it to be mistaken for an absolute path if (tmp.startsWith(File.separator)) return tmp.substring(1); else return tmp; } /** * Worker method to detect common absolute URLs. * * @param s String path\filename or URL (or any, really) * @return true if s starts with a common URI scheme (namely * the ones found in the examples of RFC2396); false otherwise */ protected static boolean isCommonURL(String s) { if (null == s) return false; if (s.startsWith("file:") || s.startsWith("http:") || s.startsWith("ftp:") || s.startsWith("gopher:") || s.startsWith("mailto:") || s.startsWith("news:") || s.startsWith("telnet:") ) return true; else return false; } /** * Utility method to get a testing Class object. * This is mainly a bit of syntactic sugar to allow users * to specify only the end parts of a package.classname * and still have it loaded. It basically does a * Class.forName() search, starting with the provided * classname, and if not found, searching through a list * of root packages to try to find the class. * * Note the inherent danger when there are same-named * classes in different packages, where the behavior will * depend on the order of searchPackages. * * Commonly called like: *testClassForName("PerformanceTestlet",
* new String[] {"org.apache.qetest", "org.apache.qetest.xsl" },
* "org.apache.qetest.StylesheetTestlet");
*
* @param String classname FQCN or partially specified classname
* that you wish to load
* @param String[] rootPackages a list of packages to search
* for the classname specified in array order; if null then
* we don't search any additional packages
* @param String defaultClassname a default known-good FQCN to
* return if the classname was not found
*
* @return Class object asked for if one found by combining
* clazz with one of the rootPackages; if none, a Class of
* defaultClassname; or null if an error occoured
*/
public static Class testClassForName(String classname,
String[] rootPackages,
String defaultClassname)
{
// Ensure we have a valid classname, and try it
if ((null != classname) && (classname.length() > 0))
{
// Try just the specified classname, in case it's a FQCN
try
{
return Class.forName(classname);
}
catch (Exception e)
{
/* no-op, fall through */
}
// Now combine each of the rootPackages with the classname
// and see if one of them gets loaded
if (null != rootPackages)
{
for (int i = 0; i < rootPackages.length; i++)
{
try
{
return Class.forName(rootPackages[i] + "." + classname);
}
catch (Exception e)
{
/* no-op, continue */
}
} // end for
} // end if rootPackages...
} // end if classname...
// If we fell out here, try the defaultClassname
try
{
return Class.forName(defaultClassname);
}
catch (Exception e)
{
// You can't always get you what you want
return null;
}
}
/**
* Utility method to get a class name of a test.
* This is mainly a bit of syntactic sugar built on
* top of testClassForName.
*
* @param String classname FQCN or partially specified classname
* that you wish to load
* @param String[] rootPackages a list of packages to search
* for the classname specified in array order; if null then
* we don't search any additional packages
* @param String defaultClassname a default known-good FQCN to
* return if the classname was not found
*
* @return name of class that testClassForName returns;
* or null if an error occoured
*/
public static String testClassnameForName(String classname,
String[] rootPackages,
String defaultClassname)
{
Class clazz = testClassForName(classname, rootPackages, defaultClassname);
if (null == clazz)
return null;
else
return clazz.getName();
}
/**
* Utility method to create a unique runId.
*
* This is used to construct a theoretically unique Id for
* each run of a test script. It is used later in some results
* analysis stylesheets to create comparative charts showing
* differences in results and timing data from one run of
* a test to another.
*
* Current format: MMddHHmm[;baseId]
* where baseId is not used if null.
*
* @param String Id base to start with
*
* @return String Id to use; will include a timestamp
*/
public static String createRunId(String baseId)
{
java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat ("MMddHHmm");
if (null != baseId)
//return formatter.format(new java.util.Date())+ ";" + baseId;
return baseId + ":" + formatter.format(new java.util.Date());
else
return formatter.format(new java.util.Date());
}
/**
* Utility method to get info about the environment.
*
* This is a simple way to get a Hashtable about the current
* JVM's environment from either Xalan's EnvironmentCheck
* utility or from org.apache.env.Which.
*
* @return Hashtable with info about the environment
*/
public static Hashtable getEnvironmentHash()
{
Hashtable hash = new Hashtable();
// Attempt to use Which, which will be better supported
Class clazz = testClassForName("org.apache.env.Which", null, null);
try
{
if (null != clazz)
{
// Call Which's method to fill hash
final Class whichSignature[] =
{ Hashtable.class, String.class, String.class };
Method which = clazz.getMethod("which", whichSignature);
String projects = "";
String options = "";
Object whichArgs[] = { hash, projects, options };
which.invoke(null, whichArgs);
}
else
{
// Use Xalan's EnvironmentCheck
clazz = testClassForName("org.apache.xalan.xslt.EnvironmentCheck", null, null);
if (null != clazz)
{
Object envCheck = clazz.newInstance();
final Class getSignature[] = { };
Method getHash = clazz.getMethod("getEnvironmentHash", getSignature);
Object getArgs[] = { }; // empty
hash = (Hashtable)getHash.invoke(envCheck, getArgs);
}
}
}
catch (Throwable t)
{
hash.put("FATAL-ERROR", "QetestUtils.getEnvironmentHash no services available; " + t.toString());
t.printStackTrace();
}
return hash;
}
/**
* Main method to run from the command line - this acts
* as a cheap launching mechanisim for Xalan tests.
*
* Simply finds the class specified in the first argument,
* instantiates one, and passes it any remaining command
* line arguments we were given.
* The primary motivation here is to provide a simpler
* command line for inexperienced users. You can either
* pass the FQCN, or just the classname and it will still
* get run. Note the one danger is the order of package
* lookups and the potential for the wrong class to run
* when we have identically named classes in different
* packages - but this will usually work!
*
* @param args command line argument array
*/
public static void main(String[] args)
{
if (args.length < 1)
{
System.err.println("QetestUtils.main() ERROR in usage: must have at least one arg: classname [options]");
return;
}
// Get the class specified by the first arg...
Class clazz = QetestUtils.testClassForName(
args[0], defaultPackages, null); // null = no default class
if (null == clazz)
{
System.err.println("QetestUtils.main() ERROR: Could not find class:" + args[0]);
return;
}
try
{
// ...find the main() method...
Class[] parameterTypes = new Class[1];
parameterTypes[0] = java.lang.String[].class;
java.lang.reflect.Method main = clazz.getMethod("main", parameterTypes);
// ...copy over our remaining cmdline args...
final String[] appArgs = new String[(args.length) == 1 ? 0 : args.length - 1];
if (args.length > 1)
{
System.arraycopy(args, 1,
appArgs, 0,
args.length - 1);
}
// ...and execute the method!
Object[] mainArgs = new Object[1];
mainArgs[0] = appArgs;
main.invoke(null, mainArgs);
}
catch (Throwable t)
{
System.err.println("QetestUtils.main() ERROR: running " + args[0]
+ ".main() threw: " + t.toString());
t.printStackTrace();
}
}
/**
* Default list of packages for xml-xalan tests.
* Technically this is Xalan-specific and should really be
* in some other directory, but I'm being lazy tonight.
* This looks for Xalan-related tests in the following
* packages in this order:
*