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 org.apache.maven.surefire.report.ReportEntry;
24  
25  import java.io.BufferedReader;
26  import java.io.File;
27  import java.io.FileNotFoundException;
28  import java.io.FileOutputStream;
29  import java.io.FileReader;
30  import java.io.IOException;
31  import java.io.PrintWriter;
32  import java.io.Reader;
33  import java.util.ArrayList;
34  import java.util.Collections;
35  import java.util.Comparator;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.regex.Matcher;
40  import java.util.regex.Pattern;
41  
42  /**
43   * @author Kristian Rosenvold
44   */
45  public class RunEntryStatisticsMap
46  {
47      private final Map<String, RunEntryStatistics> runEntryStatistics;
48  
49      public RunEntryStatisticsMap( Map<String, RunEntryStatistics> runEntryStatistics )
50      {
51          this.runEntryStatistics = Collections.synchronizedMap( runEntryStatistics );
52      }
53  
54      public RunEntryStatisticsMap()
55      {
56          this( new HashMap<String, RunEntryStatistics>() );
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<String, RunEntryStatistics> result = new HashMap<String, RunEntryStatistics>();
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<RunEntryStatistics> items = new ArrayList<RunEntryStatistics>( runEntryStatistics.values() );
105         Collections.sort( items, new RunCountComparator() );
106         RunEntryStatistics item;
107         for ( RunEntryStatistics item1 : items )
108         {
109             item = item1;
110             printWriter.println( item.getAsString() );
111         }
112         printWriter.close();
113     }
114 
115 
116     public RunEntryStatistics findOrCreate( ReportEntry reportEntry )
117     {
118         final RunEntryStatistics item = 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 : 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 : 0 );
134     }
135 
136     public void add( RunEntryStatistics item )
137     {
138         runEntryStatistics.put( item.getTestName(), item );
139     }
140 
141     class RunCountComparator
142         implements Comparator<RunEntryStatistics>
143     {
144         public int compare( RunEntryStatistics o, RunEntryStatistics o1 )
145         {
146             int runtime = o.getSuccessfulBuilds() - o1.getSuccessfulBuilds();
147             if ( runtime == 0 )
148             {
149                 return o.getRunTime() - o1.getRunTime();
150             }
151             return runtime;
152         }
153     }
154 
155     public List<Class> getPrioritizedTestsClassRunTime( List testsToRun, int threadCount )
156     {
157         final List<PrioritizedTest> prioritizedTests = getPrioritizedTests( testsToRun, new TestRuntimeComparator() );
158         ThreadedExecutionScheduler threadedExecutionScheduler = new ThreadedExecutionScheduler( threadCount );
159         for ( Object prioritizedTest1 : prioritizedTests )
160         {
161             threadedExecutionScheduler.addTest( (PrioritizedTest) prioritizedTest1 );
162         }
163 
164         return threadedExecutionScheduler.getResult();
165 
166     }
167 
168     public List<Class> getPrioritizedTestsByFailureFirst( List testsToRun )
169     {
170         final List prioritizedTests = getPrioritizedTests( testsToRun, new LeastFailureComparator() );
171         return transformToClasses( prioritizedTests );
172     }
173 
174 
175     private List<PrioritizedTest> getPrioritizedTests( List testsToRun, Comparator<Priority> priorityComparator )
176     {
177         Map classPriorities = getPriorities( priorityComparator );
178 
179         List<PrioritizedTest> tests = new ArrayList<PrioritizedTest>();
180         for ( Object aTestsToRun : testsToRun )
181         {
182             Class clazz = (Class) aTestsToRun;
183             Priority pri = (Priority) classPriorities.get( clazz.getName() );
184             if ( pri == null )
185             {
186                 pri = Priority.newTestClassPriority( clazz.getName() );
187             }
188             PrioritizedTest prioritizedTest = new PrioritizedTest( clazz, pri );
189             tests.add( prioritizedTest );
190         }
191         Collections.sort( tests, new PrioritizedTestComparator() );
192         return tests;
193 
194     }
195 
196     private List<Class> transformToClasses( List tests )
197     {
198         List<Class> result = new ArrayList<Class>();
199         for ( Object test : tests )
200         {
201             result.add( ( (PrioritizedTest) test ).getClazz() );
202         }
203         return result;
204     }
205 
206     public Map getPriorities( Comparator<Priority> priorityComparator )
207     {
208         Map<String, Priority> priorities = new HashMap<String, Priority>();
209         for ( Object o : runEntryStatistics.keySet() )
210         {
211             String testNames = (String) o;
212             String clazzName = extractClassName( testNames );
213             Priority priority = priorities.get( clazzName );
214             if ( priority == null )
215             {
216                 priority = new Priority( clazzName );
217                 priorities.put( clazzName, priority );
218             }
219 
220             RunEntryStatistics itemStat = runEntryStatistics.get( testNames );
221             priority.addItem( itemStat );
222         }
223 
224         List<Priority> items = new ArrayList<Priority>( priorities.values() );
225         Collections.sort( items, priorityComparator );
226         Map<String, Priority> result = new HashMap<String, Priority>();
227         int i = 0;
228         for ( Priority pri : items )
229         {
230             pri.setPriority( i++ );
231             result.put( pri.getClassName(), pri );
232         }
233         return result;
234     }
235 
236     class PrioritizedTestComparator
237         implements Comparator<PrioritizedTest>
238     {
239         public int compare( PrioritizedTest o, PrioritizedTest o1 )
240         {
241             return o.getPriority() - o1.getPriority();
242         }
243     }
244 
245     class TestRuntimeComparator
246         implements Comparator<Priority>
247     {
248         public int compare( Priority o, Priority o1 )
249         {
250             return o1.getTotalRuntime() - o.getTotalRuntime();
251         }
252     }
253 
254     class LeastFailureComparator
255         implements Comparator<Priority>
256     {
257         public int compare( Priority o, Priority o1 )
258         {
259             return o.getMinSuccessRate() - o1.getMinSuccessRate();
260         }
261     }
262 
263 
264     private static final Pattern PARENS = Pattern.compile( "^" + "[^\\(\\)]+" //non-parens
265                                                                + "\\((" // then an open-paren (start matching a group)
266                                                                + "[^\\\\(\\\\)]+" //non-parens
267                                                                + ")\\)" + "$" ); // then a close-paren (end group match)
268 
269     String extractClassName( String displayName )
270     {
271         Matcher m = PARENS.matcher( displayName );
272         if ( !m.find() )
273         {
274             return displayName;
275         }
276         return m.group( 1 );
277     }
278 
279 
280 }