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