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.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
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
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 {
73 if ( defaultSuite.getNumberOfTests() == 0 )
74 {
75 classesToSuites.remove( defaultSuite.getFullClassName() );
76 }
77 }
78
79 return classesToSuites.values();
80 }
81
82
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
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
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" );
174 currentSuite.setNumberOfSkipped( 1 + currentSuite.getNumberOfSkipped() );
175 }
176 }
177 catch ( ParseException e )
178 {
179 throw new SAXException( e.getMessage(), e );
180 }
181 }
182
183
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
216 }
217
218
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 }