curcuru 01/06/15 10:48:04
Added: test/java/src/org/apache/qetest/xsl
ThreadedStylesheetDatalet.java
ThreadedStylesheetTestlet.java
ThreadedTestletDriver.java
Log:
New Threaded testing model based on Testlet/StylesheetTestletDriver
Revision Changes Path
1.1
xml-xalan/test/java/src/org/apache/qetest/xsl/ThreadedStylesheetDatalet.java
Index: ThreadedStylesheetDatalet.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
*
* ThreadedStylesheetDatalet.java
*
*/
package org.apache.qetest.xsl;
import org.apache.qetest.Datalet;
import org.apache.qetest.xslwrapper.TransformWrapper;
import org.apache.qetest.xslwrapper.TransformWrapperFactory;
import javax.xml.transform.Templates;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
/**
* Datalet for conformance testing of xsl stylesheet files.
* Used specifically in multi-threaded tests. Allows a user
* to specify both a normal set of inputs (inputName, xmlName,
* etc.) as well as a second set - usually a Transformer
* object (which is threadsafe) and an xmlName2 to use with it.
*
* //@todo see if we should subclass StylesheetDatalet or not?
* @author [EMAIL PROTECTED]
* @version $Id: ThreadedStylesheetDatalet.java,v 1.1 2001/06/15 17:48:03
curcuru Exp $
*/
public class ThreadedStylesheetDatalet implements Datalet
{
//// Items associated with the normal test
/** URL of the stylesheet; default:.../identity.xsl. */
public String inputName = "tests/api/trax/identity.xsl";
/** URL of the xml document; default:.../identity.xml. */
public String xmlName = "tests/api/trax/identity.xml";
/** URL to put output into; default:ThreadedStylesheetDatalet.out. */
public String outputName = "ThreadedStylesheetDatalet.out";
/** URL of the a gold file or data; default:.../identity.out. */
public String goldName = "tests/api-gold/trax/identity.out";
/**
* Templates object to use for second transform; default: NONE
* The Templates object is wrapped inside a TransformWrapper
* since that allows us to use a shared TransformWrapper that
* encapsulates a flavor and performance measurements as well.
* Note: this must be set by the user, otherwise it will
* be ignored. The inputName parameter is still provided so
* users can supply optional systemId of the stylesheet.
*/
public TransformWrapper transformWrapper = null;
/** Number of times to loop for each thread; default: 10. */
public int iterations = 10;
/**
* If we should force any local path\filenames to URLs.
* Note: This is not really the best place for this, but
* since it works with Xerces and Crimson and Xalan, it's
* good enough for now.
* Not currently settable by user; default:true
*/
public boolean useURL = true;
/**
* Generic placeholder for any additional options.
* I'm still undecided if I like this idea or not.
* This allows ThreadedStylesheetDatalets to support additional kinds
* of tests, like performance tests, without having to change
* this data model. These options can serve as a catch-all
* for any new properties or options or what-not that new
* tests need, without having to change how the most basic
* member variables here work.
* Note that while this needs to be a Properties object to
* take advantage of the parent/default behavior in
* getProperty(), this doesn't necessarily mean they can only
* store Strings.
*/
public Properties options = new Properties();
/** Description of what this Datalet tests. */
protected String description = "ThreadedStylesheetDatalet: String
inputName, String xmlName, String outputName, String goldName, String flavor;
plus second Templates object";
/**
* No argument constructor is a no-op.
*/
public ThreadedStylesheetDatalet() { /* no-op */ }
/**
* Initialize this datalet from a string, perhaps from
* a command line.
* We will parse the command line with whitespace and fill
* in our member variables in order:
* <pre>inputName, xmlName, outputName, goldName, flavor</pre>,
* if there are too few tokens, remaining variables will default.
*/
public ThreadedStylesheetDatalet(String args)
{
load(args);
}
/**
* Accesor method for a brief description of this Datalet.
*
* @return String describing the specific set of data
* this Datalet contains (can often be used as the description
* of any check() calls made from the Testlet).
*/
public String getDescription()
{
return description;
}
/**
* Accesor method for a brief description of this Datalet.
*
* @param s description to use for this Datalet.
*/
public void setDescription(String s)
{
description = s;
}
/**
* Load fields of this Datalet from a Hashtable.
* Caller must provide data for all of our fields.
* Note: this call also fills in info about the second
* Templates, etc. object as well.
* //@todo design decision: only have load(Hashtable)
* or load(Properties), not both.
*
* @param Hashtable to load
*/
public void load(Hashtable h)
{
if (null == h)
return; //@todo should this have a return val or exception?
inputName = (String)h.get("inputName");
xmlName = (String)h.get("xmlName");
outputName = (String)h.get("outputName");
goldName = (String)h.get("goldName");
transformWrapper = (TransformWrapper)h.get("transformWrapper");
}
/**
* Load fields of this Datalet from a Properties.
* Caller must provide data for all of our fields.
* Note: this call also fills in info about the second
* Templates, etc. object as well.
* //@todo design decision: only have load(Hashtable)
* or load(Properties), not both.
*
* @param Hashtable to load
*/
public void load(Properties p)
{
if (null == p)
return; //@todo should this have a return val or exception?
inputName = (String)p.getProperty("inputName");
xmlName = (String)p.getProperty("xmlName");
outputName = (String)p.getProperty("outputName");
goldName = (String)p.getProperty("goldName");
// Also set our internal options to default to this Properties
options = new Properties(p);
// Also set our second set of templates
transformWrapper = (TransformWrapper)p.get("transformWrapper");
}
/**
* Load fields of this Datalet from a String.
* NOT IMPLEMENTED! No easy way to load the Templates from string.
*
* @param s String to load
*/
public void load(String s)
{
throw new RuntimeException("ThreadedStylesheetDatalet.load(String)
not implemented!");
}
/**
* Load fields of this Datalet from a String[].
* NOT IMPLEMENTED! No easy way to load the Templates from string.
*
* @param s String array to load
*/
public void load(String[] s)
{
throw new RuntimeException("ThreadedStylesheetDatalet.load(String[])
not implemented!");
}
} // end of class ThreadedStylesheetDatalet
1.1
xml-xalan/test/java/src/org/apache/qetest/xsl/ThreadedStylesheetTestlet.java
Index: ThreadedStylesheetTestlet.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
*
* ThreadedStylesheetTestlet.java
*
*/
package org.apache.qetest.xsl;
import org.apache.qetest.CheckService;
import org.apache.qetest.Datalet;
import org.apache.qetest.Logger;
import org.apache.qetest.QetestUtils;
import org.apache.qetest.TestletImpl;
import org.apache.qetest.xslwrapper.TransformWrapper;
import org.apache.qetest.xslwrapper.TransformWrapperFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Hashtable;
/**
* Testlet for basic thread testing of xsl stylesheet files.
*
* This class provides a simple testing algorithim for verifying
* that Xalan functions properly when run on multiple threads
* simultaneously. Currently it simply does most of what the
* normal StylesheetTestlet does, except it does it in the run()
* method on a thread - thus you'd commonly use this with
* ThreadedTestletDriver to start multiple of these testlets
* up at the same time.
* We implement Runnable, so classically a test driver would
* create us then pass us to new Thread(us).start(), instead
* of calling execute(). @todo find a better way to integrate!
*
* @author [EMAIL PROTECTED]
* @version $Id: ThreadedStylesheetTestlet.java,v 1.1 2001/06/15 17:48:03
curcuru Exp $
*/
public class ThreadedStylesheetTestlet
extends TestletImpl
implements Runnable
{
// Initialize our classname for TestletImpl's main() method
static { thisClassName =
"org.apache.qetest.xsl.ThreadedStylesheetTestlet"; }
// Initialize our defaultDatalet
{ defaultDatalet = (Datalet)new StylesheetDatalet(); }
/** Accessor for our Datalet instead of calling execute(). */
public void setDefaultDatalet(StylesheetDatalet d)
{
defaultDatalet = d;
}
/* Special ThreadedStylesheetDatalet with a Templates. */
public ThreadedStylesheetDatalet sharedDatalet = new
ThreadedStylesheetDatalet();
/* Description of our current state; changes during our lifecycle. */
protected String description = "ThreadedStylesheetTestlet - before
execute()";
/**
* Accesor method for a brief description of this test.
* When created, this returns a description of this testlet.
* After you've called execute(), this returns a brief
* description of our current result or status.
*
* @return String describing what this ThreadedStylesheetTestlet does.
* @see #getResult()
*/
public String getDescription()
{
return description;
}
/**
* Accesor method for a brief description of this test.
* Automatically adds our identifier at the start.
*
* @param d String to set as our current description.
*/
protected void setDescription(String d)
{
description = "[" + threadIdentifier + "]" + d;
}
/* Our 'final' test result; actually changes during our lifecycle. */
protected int result = Logger.DEFAULT_RESULT;
/**
* Accesor method for the final result of this test.
* Note: this starts as INCP_RESULT, and given that we're
* threaded, may end up as INCP_RESULT and you may not know
* the difference. Could use more thought.
*
* @return int one of of Logger.*_RESULT.
*/
public int getResult()
{
return result;
}
/* Cheap-o counter: so driver can differentiate each thread. */
public int threadIdentifier = 0;
/**
* Run this ThreadedStylesheetTestlet: start this test as
* a thread and return immediately.
* Note that you must join() this thread later if you want
* to wait until we're done.
* //@todo improve docs on how to communicate between threads
*
* @param Datalet to use as data point for the test.
*/
public void execute(Datalet d)
{
StylesheetDatalet datalet = null;
try
{
datalet = (StylesheetDatalet)d;
}
catch (ClassCastException e)
{
logger.checkErr("Datalet provided is not a StylesheetDatalet;
cannot continue with " + d);
return;
}
logger.logMsg(Logger.STATUSMSG, "About to test: "
+ (null == datalet.inputName
? datalet.xmlName
: datalet.inputName)
+ " plus " + sharedDatalet.xmlName);
// All the rest of the test is executed in our thread.
//if (true) //@todo check defaultDatalet.options...
// this.start();
//@todo Um, how do we do this? Or do we just ask caller to do it?
logger.logMsg(Logger.CRITICALMSG, "//@todo execute() is not yet
implemented - you must start our thread yourself");
// Return to caller; they must join() us later if they
// want to know when we're actually complete
return;
}
/**
* Called by execute() to perform the looping and actual test.
* Note: You must have set our defaultDatalet first!
*/
public void run()
{
// Relies on defaultDatalet being set!
logger.logMsg(Logger.STATUSMSG, "Beginning thread shared output into:
"
+ ((StylesheetDatalet)defaultDatalet).outputName);
// Also set our description so outside users know what
// point in our Thread lifetime we're at
setDescription("ThreadedStylesheetTestlet.run() just started...");
StylesheetDatalet datalet = null;
try
{
datalet = (StylesheetDatalet)defaultDatalet;
}
catch (ClassCastException e)
{
setDescription("Datalet provided is not a StylesheetDatalet;
cannot continue with " + datalet);
logger.checkErr(description);
return;
}
//@todo validate our Datalet - ensure it has valid
// and/or existing files available.
// Cleanup outName(s) only if asked to - delete the file on disk
// Optimization: this takes extra time and often is not
// needed, so only do this if the option is set
if
("true".equalsIgnoreCase(datalet.options.getProperty("deleteOutFile")))
{
try
{
boolean btmp = (new File(datalet.outputName)).delete();
logger.logMsg(Logger.TRACEMSG, "Deleting OutFile of::" +
datalet.outputName
+ " status: " + btmp);
}
catch (SecurityException se)
{
logger.logMsg(Logger.WARNINGMSG, "Deleting OutFile of::" +
datalet.outputName
+ " threw: " + se.toString());
// But continue anyways...
}
//@todo make sure all sharedDatalets use different
// output files! No sense in having them use
// the same file all the time in all threads!
}
// Ask our independent datalet how many iterations to use
int iterations = sharedDatalet.iterations; // default value
try
{
iterations =
Integer.parseInt(datalet.options.getProperty("iterations"));
}
catch (NumberFormatException numEx)
{
// no-op; leave as default
}
// Now loop a number of times as specified, and for each
// loop, use the presupplied Templates object, then run
// one independent process, plus validate on first & last
setDescription("...about to iterate... " + datalet.outputName);
for (int ctr = 1; (ctr <= iterations); ctr++)
{
// Only validate on first and last iteration
boolean doValidation = ((1 == ctr) || (sharedDatalet.iterations
== ctr));
logger.logMsg(Logger.TRACEMSG, "About to do iteration " + ctr);
// Note: logic moved to worker methods for clarity;
// these methods just use our local vars and datalet
//@todo Note: while I've tried to mirror as much
// structure from StylesheetTestlet as possible,
// this has made this class very inefficent (note
// we iterate, and within each method do the same
// name munging and some basic setup in each worker
// method every time!)
// Long-term, we should just redesign this to be
// a custom class with it's own algorithim
processExistingTemplates(sharedDatalet, doValidation);
processNewStylesheet(datalet, doValidation);
setDescription("...done iteration # " + ctr);
}
// That's it! We're done!
logger.logMsg(Logger.STATUSMSG, "Completed thread with: " +
datalet.getDescription());
setDescription("All iterations complete! " + datalet.outputName);
// Also set our result, now that we think we're done
try
{
result =
((org.apache.qetest.Reporter)logger).getCurrentCaseResult();
}
catch (ClassCastException cce)
{
// Basically a no-op; just log out for info
logger.logMsg(Logger.WARNINGMSG, "logger is not a Reporter;
overall result may be incorrect!");
}
}
/**
* Worker method to process premade Templates object.
* Uses various local variables.
* //@todo Should we simply propagate exceptions instead of just logging
them?
*/
private void processExistingTemplates(ThreadedStylesheetDatalet datalet,
boolean doValidation)
{
// First: use our (presumably) shared Templates object to
// perform a Transformation - using the existing
// TransformWrapper object that already has built a stylesheet
if (!datalet.transformWrapper.isStylesheetReady())
{
// Can't continue if the Templates is not ready
logger.logMsg(Logger.WARNINGMSG, "datalet shared Templates
isStylesheetReady false!");
// Anything else we should log out here? In case someone
// care about this, don't have it fail
return;
}
// Since the wrapper's ready (and flavor, etc. setup) then
// just go ahead and ask it to transform
try
{
// Store local copies of XSL, XML references for
// potential change to URLs
String xmlName = datalet.xmlName;
if (datalet.useURL)
{
xmlName = QetestUtils.filenameToURL(xmlName);
}
//@todo Should we log a custom logElement here instead?
logger.logMsg(Logger.TRACEMSG, "About to test shared Templates: "
+ " xmlName=" + xmlName
+ " outputName=" + datalet.outputName + "-" +
threadIdentifier
+ " goldName=" + datalet.goldName);
// Simply have the wrapper do all the transforming
// or processing for us - we handle either normal .xsl
// stylesheet tests or just .xml embedded tests
long retVal = 0L;
// Here, we only use the existing Templates to do the transform
long[] times =
datalet.transformWrapper.transformWithStylesheet(xmlName, datalet.outputName +
"-" + threadIdentifier);
retVal = times[TransformWrapper.IDX_OVERALL];
if (!doValidation)
{
logger.logMsg(Logger.TRACEMSG, "Skipping validation of
outputName=" + datalet.outputName + "-" + threadIdentifier);
// Only bother to validate the output if asked
return;
}
// If we get here, attempt to validate the contents of
// the last outputFile created - only first and last time
through loop!
CheckService fileChecker =
(CheckService)datalet.options.get("fileCheckerImpl");
// Supply default value
if (null == fileChecker)
fileChecker = new XHTFileCheckService();
if (Logger.PASS_RESULT
!= fileChecker.check(logger,
new File(datalet.outputName + "-" +
threadIdentifier),
new File(datalet.goldName),
"Shared Templates of: " +
datalet.getDescription())
)
{
// Log a custom element with all the file refs first
// Closely related to viewResults.xsl select='fileref"
//@todo check that these links are valid when base
// paths are either relative or absolute!
Hashtable attrs = new Hashtable();
attrs.put("inputName", "Shared Templates");
attrs.put("xmlName", datalet.xmlName);
attrs.put("outputName", datalet.outputName + "-" +
threadIdentifier);
attrs.put("goldName", datalet.goldName);
logger.logElement(Logger.STATUSMSG, "fileref", attrs,
"Conformance test file references2");
// Then log the failure reason
logger.logArbitrary(Logger.STATUSMSG, (new
File(datalet.inputName)).getName()
+ " failure reason: " +
fileChecker.getExtendedInfo());
// Also force our result to fail (Note: we should really
// use a Reporter to track our status, since this might
// just be AMBG or ERRR instead of FAIL)
result = Logger.FAIL_RESULT;
}
}
// Note that this class can only validate positive test
// cases - we don't handle ExpectedExceptions
catch (Throwable t)
{
// Put the logThrowable first, so it appears before
// the Fail record, and gets color-coded
logger.logThrowable(Logger.ERRORMSG, t, "Shared Templates of: " +
datalet.getDescription());
logger.checkFail("Shared Templates of: " +
datalet.getDescription()
+ " threw: " + t.toString());
}
}
/**
* Worker method to process a new stylesheet/xml document.
* Uses various local variables.
* Note this is essentially a copy of StylesheetTestlet.execute().
* //@todo Should we simply propagate exceptions instead of just logging
them?
*/
private void processNewStylesheet(StylesheetDatalet datalet, boolean
doValidation)
{
// Test our supplied input file, and compare with gold
try
{
// Store local copies of XSL, XML references for
// potential change to URLs
String inputName = datalet.inputName;
String xmlName = datalet.xmlName;
if (datalet.useURL)
{
// inputName may not exist if it's an embedded test
if (null != inputName)
inputName = QetestUtils.filenameToURL(inputName);
xmlName = QetestUtils.filenameToURL(xmlName);
}
//@todo Should we log a custom logElement here instead?
logger.logMsg(Logger.TRACEMSG, "About to test: inputName=" +
inputName
+ " xmlName=" + xmlName + " outputName=" +
datalet.outputName + "-" + threadIdentifier
+ " goldName=" + datalet.goldName + " flavor=" +
datalet.flavor);
// Create a new TransformWrapper of appropriate flavor
// null arg is unused liaison for TransformWrapper
TransformWrapper transformWrapper = null;
try
{
transformWrapper =
TransformWrapperFactory.newWrapper(datalet.flavor);
transformWrapper.newProcessor(null);
}
catch (Throwable t)
{
logger.logThrowable(Logger.ERRORMSG, t, getDescription() + "
newWrapper/newProcessor threw");
logger.checkErr(getDescription() + " newWrapper/newProcessor
threw: " + t.toString());
return;
}
// Simply have the wrapper do all the transforming
// or processing for us - we handle either normal .xsl
// stylesheet tests or just .xml embedded tests
long retVal = 0L;
if (null == datalet.inputName)
{
// presume it's an embedded test
long [] times = transformWrapper.transformEmbedded(xmlName,
datalet.outputName + "-" + threadIdentifier);
retVal = times[TransformWrapper.IDX_OVERALL];
}
else
{
// presume it's a normal stylesheet test
long[] times = transformWrapper.transform(xmlName, inputName,
datalet.outputName + "-" + threadIdentifier);
retVal = times[TransformWrapper.IDX_OVERALL];
}
if (!doValidation)
{
logger.logMsg(Logger.TRACEMSG, "Skipping validation of
outputName=" + datalet.outputName + "-" + threadIdentifier);
// Only bother to validate the output if asked
return;
}
// If we get here, attempt to validate the contents of
// the last outputFile created
CheckService fileChecker =
(CheckService)datalet.options.get("fileCheckerImpl");
// Supply default value
if (null == fileChecker)
fileChecker = new XHTFileCheckService();
if (Logger.PASS_RESULT
!= fileChecker.check(logger,
new File(datalet.outputName + "-" +
threadIdentifier),
new File(datalet.goldName),
getDescription() + " " +
datalet.getDescription())
)
{
// Log a custom element with all the file refs first
// Closely related to viewResults.xsl select='fileref"
//@todo check that these links are valid when base
// paths are either relative or absolute!
Hashtable attrs = new Hashtable();
attrs.put("idref", (new File(datalet.inputName)).getName());
attrs.put("inputName", datalet.inputName);
attrs.put("xmlName", datalet.xmlName);
attrs.put("outputName", datalet.outputName + "-" +
threadIdentifier);
attrs.put("goldName", datalet.goldName);
logger.logElement(Logger.STATUSMSG, "fileref", attrs,
"Conformance test file references");
// Then log the failure reason
logger.logArbitrary(Logger.STATUSMSG, (new
File(datalet.inputName)).getName()
+ " failure reason: " +
fileChecker.getExtendedInfo());
// Also force our result to fail (Note: we should really
// use a Reporter to track our status, since this might
// just be AMBG or ERRR instead of FAIL)
result = Logger.FAIL_RESULT;
}
}
// Note that this class can only validate positive test
// cases - we don't handle ExpectedExceptions
catch (Throwable t)
{
// Put the logThrowable first, so it appears before
// the Fail record, and gets color-coded
logger.logThrowable(Logger.ERRORMSG, t, "New stylesheet of: " +
datalet.getDescription());
logger.checkFail("New stylesheet of: " + datalet.getDescription()
+ " threw: " + t.toString());
}
}
} // end of class ThreadedStylesheetTestlet
1.1
xml-xalan/test/java/src/org/apache/qetest/xsl/ThreadedTestletDriver.java
Index: ThreadedTestletDriver.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
*
* ThreadedTestletDriver.java
*
*/
package org.apache.qetest.xsl;
// Support for test reporting and harness classes
import org.apache.qetest.*;
import org.apache.qetest.xslwrapper.TransformWrapper;
import org.apache.qetest.xslwrapper.TransformWrapperFactory;
// java classes
import java.io.BufferedReader;
import java.io.File;
import java.io.FilenameFilter;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
//-------------------------------------------------------------------------
/**
* Test driver for XSLT stylesheet Testlets.
*
* This is a specific driver for XSLT-oriented Testlets, testing
* them in an explicitly threaded model. Currently, this class is
* tightly bound to ThreadedStylesheetTestlet/Datalet.
*
* @author [EMAIL PROTECTED]
* @version $Id: ThreadedTestletDriver.java,v 1.1 2001/06/15 17:48:04 curcuru
Exp $
*/
public class ThreadedTestletDriver extends StylesheetTestletDriver
{
/** Just initialize test name, comment; numTestCases is not used. */
public ThreadedTestletDriver()
{
testName = "ThreadedTestletDriver";
testComment = "Threaded test driver for XSLT stylesheet Testlets";
}
/**
* Do the default: test all stylesheets found in subdirs
* of our inputDir, using FilenameFilters for dirs and files.
* This only goes down one level in the tree, eg:
* <ul>inputDir = tests/conf
* <li>tests/conf - not tested</li>
* <li>tests/conf/boolean - test all boolean*.xsl files</li>
* <li>tests/conf/copy - test all copy*.xsl files</li>
* <li>tests/conf/copy/foo - not tested</li>
* <li>tests/conf/xmanual - not tested, since default
* ConformanceDirRules excludes dirs starting with 'x|X'</li>
* <li>tests/whitespace - test all whitespace*.xsl files</li>
* <li>etc.</li>
* </ul>
* Parameters: none, uses our internal members inputDir,
* outputDir, testlet, etc.
*/
public void processInputDir()
{
// Implement this later!
throw new RuntimeException("processInputDir not yet implemented use
-fileList list.txt instead!");
}
/**
* Run a list of stylesheet tests through a Testlet.
* The file names are assumed to be fully specified, and we assume
* the corresponding directories exist.
* Each fileList is turned into a testcase.
* The first file in the list is used as a common Template that
* is passed to each ThreadedStylesheetTestlet that is created
* from every other item in the file list.
* i.e. the first file is used commonly throughout every
* testlet. For every other file in the list, a new Thread is
* created that will perform processes on both the shared
* Templates from the first file and from this listed file.
*
* @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 - must have at least two files to test
if ((null == datalets) || (datalets.size() < 2))
{
// 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/less than 2,
nothing to test!");
return;
}
// Put everything else into a testCase
// This is not necessary, but feels a lot nicer to
// break up large test sets
reporter.testCaseInit(desc);
// Now just go through the list and process each set
int numDatalets = datalets.size();
reporter.logInfoMsg("processFileList() with " + numDatalets
+ " potential tests");
// Take the first Datalet and create a Templates from it;
// this is going to be shared by all other threads/testlets
StylesheetDatalet firstDatalet =
(StylesheetDatalet)datalets.elementAt(0);
TransformWrapper transformWrapper = null;
// Create a TransformWrapper of appropriate flavor
try
{
transformWrapper =
TransformWrapperFactory.newWrapper(firstDatalet.flavor);
transformWrapper.newProcessor(null);
reporter.logMsg(Logger.INFOMSG, "Created transformWrapper, about
to process shared: " + firstDatalet.inputName);
//@todo should key off of useURL/useURI
transformWrapper.buildStylesheet(QetestUtils.filenameToURL(firstDatalet.inputName));
}
catch (Throwable t)
{
reporter.logThrowable(Logger.ERRORMSG, t, "Creating
transformWrapper: newWrapper/newProcessor threw");
reporter.checkErr("Creating transformWrapper:
newWrapper/newProcessor threw: " + t.toString());
return;
}
// Create a ThreadedStylesheetDatalet for shared use
ThreadedStylesheetDatalet sharedDatalet = new
ThreadedStylesheetDatalet();
// Copy all other info over
sharedDatalet.inputName = firstDatalet.inputName;
sharedDatalet.outputName = firstDatalet.outputName;
sharedDatalet.goldName = firstDatalet.goldName;
sharedDatalet.transformWrapper = transformWrapper;
sharedDatalet.setDescription(firstDatalet.getDescription());
// Prepare array to store all datalets for later joining
// Note: store all but first datalet, which is used as
// the common shared stylesheet/Templates
ThreadedTestletInfo[] testletThreads = new
ThreadedTestletInfo[numDatalets - 1];
// Iterate over every OTHER datalet and test it
for (int ctr = 1; ctr < numDatalets; ctr++)
{
try
{
// Create ThreadedStylesheetDatalets from the common
// one we already have and each of the normal
// StylesheetDatalets that we were handed
ThreadedStylesheetTestlet testlet = getTestlet(ctr);
testlet.sharedDatalet = sharedDatalet;
testlet.setDefaultDatalet((StylesheetDatalet)datalets.elementAt(ctr));
testlet.threadIdentifier = ctr;
// Save a copy of each datalet for later joining
// Note off-by-one necessary for arrays
testletThreads[ctr - 1] = new ThreadedTestletInfo(testlet,
new Thread(testlet));
//@todo (optional) start this testlet - should allow
// user to start sequentially or all at once later
((testletThreads[ctr - 1]).thread).start();
reporter.logMsg(Logger.INFOMSG, "Started testlet(" + ctr +
")");
// Continue looping and creating each Testlet
}
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");
}
} // of while...
// We now wait for every thread to finish, and only then
// will we write a final report and finish the test
//@todo probably an easier way; for now, just join the last one
reporter.logMsg(Logger.STATUSMSG, "Driver main thread now joining
last thread");
long maxWaitMillis = 100000; // Wait at most xxxx milliseconds
// Try waiting for the last thread several times
testletThreads[testletThreads.length - 1].waitForComplete
(reporter, maxWaitMillis, 10);
reporter.logMsg(Logger.TRACEMSG, "Apparently-Joined last thread");
// Also join all other threads
for (int i = 0; i < (testletThreads.length - 1); i++)
{
// Only wait a little while for these
testletThreads[i].waitForComplete(reporter, maxWaitMillis, 2);
}
reporter.logMsg(Logger.STATUSMSG, "Apparently-joined all threads");
// Log results from all threads
for (int i = 0; i < testletThreads.length; i++)
{
switch (testletThreads[i].result)
{
case Logger.PASS_RESULT:
if (testletThreads[i].complete)
{
reporter.checkPass("Thread(" + i + ") " +
testletThreads[i].lastStatus);
}
else
{
reporter.checkPass("Thread(" + i + ") " +
testletThreads[i].lastStatus);
//@todo What kind of status do we do here?
// In theory the testlet successfully ran
// transforms and checked results, but
// for some reason we don't think the
// thread completed properly/in time
reporter.checkErr("Thread(" + i + ") NOT COMPLETE! "
+ testletThreads[i].lastStatus);
}
break;
case Logger.FAIL_RESULT:
reporter.checkFail("Thread(" + i + ") " +
testletThreads[i].lastStatus);
break;
case Logger.AMBG_RESULT:
reporter.checkAmbiguous("Thread(" + i + ") " +
testletThreads[i].lastStatus);
break;
case Logger.ERRR_RESULT:
reporter.checkErr("Thread(" + i + ") " +
testletThreads[i].lastStatus);
break;
case Logger.INCP_RESULT:
reporter.checkErr("Thread(" + i + ") INCP! " +
testletThreads[i].lastStatus);
break;
default:
reporter.checkErr("Thread(" + i + ") BAD RESULT! " +
testletThreads[i].lastStatus);
break;
}
//@todo optimizaion: null out vars for gc?
// or should we do this earlier?
testletThreads[i].thread = null;
testletThreads[i].testlet = null;
}
reporter.testCaseClose();
}
/**
* Convenience method to get a Testlet to use.
* Attempts to return one as specified by our testlet parameter,
* otherwise returns a default ThreadedStylesheetTestlet.
* Overrides StylesheetTestletDriver to use a separate logger
* for every testlet, since currently loggers may not be threadsafe.
* Note: We actually cheat and pass a Reporter to each Testlet,
* since they need to keep their own pass/fail state.
*
* @return Testlet for use in this test; null if error
*/
public ThreadedStylesheetTestlet getTestlet(int ctr)
{
// Find a Testlet class to use if we haven't already
if (null == cachedTestletClazz)
{
cachedTestletClazz = QetestUtils.testClassForName(testlet,
testPackages,
defaultTestlet);
}
try
{
// Create it and set a new logger into it
ThreadedStylesheetTestlet t =
(ThreadedStylesheetTestlet)cachedTestletClazz.newInstance();
//@todo Note assumption we have a file name to log to!
// This is too tightly bound to the file-based Loggers
// this design should be cleaned up so we don't know
// what/where/how the loggers are outputing stuff, and
// so each testlet can automatically get a new logger
// based off of our logger
String testletLogFile = testProps.getProperty(Logger.OPT_LOGFILE,
"threadedTestlet");
testletLogFile = testletLogFile + ctr + ".xml";
Properties testletLoggerProperties = new Properties(testProps);
testletLoggerProperties.put(Logger.OPT_LOGFILE, testletLogFile);
t.setLogger(new Reporter(testletLoggerProperties));
return t;
}
catch (Exception e)
{
// Ooops, none found! This should be very rare, since
// we know the defaultTestlet should be found
return null;
}
}
/**
* Local class to store info about threads we spawn.
* A simple little data holding class that stores info about
* various Threads we spawn (presumably each
* ThreadedStylesheetTestlets or the like).
* <ul>
* <li>ThreadedStylesheetTestlet</li>
* <li>Thread - the actual thread started</li>
* <li>lastStatus - copy of testlet.getDescription,
* which the testlet changes to reflect it's current state</li>
* <li>result - copy of testlet.getResult</li>
* <li>complete - convenience variable, if we think
* the testlet is done (or if we think it's ready to be
* reported on; may force this to true if we timeout or think
* the testlet/thread has hung, etc.)</li>
* </ul>
*/
class ThreadedTestletInfo
{
ThreadedStylesheetTestlet testlet = null;
Thread thread = null;
String lastStatus = Logger.INCP;
int result = Logger.INCP_RESULT;
boolean complete = false;
ThreadedTestletInfo(ThreadedStylesheetTestlet tst,
Thread t)
{
testlet = tst;
thread = t;
// Should help ease debugging
thread.setName(((StylesheetDatalet)testlet.getDefaultDatalet()).inputName);
}
/**
* Attempt to wait for this thread to complete.
* Essentially does a .join on our thread, waiting
* millisWait. If this doesn't work, it logs a message
* and then tries again waitNumTimes. If the thread is
* still not done, then log a message and return anyway.
* //@todo should we kill the thread at this point?
*/
void waitForComplete(Logger l, long millisWait, int waitNumTimes)
{
// If we think we're already done, just return
//@todo ensure coordination between this and actual
// Thread state; or remove this
if (complete)
return;
try
{
thread.join(millisWait);
}
catch (InterruptedException ie)
{
l.logMsg(Logger.WARNINGMSG, "waitForComplete threw: " +
ie.toString());
}
if (!thread.isAlive())
{
// If the Thread is already done, then copy over
// each value and return
complete = true;
lastStatus = testlet.getDescription();
result = testlet.getResult();
return;
}
// Otherwise, keep waiting for the thread
for (int i = 0; i < waitNumTimes; i++)
{
l.logMsg(Logger.TRACEMSG, "waitForComplete(" + i + ") of "
+ thread.getName());
try
{
thread.join(millisWait);
}
catch (InterruptedException ie)
{
l.logMsg(Logger.WARNINGMSG, "waitForComplete(" + i + ")
threw: " + ie.toString());
}
}
if (!thread.isAlive())
{
// If the Thread is already done, then set complete
// (which means the thread did finish normally)
complete = true;
}
// Copy over the rest of the testlet/thread's status
lastStatus = testlet.getDescription();
result = testlet.getResult();
return;
}
} // end of inner class ThreadedTestletInfo
/**
* Convenience method to print out usage information - update if needed.
* //@todo update this for iteration, etc.
* @return String denoting usage of this test class
*/
public String usage()
{
return ("Common [optional] options supported by
ThreadedTestletDriver:\n"
+ " -" + OPT_FILELIST
+ " <name of listfile of tests to run>\n"
+ " -" + OPT_DIRFILTER
+ " <classname of FilenameFilter for dirs>\n"
+ " -" + OPT_FILEFILTER
+ " <classname of FilenameFilter for files>\n"
+ " -" + OPT_TESTLET
+ " <classname of Testlet to execute tests with>\n"
+ 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)
{
ThreadedTestletDriver app = new ThreadedTestletDriver();
app.doMain(args);
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]