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]

Reply via email to