View Javadoc

1   package org.apache.maven.plugins.surefire.report;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.text.NumberFormat;
25  import java.text.ParseException;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Locale;
31  import java.util.Map;
32  import java.util.StringTokenizer;
33  
34  import javax.xml.parsers.ParserConfigurationException;
35  import javax.xml.parsers.SAXParser;
36  import javax.xml.parsers.SAXParserFactory;
37  
38  import org.xml.sax.Attributes;
39  import org.xml.sax.SAXException;
40  import org.xml.sax.helpers.DefaultHandler;
41  
42  /**
43   * @version $Id: TestSuiteXmlParser.java 1125058 2011-05-19 19:06:07Z krosenvold $
44   */
45  public class TestSuiteXmlParser
46      extends DefaultHandler
47  {
48      private ReportTestSuite defaultSuite;
49      private ReportTestSuite currentSuite;
50      private Map classesToSuites;
51      private final NumberFormat numberFormat = NumberFormat.getInstance( Locale.ENGLISH);
52  
53      /**
54       * @noinspection StringBufferField
55       */
56      private StringBuffer currentElement;
57  
58      private ReportTestCase testCase;
59  
60      public Collection parse( String xmlPath )
61          throws ParserConfigurationException, SAXException, IOException
62      {
63          SAXParserFactory factory = SAXParserFactory.newInstance();
64  
65          SAXParser saxParser = factory.newSAXParser();
66  
67          classesToSuites = new HashMap();
68  
69          saxParser.parse( new File( xmlPath ), this );
70  
71          if ( currentSuite != defaultSuite )
72          { // omit the defaultSuite if it's empty and there are alternatives
73              if ( defaultSuite.getNumberOfTests() == 0 )
74              {
75                  classesToSuites.remove( defaultSuite.getFullClassName() );
76              }
77          }
78  
79          return classesToSuites.values();
80      }
81  
82      /** {@inheritDoc} */
83      public void startElement( String uri, String localName, String qName, Attributes attributes )
84          throws SAXException
85      {
86          try
87          {
88              if ( "testsuite".equals( qName ) )
89              {
90                  currentSuite = defaultSuite = new ReportTestSuite();
91  
92                  try
93                  {
94                      Number time = numberFormat.parse( attributes.getValue( "time" ) );
95  
96                      defaultSuite.setTimeElapsed( time.floatValue() );
97                  }
98                  catch ( NullPointerException npe )
99                  {
100                     System.err.println( "WARNING: no time attribute found on testsuite element" );
101                 }
102 
103                 //check if group attribute is existing
104                 if ( attributes.getValue( "group" ) != null && !"".equals( attributes.getValue( "group" ) ) )
105                 {
106                     String packageName = attributes.getValue( "group" );
107                     String name = attributes.getValue( "name" );
108 
109                     defaultSuite.setFullClassName( packageName + "." + name );
110                 }
111                 else
112                 {
113                     String fullClassName = attributes.getValue( "name" );
114                     defaultSuite.setFullClassName( fullClassName );
115                 }
116 
117                 classesToSuites.put( defaultSuite.getFullClassName(), defaultSuite );
118             }
119             else if ( "testcase".equals( qName ) )
120             {
121                 currentElement = new StringBuffer();
122 
123                 testCase = new ReportTestCase();
124 
125                 testCase.setName( attributes.getValue( "name" ) );
126 
127                 String fullClassName = attributes.getValue( "classname" );
128 
129                 // if the testcase declares its own classname, it may need to belong to its own suite
130                 if ( fullClassName != null )
131                 {
132                     currentSuite = (ReportTestSuite) classesToSuites.get( fullClassName );
133                     if ( currentSuite == null )
134                     {
135                         currentSuite = new ReportTestSuite();
136                         currentSuite.setFullClassName( fullClassName );
137                         classesToSuites.put( fullClassName, currentSuite );
138                     }
139                 }
140 
141                 testCase.setFullClassName( currentSuite.getFullClassName() );
142                 testCase.setClassName( currentSuite.getName() );
143                 testCase.setFullName( currentSuite.getFullClassName() + "." + testCase.getName() );
144 
145                 String timeAsString = attributes.getValue( "time" );
146 
147                 Number time = new Integer( 0 );
148 
149                 if ( timeAsString != null )
150                 {
151                     time = numberFormat.parse( timeAsString );
152                 }
153 
154                 testCase.setTime( time.floatValue() );
155 
156                 if ( currentSuite != defaultSuite )
157                 {
158                     currentSuite.setTimeElapsed( time.floatValue() + currentSuite.getTimeElapsed() );
159                 }
160             }
161             else if ( "failure".equals( qName ) )
162             {
163                 testCase.addFailure( attributes.getValue( "message" ), attributes.getValue( "type" ) );
164                 currentSuite.setNumberOfFailures( 1 + currentSuite.getNumberOfFailures() );
165             }
166             else if ( "error".equals( qName ) )
167             {
168                 testCase.addFailure( attributes.getValue( "message" ), attributes.getValue( "type" ) );
169                 currentSuite.setNumberOfErrors( 1 + currentSuite.getNumberOfErrors() );
170             }
171             else if ( "skipped".equals( qName ) )
172             {
173                 testCase.addFailure( "skipped", "skipped" ); // TODO extract real reasons
174                 currentSuite.setNumberOfSkipped( 1 + currentSuite.getNumberOfSkipped() );
175             }
176         }
177         catch ( ParseException e )
178         {
179             throw new SAXException( e.getMessage(), e );
180         }
181     }
182 
183     /** {@inheritDoc} */
184     public void endElement( String uri, String localName, String qName )
185         throws SAXException
186     {
187         if ( "testcase".equals( qName ) )
188         {
189             currentSuite.getTestCases().add( testCase );
190         }
191         else if ( "failure".equals( qName ) )
192         {
193             Map failure = testCase.getFailure();
194 
195             failure.put( "detail", parseCause( currentElement.toString() ) );
196         }
197         else if ( "error".equals( qName ) )
198         {
199             Map error = testCase.getFailure();
200 
201             error.put( "detail", parseCause( currentElement.toString() ) );
202         }
203         else if ( "time".equals( qName ) )
204         {
205             try
206             {
207                 Number time = numberFormat.parse( currentElement.toString() );
208                 defaultSuite.setTimeElapsed( time.floatValue() );
209             }
210             catch ( ParseException e )
211             {
212                 throw new SAXException( e.getMessage(), e );
213             }
214         }
215         // TODO extract real skipped reasons
216     }
217 
218     /** {@inheritDoc} */
219     public void characters( char[] ch, int start, int length )
220         throws SAXException
221     {
222         String s = new String( ch, start, length );
223 
224         if ( !"".equals( s.trim() ) )
225         {
226             currentElement.append( s );
227         }
228     }
229 
230     private List parseCause( String detail )
231     {
232         String fullName = testCase.getFullName();
233         String name = fullName.substring( fullName.lastIndexOf( "." ) + 1 );
234         return parseCause( detail, name );
235     }
236 
237     private List parseCause( String detail, String compareTo )
238     {
239         StringTokenizer stringTokenizer = new StringTokenizer( detail, "\n" );
240         List parsedDetail = new ArrayList( stringTokenizer.countTokens() );
241 
242         while ( stringTokenizer.hasMoreTokens() )
243         {
244             String lineString = stringTokenizer.nextToken().trim();
245             parsedDetail.add( lineString );
246             if ( lineString.indexOf( compareTo ) >= 0 )
247             {
248                 break;
249             }
250         }
251 
252         return parsedDetail;
253     }
254 
255 }