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