Clover coverage report - Cactus 1.5 for J2EE API 1.2
Coverage timestamp: Wed Feb 18 2004 09:04:33 EST
file stats: LOC: 378   Methods: 13
NCLOC: 163   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
XMLFormatter.java 0% 0% 0% 0%
coverage
 1   
 /*
 2   
  * ====================================================================
 3   
  *
 4   
  * The Apache Software License, Version 1.1
 5   
  *
 6   
  * Copyright (c) 2001-2003 The Apache Software Foundation.  All rights
 7   
  * reserved.
 8   
  *
 9   
  * Redistribution and use in source and binary forms, with or without
 10   
  * modification, are permitted provided that the following conditions
 11   
  * are met:
 12   
  *
 13   
  * 1. Redistributions of source code must retain the above copyright
 14   
  *    notice, this list of conditions and the following disclaimer.
 15   
  *
 16   
  * 2. Redistributions in binary form must reproduce the above copyright
 17   
  *    notice, this list of conditions and the following disclaimer in
 18   
  *    the documentation and/or other materials provided with the
 19   
  *    distribution.
 20   
  *
 21   
  * 3. The end-user documentation included with the redistribution, if
 22   
  *    any, must include the following acknowlegement:
 23   
  *       "This product includes software developed by the
 24   
  *        Apache Software Foundation (http://www.apache.org/)."
 25   
  *    Alternately, this acknowlegement may appear in the software itself,
 26   
  *    if and wherever such third-party acknowlegements normally appear.
 27   
  *
 28   
  * 4. The names "The Jakarta Project", "Cactus" and "Apache Software
 29   
  *    Foundation" must not be used to endorse or promote products
 30   
  *    derived from this software without prior written permission. For
 31   
  *    written permission, please contact apache@apache.org.
 32   
  *
 33   
  * 5. Products derived from this software may not be called "Apache"
 34   
  *    nor may "Apache" appear in their names without prior written
 35   
  *    permission of the Apache Group.
 36   
  *
 37   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 38   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 39   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 40   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 41   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 42   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 43   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 44   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 45   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 46   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 47   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 48   
  * SUCH DAMAGE.
 49   
  * ====================================================================
 50   
  *
 51   
  * This software consists of voluntary contributions made by many
 52   
  * individuals on behalf of the Apache Software Foundation.  For more
 53   
  * information on the Apache Software Foundation, please see
 54   
  * <http://www.apache.org/>.
 55   
  *
 56   
  */
 57   
 package org.apache.cactus.server.runner;
 58   
 
 59   
 import java.text.NumberFormat;
 60   
 import java.util.Locale;
 61   
 
 62   
 import junit.framework.AssertionFailedError;
 63   
 import junit.framework.Test;
 64   
 import junit.framework.TestFailure;
 65   
 import junit.framework.TestListener;
 66   
 import junit.framework.TestResult;
 67   
 
 68   
 import org.apache.cactus.util.JUnitVersionHelper;
 69   
 import org.apache.cactus.util.StringUtil;
 70   
 
 71   
 /**
 72   
  * Format the test results in XML.
 73   
  *
 74   
  * @author <a href="mailto:vmassol@apache.org">Vincent Massol</a>
 75   
  *
 76   
  * @version $Id: XMLFormatter.java,v 1.11 2003/01/03 15:35:45 vmassol Exp $
 77   
  */
 78   
 public class XMLFormatter implements XMLConstants, TestListener
 79   
 {
 80   
     /**
 81   
      * Default stack filter patterns.
 82   
      */
 83   
     public static final String[] DEFAULT_STACK_FILTER_PATTERNS = new String[]
 84   
         {
 85   
             "org.apache.cactus.AbstractTestCase",
 86   
             "org.apache.cactus.AbstractWebTestCase",
 87   
             "org.apache.cactus.FilterTestCase",
 88   
             "org.apache.cactus.JspTestCase",
 89   
             "org.apache.cactus.ServletTestCase",
 90   
             "junit.framework.TestCase",
 91   
             "junit.framework.TestResult",
 92   
             "junit.framework.TestSuite",
 93   
             "junit.framework.Assert.", // don't filter AssertionFailure
 94   
             "java.lang.reflect.Method.invoke("
 95   
         };
 96   
 
 97   
     /**
 98   
      * (optional) Name of the XSL stylesheet to put in the returned XML string
 99   
      * so that the browser will try to apply it (IE at least, I don't know
 100   
      * about the others).
 101   
      */
 102   
     private String xslFileName;
 103   
 
 104   
     /**
 105   
      * The name of the test suite class.
 106   
      */
 107   
     private String suiteClassName;
 108   
 
 109   
     /**
 110   
      * Duration it took to execute all the tests.
 111   
      */
 112   
     private long totalDuration;
 113   
 
 114   
     /**
 115   
      * Time current test was started
 116   
      */
 117   
     private long currentTestStartTime;
 118   
 
 119   
     /**
 120   
      * The number format used to convert durations into strings. Don't use the
 121   
      * default locale for that, because the resulting string needs to use 
 122   
      * dotted decimal notation for an XSLT transformation to work correctly.
 123   
      */
 124   
     private NumberFormat durationFormat = NumberFormat.getInstance(Locale.US);
 125   
 
 126   
     /**
 127   
      * XML string containing executed test case results
 128   
      */
 129   
     private StringBuffer currentTestCaseResults = new StringBuffer();
 130   
 
 131   
     /**
 132   
      * Current test failure (XML string) : failure or error.
 133   
      */
 134   
     private String currentTestFailure;
 135   
 
 136   
     /**
 137   
      * Sets the XSL stylesheet file name to put in the returned XML string
 138   
      * so that the browser will try to apply it (IE at least, I don't know
 139   
      * about the others).
 140   
      *
 141   
      * @param theXslFileName the file name (relative to the webapp root)
 142   
      */
 143  0
     public void setXslFileName(String theXslFileName)
 144   
     {
 145  0
         this.xslFileName = theXslFileName;
 146   
     }
 147   
 
 148   
     /**
 149   
      * @return the suite class name
 150   
      */
 151  0
     public String getSuiteClassName()
 152   
     {
 153  0
         return this.suiteClassName;
 154   
     }
 155   
 
 156   
     /**
 157   
      * Sets the suite class name that was executed.
 158   
      *
 159   
      * @param theSuiteClassName the suite class name
 160   
      */
 161  0
     public void setSuiteClassName(String theSuiteClassName)
 162   
     {
 163  0
         this.suiteClassName = theSuiteClassName;
 164   
     }
 165   
 
 166   
     /**
 167   
      * @return the total duration as a string
 168   
      */
 169  0
     public String getTotalDurationAsString()
 170   
     {
 171  0
         return getDurationAsString(this.totalDuration);
 172   
     }
 173   
 
 174   
     /**
 175   
      * Comvert a duration expressed as a long into a string.
 176   
      *
 177   
      * @param theDuration the duration to convert to string
 178   
      * @return the total duration as a string
 179   
      */
 180  0
     private String getDurationAsString(long theDuration)
 181   
     {
 182  0
         return durationFormat.format((double) theDuration / 1000);
 183   
     }
 184   
 
 185   
     /**
 186   
      * Sets the duration it took to execute all the tests.
 187   
      *
 188   
      * @param theDuration the time it took
 189   
      */
 190  0
     public void setTotalDuration(long theDuration)
 191   
     {
 192  0
         this.totalDuration = theDuration;
 193   
     }
 194   
 
 195   
     /**
 196   
      * Formats the test result as an XML string.
 197   
      *
 198   
      * @param theResult the test result object
 199   
      * @return the XML string representation of the test results
 200   
      */
 201  0
     public String toXML(TestResult theResult)
 202   
     {
 203  0
         StringBuffer xml = new StringBuffer();
 204   
 
 205  0
         xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
 206   
 
 207  0
         if (this.xslFileName != null)
 208   
         {
 209  0
             xml.append("<?xml-stylesheet type=\"text/xsl\" " + "href=\""
 210   
                 + this.xslFileName + "\"?>");
 211   
         }
 212   
 
 213  0
         xml.append("<" + TESTSUITES + ">");
 214   
 
 215  0
         xml.append("<" + TESTSUITE + " " + ATTR_NAME + "=\""
 216   
             + getSuiteClassName() + "\" " + ATTR_TESTS + "=\""
 217   
             + theResult.runCount() + "\" " + ATTR_FAILURES + "=\""
 218   
             + theResult.failureCount() + "\" " + ATTR_ERRORS + "=\""
 219   
             + theResult.errorCount() + "\" " + ATTR_TIME + "=\""
 220   
             + getTotalDurationAsString() + "\">");
 221   
 
 222  0
         xml.append(this.currentTestCaseResults.toString());
 223   
 
 224  0
         xml.append("</" + TESTSUITE + ">");
 225  0
         xml.append("</" + TESTSUITES + ">");
 226   
 
 227  0
         return xml.toString();
 228   
     }
 229   
 
 230   
     /**
 231   
      * Event called by the base test runner when the test starts.
 232   
      *
 233   
      * @param theTest the test object being executed
 234   
      */
 235  0
     public void startTest(Test theTest)
 236   
     {
 237  0
         this.currentTestStartTime = System.currentTimeMillis();
 238  0
         this.currentTestFailure = null;
 239   
     }
 240   
 
 241   
     /**
 242   
      * Event called by the base test runner when the test fails with an error.
 243   
      *
 244   
      * @param theTest the test object that failed
 245   
      * @param theThrowable the exception that was thrown
 246   
      */
 247  0
     public void addError(Test theTest, Throwable theThrowable)
 248   
     {
 249  0
         TestFailure failure = new TestFailure(theTest, theThrowable);
 250  0
         StringBuffer xml = new StringBuffer();
 251   
 
 252  0
         xml.append("<" + ERROR + " " + ATTR_MESSAGE + "=\""
 253   
             + xmlEncode(failure.thrownException().getMessage()) + "\" "
 254   
             + ATTR_TYPE + "=\""
 255   
             + failure.thrownException().getClass().getName() + "\">");
 256  0
         xml.append(xmlEncode(StringUtil.exceptionToString(
 257   
             failure.thrownException(), DEFAULT_STACK_FILTER_PATTERNS)));
 258  0
         xml.append("</" + ERROR + ">");
 259   
 
 260  0
         this.currentTestFailure = xml.toString();
 261   
     }
 262   
 
 263   
     /**
 264   
      * Event called by the base test runner when the test fails with a failure.
 265   
      *
 266   
      * @param theTest the test object that failed
 267   
      * @param theError the exception that was thrown
 268   
      */
 269  0
     public void addFailure(Test theTest, AssertionFailedError theError)
 270   
     {
 271  0
         TestFailure failure = new TestFailure(theTest, theError);
 272  0
         StringBuffer xml = new StringBuffer();
 273   
 
 274  0
         xml.append("<" + FAILURE + " " + ATTR_MESSAGE + "=\""
 275   
             + xmlEncode(failure.thrownException().getMessage()) + "\" "
 276   
             + ATTR_TYPE + "=\""
 277   
             + failure.thrownException().getClass().getName() + "\">");
 278  0
         xml.append(xmlEncode(StringUtil.exceptionToString(
 279   
             failure.thrownException(), DEFAULT_STACK_FILTER_PATTERNS)));
 280  0
         xml.append("</" + FAILURE + ">");
 281   
 
 282  0
         this.currentTestFailure = xml.toString();
 283   
     }
 284   
 
 285   
     /**
 286   
      * Event called by the base test runner when the test ends.
 287   
      *
 288   
      * @param theTest the test object being executed
 289   
      */
 290  0
     public void endTest(Test theTest)
 291   
     {
 292  0
         StringBuffer xml = new StringBuffer();
 293  0
         String duration = getDurationAsString(System.currentTimeMillis()
 294   
             - this.currentTestStartTime);
 295   
 
 296  0
         xml.append("<" + TESTCASE + " " + ATTR_NAME + "=\""
 297   
             + JUnitVersionHelper.getTestCaseName(theTest) + "\" "
 298   
             + ATTR_TIME + "=\"" + duration + "\">");
 299   
 
 300  0
         if (this.currentTestFailure != null)
 301   
         {
 302  0
             xml.append(this.currentTestFailure);
 303   
         }
 304   
 
 305  0
         xml.append("</" + TESTCASE + ">");
 306   
 
 307  0
         this.currentTestCaseResults.append(xml.toString());
 308   
     }
 309   
 
 310   
     /**
 311   
      * Escapes reserved XML characters.
 312   
      *
 313   
      * @param theString the string to escape
 314   
      * @return the escaped string
 315   
      */
 316  0
     private String xmlEncode(String theString)
 317   
     {
 318  0
         String newString;
 319   
 
 320   
         // It is important to replace the "&" first as the other replacements
 321   
         // also introduces "&" chars ...
 322  0
         newString = XMLFormatter.replace(theString, '&', "&amp;");
 323   
 
 324  0
         newString = XMLFormatter.replace(newString, '<', "&lt;");
 325  0
         newString = XMLFormatter.replace(newString, '>', "&gt;");
 326  0
         newString = XMLFormatter.replace(newString, '\"', "&quot;");
 327   
 
 328  0
         return newString;
 329   
     }
 330   
 
 331   
     /**
 332   
      * Replaces a character in a string by a substring.
 333   
      *
 334   
      * @param theBaseString the base string in which to perform replacements
 335   
      * @param theChar the char to look for
 336   
      * @param theNewString the string with which to replace the char
 337   
      * @return the string with replacements done or null if the input string
 338   
      *          was null
 339   
      */
 340  0
     private static String replace(String theBaseString, char theChar, 
 341   
         String theNewString)
 342   
     {
 343  0
         if (theBaseString == null)
 344   
         {
 345  0
             return null;
 346   
         }
 347   
 
 348  0
         final int len = theBaseString.length() - 1;
 349  0
         int pos = -1;
 350   
 
 351  0
         while ((pos = theBaseString.indexOf(theChar, pos + 1)) > -1)
 352   
         {
 353  0
             if (pos == 0)
 354   
             {
 355  0
                 final String after = theBaseString.substring(1);
 356   
 
 357  0
                 theBaseString = theNewString + after;
 358   
             }
 359  0
             else if (pos == len)
 360   
             {
 361  0
                 final String before = theBaseString.substring(0, pos);
 362   
 
 363  0
                 theBaseString = before + theNewString;
 364   
             }
 365   
             else
 366   
             {
 367  0
                 final String before = theBaseString.substring(0, pos);
 368  0
                 final String after = theBaseString.substring(pos + 1);
 369   
 
 370  0
                 theBaseString = before + theNewString + after;
 371   
             }
 372   
         }
 373   
 
 374  0
         return theBaseString;
 375   
     }
 376   
 
 377   
 }
 378