blob: f2cb641f0c7b296629871bd5abf9b4cec561c333 [file] [log] [blame]
/*
* 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$
*/
/*
*
* BugzillaTestletDriver.java
*
*/
package org.apache.qetest.xsl;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;
import org.apache.qetest.Datalet;
import org.apache.qetest.Logger;
import org.apache.qetest.QetestUtils;
import org.apache.qetest.Testlet;
//-------------------------------------------------------------------------
/**
* Test driver for Bugzilla tests with .java/.xsl files..
*
* This driver does not iterate over a directory tree; only
* over a single directory. It supports either 'classic' tests
* with matching .xsl/.xml/.out files like the conformance test,
* or tests that also include a .java file that is the specific
* testlet to execute for that test.
*
*
*
* @author shane_curcuru@lotus.com
* @version $Id$
*/
public class BugzillaTestletDriver extends StylesheetTestletDriver
{
/** Convenience constant: .java extension for Java Testlet source. */
public static final String JAVA_EXTENSION = ".java";
/** Convenience constant: Property key for java filenames. */
public static final String JAVA_SOURCE_NAME = "java.source.name";
/** Convenience constant: Default .xml file to use. */
public static final String DEFAULT_XML_FILE = "identity.xml";
/**
* Default FilenameFilter FQCN for files - overridden.
* By default, use a custom FilenameFilter that picks up
* both .java and .xsl files, with slightly different
* naming conventions than normal.
*/
protected String defaultFileFilter = "org.apache.qetest.xsl.BugzillaFileRules";
/** Just initialize test name, comment; numTestCases is not used. */
public BugzillaTestletDriver()
{
testName = "BugzillaTestletDriver";
testComment = "Test driver for Bugzilla tests with .java/.xsl files.";
}
/**
* Special: test all Bugzilla* files in just the bugzilla directory.
* This does not iterate down directories.
* This is a specific test driver for testlets that may have
* matching foo*.java and foo*.xml/xsl/out
* Parameters: none, uses our internal members inputDir,
* outputDir, testlet, etc.
*/
public void processInputDir()
{
// Ensure the inputDir is there - we must have a valid location for input files
File testDirectory = new File(inputDir);
if (!testDirectory.exists())
{
// Try a default inputDir
String oldInputDir = inputDir; // cache for potential error message
testDirectory = new File((inputDir = getDefaultInputDir()));
if (!testDirectory.exists())
{
// No inputDir, can't do any tests!
// @todo check if this is the best way to express this
reporter.checkErr("inputDir(" + oldInputDir
+ ", or " + inputDir + ") does not exist, aborting!");
return;
}
}
// Validate that each of the specified dirs exists
// Returns directory references like so:
// testDirectory = 0, outDirectory = 1, goldDirectory = 2
File[] dirs = validateDirs(new File[] { testDirectory },
new File[] { new File(outputDir), new File(goldDir) });
if (null == dirs) // this should never happen...
{
// No inputDir, can't do any tests!
// @todo check if this is the best way to express this
reporter.checkErr("inputDir(" + dirs[0] + ") does not exist, aborting!");
return;
}
// Call worker method to process the individual directory
// and get a list of .java or .xsl files to test
Vector files = getFilesFromDir(dirs[0], getFileFilter(), embedded);
// 'Transform' the list of individual test files into a
// list of Datalets with all fields filled in
//@todo should getFilesFromDir and buildDatalets be combined?
Vector datalets = buildDatalets(files, dirs[0], dirs[1], dirs[2]);
if ((null == datalets) || (0 == datalets.size()))
{
// No tests, log error and return
// other directories to test
reporter.checkErr("inputDir(" + dirs[0] + ") did not contain any tests, aborting!");
return;
}
// Now process the list of files found in this dir
processFileList(datalets, "Bugzilla tests of: " + dirs[0]);
}
/**
* Run a list of bugzilla-specific tests.
* Bugzilla tests may either be encoded as a .java file that
* defines a Testlet, or as a normal .xsl/.xml file pair that
* should simply be transformed simply, by a StylesheetTestlet.
*
* @param vector of Datalet objects to pass in
* @param desc String to use as testCase description
*/
public void processFileList(Vector datalets, String desc)
{
// Validate arguments
if ((null == datalets) || (0 == datalets.size()))
{
// Bad arguments, report it as an error
// Note: normally, this should never happen, since
// this class normally validates these arguments
// before calling us
reporter.checkErr("Testlet or datalets are null/blank, nothing to test!");
return;
}
// Now just go through the list and process each set
int numDatalets = datalets.size();
reporter.logInfoMsg("processFileList() with " + numDatalets
+ " potential Bugzillas");
// Iterate over every datalet and test it
for (int ctr = 0; ctr < numDatalets; ctr++)
{
try
{
// Depending on the Datalet class, run a different algorithim
Datalet d = (Datalet)datalets.elementAt(ctr);
if (d instanceof TraxDatalet)
{
// Assume we the datalet holds the name of a
// .java file that's a testlet, and just
// execute that itself
// Note: Since they're packageless and have
// hardcoded paths to the current dir, must
// change user.dir each time in worker method
Testlet t = getTestlet((TraxDatalet)d);
// Each Bugzilla is it's own testcase
reporter.testCaseInit(t.getDescription());
executeTestletInDir(t, d, inputDir);
}
else if (d instanceof StylesheetDatalet)
{
// Create plain Testlet to execute a test with this
// next datalet - the Testlet will log all info
// about the test, including calling check*()
// Each Bugzilla is it's own testcase
reporter.testCaseInit(d.getDescription());
getTestlet().execute(d);
}
else
{
reporter.checkErr("Unknown Datalet type: " + d);
}
}
catch (Throwable t)
{
// Log any exceptions as fails and keep going
//@todo improve the below to output more useful info
reporter.checkFail("Datalet num " + ctr + " threw: " + t.toString());
reporter.logThrowable(Logger.ERRORMSG, t, "Datalet threw");
}
reporter.testCaseClose();
} // of while...
}
/**
* Transform a vector of individual test names into a Vector
* of filled-in datalets to be tested - Bugzilla-specific.
*
* This does special processing since we may either have .java
* files that should be compiled, or we may have plain .xsl/.xml
* file pairs that we should simpy execute through a default
* StylesheetTestlet as-is.
* This basically just calculates local path\filenames across
* the three presumably-parallel directory trees of testLocation
* (inputDir), outLocation (outputDir) and goldLocation
* (forced to be same as inputDir). It then stuffs each of
* these values plus some generic info like our testProps
* into each datalet it creates.
*
* @param files Vector of local path\filenames to be tested
* @param testLocation File denoting directory where all
* .xml/.xsl tests are found
* @param outLocation File denoting directory where all
* output files should be put
* @param goldLocation File denoting directory where all
* gold files are found - IGNORED; forces testLocation instead
* @return Vector of StylesheetDatalets that are fully filled in,
* i.e. outputName, goldName, etc are filled in respectively
* to inputName
*/
public Vector buildDatalets(Vector files, File testLocation,
File outLocation, File goldLocation)
{
// Validate arguments
if ((null == files) || (files.size() < 1))
{
// Bad arguments, report it as an error
// Note: normally, this should never happen, since
// this class normally validates these arguments
// before calling us
reporter.logWarningMsg("buildDatalets null or empty file vector");
return null;
}
Vector v = new Vector(files.size());
int xslCtr = 0;
int javaCtr = 0;
// For every file in the vector, construct the matching
// out, gold, and xml/xsl files; plus see if we have
// a .java file as well
for (Enumeration elements = files.elements();
elements.hasMoreElements(); /* no increment portion */ )
{
String file = null;
try
{
file = (String)elements.nextElement();
}
catch (ClassCastException cce)
{
// Just skip this entry
reporter.logWarningMsg("Bad file element found, skipping: " + cce.toString());
continue;
}
Datalet d = null;
// If it's a .java file: just set java.source.name/java.class.name
if (file.endsWith(JAVA_EXTENSION))
{
// Use TraxDatalets if we have .java
d = new TraxDatalet();
((TraxDatalet)d).options = new Properties(testProps);
((TraxDatalet)d).options.put("java.source.dir", testLocation);
((TraxDatalet)d).options.put(JAVA_SOURCE_NAME, file);
((TraxDatalet)d).options.put("fileCheckerImpl", fileChecker);
// That's it - when we execute tests later on, if
// there's a JAVA_SOURCE_NAME we simply use that to
// find the testlet to execute
javaCtr++;
}
// If it's a .xsl file, just set the filenames as usual
else if (file.endsWith(XSL_EXTENSION))
{
// Use plain StylesheetDatalets if we just have .xsl
d = new StylesheetDatalet();
((StylesheetDatalet)d).inputName = testLocation.getPath() + File.separator + file;
String fileNameRoot = file.substring(0, file.indexOf(XSL_EXTENSION));
// Check for existence of xml - if not there, then set to some default
//@todo this would be a perfect use of TraxDatalet.setXMLString()
String xmlFileName = testLocation.getPath() + File.separator + fileNameRoot + XML_EXTENSION;
if ((new File(xmlFileName)).exists())
{
((StylesheetDatalet)d).xmlName = xmlFileName;
}
else
{
((StylesheetDatalet)d).xmlName = testLocation.getPath() + File.separator + DEFAULT_XML_FILE;
}
((StylesheetDatalet)d).outputName = outLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
((StylesheetDatalet)d).goldName = testLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
((StylesheetDatalet)d).flavor = flavor;
((StylesheetDatalet)d).options = new Properties(testProps);
((StylesheetDatalet)d).options.put("fileCheckerImpl", fileChecker);
// These tests will be run by a plain StylesheetTestlet
xslCtr++;
}
else
{
// Hmmm - I'm not sure what we should do here
reporter.logWarningMsg("Unexpected test file found, skipping: " + file);
continue;
}
d.setDescription(file);
v.addElement(d);
}
reporter.logTraceMsg("Bugzilla buildDatalets with " + javaCtr
+ " .java Testlets, and " + xslCtr + " .xsl files to test");
return v;
}
/**
* Execute a Testlet with a specific user.dir.
* Bugzilla testlets hardcode their input file names, assuming
* they're in the current directory. But this automation is
* frequently run in another directory, and uses the inputDir
* setting to point where the files are. Hence this worker
* method to change user.dir, execute the Testlet, and then
* switch back.
* Note: will not work in Applet context, obviously.
*
* @param t Testlet to execute
* @param dir to change user.dir to first
* @throws propagates any non-user.dir exceptions
*/
public void executeTestletInDir(Testlet t, Datalet d, String dir)
throws Exception
{
final String USER_DIR = "user.dir";
try
{
// Note: we must actually keep a cloned copy of the
// whole system properties block to replace later
// in case a Bugzilla testlet changes any other
// properties during it's execution
Properties p = System.getProperties();
Properties cacheProps = (Properties)p.clone();
// This should, I hope, properly get the correct path
// for what the inputDir would be, whether it's a
// relative or absolute path from where we are now
File f = new File(inputDir);
try
{
// Note the canonical form seems to be the most reliable for our purpose
p.put(USER_DIR, f.getCanonicalPath());
}
catch (IOException ioe)
{
p.put(USER_DIR, f.getAbsolutePath());
}
System.setProperties(p);
// Now just execute the Testlet from here
t.execute(d);
// Replace the system properties to be polite!
System.setProperties(cacheProps);
}
catch (SecurityException se)
{
reporter.logThrowable(Logger.ERRORMSG, se, "executeTestletInDir threw");
reporter.checkErr("executeTestletInDir threw :" + se
+ " cannot execute Testlet in correct dir " + dir);
}
}
/**
* Convenience method to get a Bugzilla Testlet to use.
* Take the TraxDatalet given and find the java classname
* from it. Then just load an instance of that Testlet class.
*
* @return Testlet for use in this test; null if error
*/
public Testlet getTestlet(TraxDatalet d)
{
try
{
// Calculate the java classname
String testletSourceName = (String)d.options.get(JAVA_SOURCE_NAME);
// Potential problem: what if the SourceName doesn't have .java at end?
String testletClassName = testletSourceName.substring(0, testletSourceName.indexOf(JAVA_EXTENSION));
//@todo should we attempt to compile to a .class file
// if we can't find the class here? This adds a bunch
// of complexity here; so I'm thinking it's better to
// simply require the user to 'build all' first
Class testletClazz = Class.forName(testletClassName);
// Create it and set our reporter into it
Testlet t = (Testlet)testletClazz.newInstance();
t.setLogger((Logger)reporter);
return (Testlet)t;
}
catch (Exception e)
{
// Ooops, none found, log an error
reporter.logThrowable(Logger.ERRORMSG, e, "getTestlet(d) threw");
reporter.checkErr("getTestlet(d) threw: " + e.toString());
return null;
}
}
/**
* Convenience method to get a default filter for files.
* Returns special file filter for our use.
*
* @return FilenameFilter using BugzillaFileRules(excludes).
*/
public FilenameFilter getFileFilter()
{
// Find a Testlet class to use
Class clazz = QetestUtils.testClassForName("org.apache.qetest.xsl.BugzillaFileRules",
QetestUtils.defaultPackages,
defaultFileFilter);
try
{
// Create it, optionally with a category
String excludes = testProps.getProperty(OPT_EXCLUDES);
if ((null != excludes) && (excludes.length() > 1)) // Arbitrary check for non-null, non-blank string
{
Class[] parameterTypes = { java.lang.String.class };
Constructor ctor = clazz.getConstructor(parameterTypes);
Object[] ctorArgs = { excludes };
return (FilenameFilter) ctor.newInstance(ctorArgs);
}
else
{
return (FilenameFilter)clazz.newInstance();
}
}
catch (Exception e)
{
// Ooops, none found!
return null;
}
}
/**
* Convenience method to get a default inputDir when none or
* a bad one was given.
* @return String pathname of default inputDir "tests\bugzilla".
*/
public String getDefaultInputDir()
{
return "tests" + File.separator + "bugzilla";
}
/**
* Convenience method to print out usage information - update if needed.
* @return String denoting usage of this test class
*/
public String usage()
{
return ("Additional options supported by BugzillaTestletDriver:\n"
+ " (Note: assumes inputDir=test/tests/bugzilla)"
+ " (Note: we do *not* support -embedded)"
+ super.usage()); // Grab our parent classes usage as well
}
/**
* Main method to run test from the command line - can be left alone.
* @param args command line argument array
*/
public static void main(String[] args)
{
BugzillaTestletDriver app = new BugzillaTestletDriver();
app.doMain(args);
}
}