View Javadoc

1   package org.apache.maven.plugin.surefire.runorder;
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  
23  import java.io.BufferedReader;
24  import java.io.File;
25  import java.io.FileNotFoundException;
26  import java.io.FileOutputStream;
27  import java.io.FileReader;
28  import java.io.IOException;
29  import java.io.PrintWriter;
30  import java.io.Reader;
31  import java.util.ArrayList;
32  import java.util.Collections;
33  import java.util.Comparator;
34  import java.util.HashMap;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.regex.Matcher;
39  import java.util.regex.Pattern;
40  import org.apache.maven.surefire.report.ReportEntry;
41  
42  /**
43   * @author Kristian Rosenvold
44   */
45  public class RunEntryStatisticsMap
46  {
47      private final Map runEntryStatistics;
48  
49      public RunEntryStatisticsMap( Map runEntryStatistics )
50      {
51          this.runEntryStatistics = Collections.synchronizedMap( runEntryStatistics );
52      }
53  
54      public RunEntryStatisticsMap()
55      {
56          this( new HashMap() );
57      }
58  
59      public static RunEntryStatisticsMap fromFile( File file )
60      {
61          if ( file.exists() )
62          {
63              try
64              {
65                  FileReader fileReader = new FileReader( file );
66                  return fromReader( fileReader );
67              }
68              catch ( FileNotFoundException e )
69              {
70                  throw new RuntimeException( e );
71              }
72              catch ( IOException e1 )
73              {
74                  throw new RuntimeException( e1 );
75              }
76  
77          }
78          return new RunEntryStatisticsMap();
79      }
80  
81      static RunEntryStatisticsMap fromReader( Reader fileReader )
82          throws IOException
83      {
84          Map result = new HashMap();
85          BufferedReader bufferedReader = new BufferedReader( fileReader );
86          String line = bufferedReader.readLine();
87          while ( line != null )
88          {
89              if ( !line.startsWith( "#" ) )
90              {
91                  final RunEntryStatistics stats = RunEntryStatistics.fromString( line );
92                  result.put( stats.getTestName(), stats );
93              }
94              line = bufferedReader.readLine();
95          }
96          return new RunEntryStatisticsMap( result );
97      }
98  
99      public void serialize( File file )
100         throws FileNotFoundException
101     {
102         FileOutputStream fos = new FileOutputStream( file );
103         PrintWriter printWriter = new PrintWriter( fos );
104         List items = new ArrayList( runEntryStatistics.values() );
105         Collections.sort( items, new RunCountComparator() );
106         RunEntryStatistics item;
107         for ( Iterator iter = items.iterator(); iter.hasNext(); )
108         {
109             item = (RunEntryStatistics) iter.next();
110             printWriter.println( item.getAsString() );
111         }
112         printWriter.close();
113     }
114 
115 
116     public RunEntryStatistics findOrCreate( ReportEntry reportEntry )
117     {
118         final RunEntryStatistics item = (RunEntryStatistics) runEntryStatistics.get( reportEntry.getName() );
119         return item != null ? item : RunEntryStatistics.fromReportEntry( reportEntry );
120     }
121 
122     public RunEntryStatistics createNextGeneration( ReportEntry reportEntry )
123     {
124         final RunEntryStatistics newItem = findOrCreate( reportEntry );
125         final Integer elapsed = reportEntry.getElapsed();
126         return newItem.nextGeneration( elapsed != null ? elapsed.intValue() : 0 );
127     }
128 
129     public RunEntryStatistics createNextGenerationFailure( ReportEntry reportEntry )
130     {
131         final RunEntryStatistics newItem = findOrCreate( reportEntry );
132         final Integer elapsed = reportEntry.getElapsed();
133         return newItem.nextGenerationFailure( elapsed != null ? elapsed.intValue() : 0 );
134     }
135 
136     public void add( RunEntryStatistics item )
137     {
138         runEntryStatistics.put( item.getTestName(), item );
139     }
140 
141     class RunCountComparator
142         implements Comparator
143     {
144         public int compare( Object o, Object o1 )
145         {
146             RunEntryStatistics re = (RunEntryStatistics) o;
147             RunEntryStatistics re1 = (RunEntryStatistics) o1;
148             int runtime = re.getSuccessfulBuilds() - re1.getSuccessfulBuilds();
149             if ( runtime == 0 )
150             {
151                 return re.getRunTime() - re1.getRunTime();
152             }
153             return runtime;
154         }
155     }
156 
157     public List getPrioritizedTestsClassRunTime( List testsToRun, int threadCount )
158     {
159         final List prioritizedTests = getPrioritizedTests( testsToRun, new TestRuntimeComparator() );
160         ThreadedExecutionScheduler threadedExecutionScheduler = new ThreadedExecutionScheduler( threadCount );
161         for ( Iterator prioritizedTest = prioritizedTests.iterator(); prioritizedTest.hasNext(); )
162         {
163             threadedExecutionScheduler.addTest( (PrioritizedTest) prioritizedTest.next() );
164         }
165 
166         return threadedExecutionScheduler.getResult();
167 
168     }
169 
170     public List getPrioritizedTestsByFailureFirst( List testsToRun )
171     {
172         final List prioritizedTests = getPrioritizedTests( testsToRun, new LeastFailureComparator() );
173         return transformToClasses( prioritizedTests );
174     }
175 
176 
177     private List getPrioritizedTests( List testsToRun, Comparator priorityComparator )
178     {
179         Map classPriorities = getPriorities( priorityComparator );
180 
181         List tests = new ArrayList();
182         for ( Iterator iter = testsToRun.iterator(); iter.hasNext(); )
183         {
184             Class clazz = (Class) iter.next();
185             Priority pri = (Priority) classPriorities.get( clazz.getName() );
186             if ( pri == null )
187             {
188                 pri = Priority.newTestClassPriority( clazz.getName() );
189             }
190             PrioritizedTest prioritizedTest = new PrioritizedTest( clazz, pri );
191             tests.add( prioritizedTest );
192         }
193         Collections.sort( tests, new PrioritizedTestComparator() );
194         return tests;
195 
196     }
197 
198     private List transformToClasses( List tests )
199     {
200         List result = new ArrayList();
201         for ( int i = 0; i < tests.size(); i++ )
202         {
203             result.add( ( (PrioritizedTest) tests.get( i ) ).getClazz() );
204         }
205         return result;
206     }
207 
208     public Map getPriorities( Comparator priorityComparator )
209     {
210         Map priorities = new HashMap();
211         for ( Iterator iter = runEntryStatistics.keySet().iterator(); iter.hasNext(); )
212         {
213             String testNames = (String) iter.next();
214             String clazzName = extractClassName( testNames );
215             Priority priority = (Priority) priorities.get( clazzName );
216             if ( priority == null )
217             {
218                 priority = new Priority( clazzName );
219                 priorities.put( clazzName, priority );
220             }
221 
222             RunEntryStatistics itemStat = (RunEntryStatistics) runEntryStatistics.get( testNames );
223             priority.addItem( itemStat );
224         }
225 
226         List items = new ArrayList( priorities.values() );
227         Collections.sort( items, priorityComparator );
228         Map result = new HashMap();
229         int i = 0;
230         for ( Iterator iter = items.iterator(); iter.hasNext(); )
231         {
232             Priority pri = (Priority) iter.next();
233             pri.setPriority( i++ );
234             result.put( pri.getClassName(), pri );
235         }
236         return result;
237     }
238 
239     class PrioritizedTestComparator
240         implements Comparator
241     {
242         public int compare( Object o, Object o1 )
243         {
244             PrioritizedTest re = (PrioritizedTest) o;
245             PrioritizedTest re1 = (PrioritizedTest) o1;
246             return re.getPriority() - re1.getPriority();
247         }
248     }
249 
250     class TestRuntimeComparator
251         implements Comparator
252     {
253         public int compare( Object o, Object o1 )
254         {
255             Priority re = (Priority) o;
256             Priority re1 = (Priority) o1;
257             return re1.getTotalRuntime() - re.getTotalRuntime();
258         }
259     }
260 
261     class LeastFailureComparator
262         implements Comparator
263     {
264         public int compare( Object o, Object o1 )
265         {
266             Priority re = (Priority) o;
267             Priority re1 = (Priority) o1;
268             return re.getMinSuccessRate() - re1.getMinSuccessRate();
269         }
270     }
271 
272 
273     private static final Pattern PARENS = Pattern.compile( "^" + "[^\\(\\)]+" //non-parens
274                                                                + "\\((" // then an open-paren (start matching a group)
275                                                                + "[^\\\\(\\\\)]+" //non-parens
276                                                                + ")\\)" + "$" ); // then a close-paren (end group match)
277 
278     String extractClassName( String displayName )
279     {
280         Matcher m = PARENS.matcher( displayName );
281         if ( !m.find() )
282         {
283             return displayName;
284         }
285         return m.group( 1 );
286     }
287 
288 
289 }