1 package org.apache.maven.plugins.surefire.report;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.io.InputStreamReader;
26 import java.text.NumberFormat;
27 import java.text.ParseException;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.Map;
34 import java.util.StringTokenizer;
35
36 import javax.xml.parsers.ParserConfigurationException;
37 import javax.xml.parsers.SAXParser;
38 import javax.xml.parsers.SAXParserFactory;
39
40 import org.xml.sax.Attributes;
41 import org.xml.sax.InputSource;
42 import org.xml.sax.SAXException;
43 import org.xml.sax.helpers.DefaultHandler;
44
45
46
47
48 public class TestSuiteXmlParser
49 extends DefaultHandler
50 {
51 private ReportTestSuite defaultSuite;
52
53 private ReportTestSuite currentSuite;
54
55 private Map<String, ReportTestSuite> classesToSuites;
56
57 private final NumberFormat numberFormat = NumberFormat.getInstance( Locale.ENGLISH );
58
59
60
61
62 private StringBuffer currentElement;
63
64 private ReportTestCase testCase;
65
66 private boolean valid;
67
68 public Collection<ReportTestSuite> parse( String xmlPath )
69 throws ParserConfigurationException, SAXException, IOException
70 {
71
72 File f = new File( xmlPath );
73
74 FileInputStream fileInputStream = new FileInputStream( f );
75
76 InputStreamReader inputStreamReader = new InputStreamReader( fileInputStream, "UTF-8" );
77
78 try
79 {
80 return parse( inputStreamReader );
81 }
82 finally
83 {
84 inputStreamReader.close();
85 fileInputStream.close();
86 }
87 }
88
89 public Collection<ReportTestSuite> parse( InputStreamReader stream )
90 throws ParserConfigurationException, SAXException, IOException
91 {
92 SAXParserFactory factory = SAXParserFactory.newInstance();
93
94 SAXParser saxParser = factory.newSAXParser();
95
96 valid = true;
97
98 classesToSuites = new HashMap<String, ReportTestSuite>();
99
100 saxParser.parse( new InputSource( stream ), this );
101
102 if ( currentSuite != defaultSuite )
103 {
104 if ( defaultSuite.getNumberOfTests() == 0 )
105 {
106 classesToSuites.remove( defaultSuite.getFullClassName() );
107 }
108 }
109
110 return classesToSuites.values();
111 }
112
113
114
115
116 public void startElement( String uri, String localName, String qName, Attributes attributes )
117 throws SAXException
118 {
119 if ( !valid )
120 {
121 return;
122 }
123 try
124 {
125 if ( "testsuite".equals( qName ) )
126 {
127 defaultSuite = new ReportTestSuite();
128 currentSuite = defaultSuite;
129
130 try
131 {
132 Number time = numberFormat.parse( attributes.getValue( "time" ) );
133
134 defaultSuite.setTimeElapsed( time.floatValue() );
135 }
136 catch ( NullPointerException npe )
137 {
138 System.err.println( "WARNING: no time attribute found on testsuite element" );
139 }
140
141
142 if ( attributes.getValue( "group" ) != null && !"".equals( attributes.getValue( "group" ) ) )
143 {
144 String packageName = attributes.getValue( "group" );
145 String name = attributes.getValue( "name" );
146
147 defaultSuite.setFullClassName( packageName + "." + name );
148 }
149 else
150 {
151 String fullClassName = attributes.getValue( "name" );
152 defaultSuite.setFullClassName( fullClassName );
153 }
154
155 classesToSuites.put( defaultSuite.getFullClassName(), defaultSuite );
156 }
157 else if ( "testcase".equals( qName ) )
158 {
159 currentElement = new StringBuffer();
160
161 testCase = new ReportTestCase();
162
163 testCase.setName( attributes.getValue( "name" ) );
164
165 String fullClassName = attributes.getValue( "classname" );
166
167
168 if ( fullClassName != null )
169 {
170 currentSuite = classesToSuites.get( fullClassName );
171 if ( currentSuite == null )
172 {
173 currentSuite = new ReportTestSuite();
174 currentSuite.setFullClassName( fullClassName );
175 classesToSuites.put( fullClassName, currentSuite );
176 }
177 }
178
179 testCase.setFullClassName( currentSuite.getFullClassName() );
180 testCase.setClassName( currentSuite.getName() );
181 testCase.setFullName( currentSuite.getFullClassName() + "." + testCase.getName() );
182
183 String timeAsString = attributes.getValue( "time" );
184
185 Number time = 0;
186
187 if ( timeAsString != null )
188 {
189 time = numberFormat.parse( timeAsString );
190 }
191
192 testCase.setTime( time.floatValue() );
193
194 if ( currentSuite != defaultSuite )
195 {
196 currentSuite.setTimeElapsed( time.floatValue() + currentSuite.getTimeElapsed() );
197 }
198 }
199 else if ( "failure".equals( qName ) )
200 {
201 testCase.addFailure( attributes.getValue( "message" ), attributes.getValue( "type" ) );
202 currentSuite.setNumberOfFailures( 1 + currentSuite.getNumberOfFailures() );
203 }
204 else if ( "error".equals( qName ) )
205 {
206 testCase.addFailure( attributes.getValue( "message" ), attributes.getValue( "type" ) );
207 currentSuite.setNumberOfErrors( 1 + currentSuite.getNumberOfErrors() );
208 }
209 else if ( "skipped".equals( qName ) )
210 {
211 final String message = attributes.getValue( "message" );
212 testCase.addFailure( message != null ? message : "skipped", "skipped" );
213 currentSuite.setNumberOfSkipped( 1 + currentSuite.getNumberOfSkipped() );
214 }
215 else if ( "failsafe-summary".equals( qName ) )
216 {
217 valid = false;
218 }
219 }
220 catch ( ParseException e )
221 {
222 throw new SAXException( e.getMessage(), e );
223 }
224 }
225
226
227
228
229 public void endElement( String uri, String localName, String qName )
230 throws SAXException
231 {
232 if ( "testcase".equals( qName ) )
233 {
234 currentSuite.getTestCases().add( testCase );
235 }
236 else if ( "failure".equals( qName ) )
237 {
238 Map<String, Object> failure = testCase.getFailure();
239
240 failure.put( "detail", parseCause( currentElement.toString() ) );
241 }
242 else if ( "error".equals( qName ) )
243 {
244 Map<String, Object> error = testCase.getFailure();
245
246 error.put( "detail", parseCause( currentElement.toString() ) );
247 }
248 else if ( "time".equals( qName ) )
249 {
250 try
251 {
252 Number time = numberFormat.parse( currentElement.toString() );
253 defaultSuite.setTimeElapsed( time.floatValue() );
254 }
255 catch ( ParseException e )
256 {
257 throw new SAXException( e.getMessage(), e );
258 }
259 }
260
261 }
262
263
264
265
266 public void characters( char[] ch, int start, int length )
267 throws SAXException
268 {
269 if ( !valid )
270 {
271 return;
272 }
273 String s = new String( ch, start, length );
274
275 if ( !"".equals( s.trim() ) )
276 {
277 currentElement.append( s );
278 }
279 }
280
281 private List<String> parseCause( String detail )
282 {
283 String fullName = testCase.getFullName();
284 String name = fullName.substring( fullName.lastIndexOf( "." ) + 1 );
285 return parseCause( detail, name );
286 }
287
288 private List<String> parseCause( String detail, String compareTo )
289 {
290 StringTokenizer stringTokenizer = new StringTokenizer( detail, "\n" );
291 List<String> parsedDetail = new ArrayList<String>( stringTokenizer.countTokens() );
292
293 while ( stringTokenizer.hasMoreTokens() )
294 {
295 String lineString = stringTokenizer.nextToken().trim();
296 parsedDetail.add( lineString );
297 if ( lineString.contains( compareTo ) )
298 {
299 break;
300 }
301 }
302
303 return parsedDetail;
304 }
305
306 public boolean isValid()
307 {
308 return valid;
309 }
310 }