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         return new String( out.toByteArray() );
122     }
123 
124     public int getCompletedCount()
125     {
126         return completedCount;
127     }
128 
129     public int getErrors()
130     {
131         return errors;
132     }
133 
134     public int getFlakes()
135     {
136         return flakes;
137     }
138 
139     public int getFailures()
140     {
141         return failures;
142     }
143 
144     public int getSkipped()
145     {
146         return skipped;
147     }
148 
149     public Integer getFailsafeCode()  // Only used for compatibility reasons.
150     {
151         if ( completedCount == 0 )
152         {
153             return NO_TESTS;
154         }
155         if ( !isErrorFree() )
156         {
157             return FAILURE;
158         }
159         return null;
160     }
161 
162     /* Indicates if the tests are error free */
163     public boolean isErrorFree()
164     {
165         return getFailures() == 0 && getErrors() == 0;
166     }
167 
168     /* Indicates test timeout or technical failure */
169     public boolean isFailureOrTimeout()
170     {
171         return this.timeout || isFailure();
172     }
173 
174     public boolean isFailure()
175     {
176         return failure != null;
177     }
178 
179     public String getFailure()
180     {
181         return failure;
182     }
183 
184     public boolean isTimeout()
185     {
186         return timeout;
187     }
188 
189 
190     public RunResult aggregate( RunResult other )
191     {
192         String failureMessage = getFailure() != null ? getFailure() : other.getFailure();
193         boolean timeout = isTimeout() || other.isTimeout();
194         int completed = getCompletedCount() + other.getCompletedCount();
195         int fail = getFailures() + other.getFailures();
196         int ign = getSkipped() + other.getSkipped();
197         int err = getErrors() + other.getErrors();
198         int flakes = getFlakes() + other.getFlakes();
199         return new RunResult( completed, err, fail, ign, flakes, failureMessage, timeout );
200     }
201 
202     public static RunResult noTestsRun()
203     {
204         return new RunResult( 0, 0, 0, 0 );
205     }
206 
207     private Xpp3Dom create( String node, String value )
208     {
209         Xpp3Dom dom = new Xpp3Dom( node );
210         dom.setValue( value );
211         return dom;
212     }
213 
214     private Xpp3Dom create( String node, int value )
215     {
216         return create( node, Integer.toString( value ) );
217     }
218 
219     Xpp3Dom asXpp3Dom()
220     {
221         Xpp3Dom dom = new Xpp3Dom( "failsafe-summary" );
222         Integer failsafeCode = getFailsafeCode();
223         if ( failsafeCode != null )
224         {
225             dom.setAttribute( "result", Integer.toString( failsafeCode ) );
226         }
227         dom.setAttribute( "timeout", Boolean.toString( this.timeout ) );
228         dom.addChild( create( "completed", this.completedCount ) );
229         dom.addChild( create( "errors", this.errors ) );
230         dom.addChild( create( "failures", this.failures ) );
231         dom.addChild( create( "skipped", this.skipped ) );
232         dom.addChild( create( "failureMessage", this.failure ) );
233         return dom;
234     }
235 
236     public static RunResult fromInputStream( InputStream inputStream, String encoding )
237         throws FileNotFoundException
238     {
239         Xpp3Dom dom = Xpp3DomBuilder.build( inputStream, encoding );
240         boolean timeout = Boolean.parseBoolean( dom.getAttribute( "timeout" ) );
241         int completed = Integer.parseInt( dom.getChild( "completed" ).getValue() );
242         int errors = Integer.parseInt( dom.getChild( "errors" ).getValue() );
243         int failures = Integer.parseInt( dom.getChild( "failures" ).getValue() );
244         int skipped = Integer.parseInt( dom.getChild( "skipped" ).getValue() );
245         String failureMessage1 = dom.getChild( "failureMessage" ).getValue();
246         String failureMessage = StringUtils.isEmpty( failureMessage1 ) ? null : failureMessage1;
247         return new RunResult( completed, errors, failures, skipped, failureMessage, timeout );
248     }
249 
250     public void writeSummary( File summaryFile, boolean inProgress, String encoding )
251         throws IOException
252     {
253         if ( !summaryFile.getParentFile().isDirectory() )
254         {
255             //noinspection ResultOfMethodCallIgnored
256             summaryFile.getParentFile().mkdirs();
257         }
258 
259         FileInputStream fin = null;
260         FileWriter writer = null;
261         try
262         {
263             RunResult mergedSummary = this;
264             if ( summaryFile.exists() && inProgress )
265             {
266                 fin = new FileInputStream( summaryFile );
267 
268                 RunResult runResult = RunResult.fromInputStream( new BufferedInputStream( fin ), encoding );
269                 mergedSummary = mergedSummary.aggregate( runResult );
270             }
271 
272             writer = new FileWriter( summaryFile );
273             writer.write( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
274             PrettyPrintXMLWriter prettyPrintXMLWriter = new PrettyPrintXMLWriter( writer );
275             Xpp3DomWriter.write( prettyPrintXMLWriter, mergedSummary.asXpp3Dom() );
276         }
277         finally
278         {
279             IOUtil.close( fin );
280             IOUtil.close( writer );
281         }
282     }
283 
284     @SuppressWarnings( "RedundantIfStatement" )
285     public boolean equals( Object o )
286     {
287         if ( this == o )
288         {
289             return true;
290         }
291         if ( o == null || getClass() != o.getClass() )
292         {
293             return false;
294         }
295 
296         RunResult runResult = (RunResult) o;
297 
298         if ( completedCount != runResult.completedCount )
299         {
300             return false;
301         }
302         if ( errors != runResult.errors )
303         {
304             return false;
305         }
306         if ( failures != runResult.failures )
307         {
308             return false;
309         }
310         if ( skipped != runResult.skipped )
311         {
312             return false;
313         }
314         if ( timeout != runResult.timeout )
315         {
316             return false;
317         }
318         if ( failure != null ? !failure.equals( runResult.failure ) : runResult.failure != null )
319         {
320             return false;
321         }
322 
323         return true;
324     }
325 
326     public int hashCode()
327     {
328         int result = completedCount;
329         result = 31 * result + errors;
330         result = 31 * result + failures;
331         result = 31 * result + skipped;
332         result = 31 * result + ( failure != null ? failure.hashCode() : 0 );
333         result = 31 * result + ( timeout ? 1 : 0 );
334         return result;
335     }
336 }