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