View Javadoc
1   package org.apache.maven.surefire.suite;
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 org.apache.maven.shared.utils.StringUtils;
23  import org.apache.maven.shared.utils.io.IOUtil;
24  import org.apache.maven.shared.utils.xml.PrettyPrintXMLWriter;
25  import org.apache.maven.shared.utils.xml.Xpp3Dom;
26  import org.apache.maven.shared.utils.xml.Xpp3DomBuilder;
27  import org.apache.maven.shared.utils.xml.Xpp3DomWriter;
28  
29  import java.io.BufferedInputStream;
30  import java.io.ByteArrayOutputStream;
31  import java.io.File;
32  import java.io.FileInputStream;
33  import java.io.FileNotFoundException;
34  import java.io.FileWriter;
35  import java.io.IOException;
36  import java.io.InputStream;
37  import java.io.PrintWriter;
38  
39  /**
40   * Represents a test-run-result; this may be from a single test run or an aggregated result.
41   * <p/>
42   * In the case of timeout==true, the run-counts reflect the state of the test-run at the time
43   * of the timeout.
44   *
45   * @author Kristian Rosenvold
46   */
47  public class RunResult
48  {
49      private final int completedCount;
50  
51      private final int errors;
52  
53      private final int failures;
54  
55      private final int skipped;
56  
57      private final int flakes;
58  
59      private final String failure;
60  
61      private final boolean timeout;
62  
63      public static final int SUCCESS = 0;
64  
65      private static final int FAILURE = 255;
66  
67      private static final int NO_TESTS = 254;
68  
69      public static RunResult timeout( RunResult accumulatedAtTimeout )
70      {
71          return errorCode( accumulatedAtTimeout, accumulatedAtTimeout.getFailure(), true );
72      }
73  
74      public static RunResult failure( RunResult accumulatedAtTimeout, Exception cause )
75      {
76          return errorCode( accumulatedAtTimeout, getStackTrace( cause ), accumulatedAtTimeout.isTimeout() );
77      }
78  
79      private static RunResult errorCode( RunResult other, String failure, boolean timeout )
80      {
81          return new RunResult( other.getCompletedCount(), other.getErrors(), other.getFailures(), other.getSkipped(),
82                                failure, timeout );
83  
84      }
85  
86      public RunResult( int completedCount, int errors, int failures, int skipped )
87      {
88          this( completedCount, errors, failures, skipped, null, false );
89      }
90  
91      public RunResult( int completedCount, int errors, int failures, int skipped, int flakes )
92      {
93          this( completedCount, errors, failures, skipped, flakes, null, false );
94      }
95  
96      public RunResult( int completedCount, int errors, int failures, int skipped, String failure, boolean timeout )
97      {
98          this( completedCount, errors, failures, skipped, 0, failure, timeout );
99      }
100 
101     public RunResult( int completedCount, int errors, int failures, int skipped, int flakes, String failure,
102                       boolean timeout )
103     {
104         this.completedCount = completedCount;
105         this.errors = errors;
106         this.failures = failures;
107         this.skipped = skipped;
108         this.failure = failure;
109         this.timeout = timeout;
110         this.flakes = flakes;
111     }
112 
113     private static String getStackTrace( Exception e )
114     {
115         if ( e == null )
116         {
117             return null;
118         }
119         ByteArrayOutputStream out = new ByteArrayOutputStream();
120         PrintWriter pw = new PrintWriter( out );
121         try
122         {
123             e.printStackTrace( pw );
124         }
125         finally
126         {
127             pw.close();
128         }
129         return new String( out.toByteArray() );
130     }
131 
132     public int getCompletedCount()
133     {
134         return completedCount;
135     }
136 
137     public int getErrors()
138     {
139         return errors;
140     }
141 
142     public int getFlakes()
143     {
144         return flakes;
145     }
146 
147     public int getFailures()
148     {
149         return failures;
150     }
151 
152     public int getSkipped()
153     {
154         return skipped;
155     }
156 
157     public Integer getFailsafeCode()  // Only used for compatibility reasons.
158     {
159         if ( completedCount == 0 )
160         {
161             return NO_TESTS;
162         }
163         if ( !isErrorFree() )
164         {
165             return FAILURE;
166         }
167         return null;
168     }
169 
170     /* Indicates if the tests are error free */
171     public boolean isErrorFree()
172     {
173         return getFailures() == 0 && getErrors() == 0;
174     }
175 
176     /* Indicates test timeout or technical failure */
177     public boolean isFailureOrTimeout()
178     {
179         return this.timeout || isFailure();
180     }
181 
182     public boolean isFailure()
183     {
184         return failure != null;
185     }
186 
187     public String getFailure()
188     {
189         return failure;
190     }
191 
192     public boolean isTimeout()
193     {
194         return timeout;
195     }
196 
197 
198     public RunResult aggregate( RunResult other )
199     {
200         String failureMessage = getFailure() != null ? getFailure() : other.getFailure();
201         boolean timeout = isTimeout() || other.isTimeout();
202         int completed = getCompletedCount() + other.getCompletedCount();
203         int fail = getFailures() + other.getFailures();
204         int ign = getSkipped() + other.getSkipped();
205         int err = getErrors() + other.getErrors();
206         int flakes = getFlakes() + other.getFlakes();
207         return new RunResult( completed, err, fail, ign, flakes, failureMessage, timeout );
208     }
209 
210     public static RunResult noTestsRun()
211     {
212         return new RunResult( 0, 0, 0, 0 );
213     }
214 
215     private Xpp3Dom create( String node, String value )
216     {
217         Xpp3Dom dom = new Xpp3Dom( node );
218         dom.setValue( value );
219         return dom;
220     }
221 
222     private Xpp3Dom create( String node, int value )
223     {
224         return create( node, Integer.toString( value ) );
225     }
226 
227     Xpp3Dom asXpp3Dom()
228     {
229         Xpp3Dom dom = new Xpp3Dom( "failsafe-summary" );
230         Integer failsafeCode = getFailsafeCode();
231         if ( failsafeCode != null )
232         {
233             dom.setAttribute( "result", Integer.toString( failsafeCode ) );
234         }
235         dom.setAttribute( "timeout", Boolean.toString( this.timeout ) );
236         dom.addChild( create( "completed", this.completedCount ) );
237         dom.addChild( create( "errors", this.errors ) );
238         dom.addChild( create( "failures", this.failures ) );
239         dom.addChild( create( "skipped", this.skipped ) );
240         dom.addChild( create( "failureMessage", this.failure ) );
241         return dom;
242     }
243 
244     public static RunResult fromInputStream( InputStream inputStream, String encoding )
245         throws FileNotFoundException
246     {
247         Xpp3Dom dom = Xpp3DomBuilder.build( inputStream, encoding );
248         boolean timeout = Boolean.parseBoolean( dom.getAttribute( "timeout" ) );
249         int completed = Integer.parseInt( dom.getChild( "completed" ).getValue() );
250         int errors = Integer.parseInt( dom.getChild( "errors" ).getValue() );
251         int failures = Integer.parseInt( dom.getChild( "failures" ).getValue() );
252         int skipped = Integer.parseInt( dom.getChild( "skipped" ).getValue() );
253         String failureMessage1 = dom.getChild( "failureMessage" ).getValue();
254         String failureMessage = StringUtils.isEmpty( failureMessage1 ) ? null : failureMessage1;
255         return new RunResult( completed, errors, failures, skipped, failureMessage, timeout );
256     }
257 
258     public void writeSummary( File summaryFile, boolean inProgress, String encoding )
259         throws IOException
260     {
261         if ( !summaryFile.getParentFile().isDirectory() )
262         {
263             //noinspection ResultOfMethodCallIgnored
264             summaryFile.getParentFile().mkdirs();
265         }
266 
267         FileInputStream fin = null;
268         FileWriter writer = null;
269         try
270         {
271             RunResult mergedSummary = this;
272             if ( summaryFile.exists() && inProgress )
273             {
274                 fin = new FileInputStream( summaryFile );
275 
276                 RunResult runResult = RunResult.fromInputStream( new BufferedInputStream( fin ), encoding );
277                 mergedSummary = mergedSummary.aggregate( runResult );
278             }
279 
280             writer = new FileWriter( summaryFile );
281             writer.write( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
282             PrettyPrintXMLWriter prettyPrintXMLWriter = new PrettyPrintXMLWriter( writer );
283             Xpp3DomWriter.write( prettyPrintXMLWriter, mergedSummary.asXpp3Dom() );
284         }
285         finally
286         {
287             IOUtil.close( fin );
288             IOUtil.close( writer );
289         }
290     }
291 
292     @SuppressWarnings( "RedundantIfStatement" )
293     public boolean equals( Object o )
294     {
295         if ( this == o )
296         {
297             return true;
298         }
299         if ( o == null || getClass() != o.getClass() )
300         {
301             return false;
302         }
303 
304         RunResult runResult = (RunResult) o;
305 
306         if ( completedCount != runResult.completedCount )
307         {
308             return false;
309         }
310         if ( errors != runResult.errors )
311         {
312             return false;
313         }
314         if ( failures != runResult.failures )
315         {
316             return false;
317         }
318         if ( skipped != runResult.skipped )
319         {
320             return false;
321         }
322         if ( timeout != runResult.timeout )
323         {
324             return false;
325         }
326         if ( failure != null ? !failure.equals( runResult.failure ) : runResult.failure != null )
327         {
328             return false;
329         }
330 
331         return true;
332     }
333 
334     public int hashCode()
335     {
336         int result = completedCount;
337         result = 31 * result + errors;
338         result = 31 * result + failures;
339         result = 31 * result + skipped;
340         result = 31 * result + ( failure != null ? failure.hashCode() : 0 );
341         result = 31 * result + ( timeout ? 1 : 0 );
342         return result;
343     }
344 }