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         try
107         {
108             List<RunEntryStatistics> items = new ArrayList<RunEntryStatistics>( runEntryStatistics.values() );
109             Collections.sort( items, new RunCountComparator() );
110             for ( RunEntryStatistics item : items )
111             {
112                 printWriter.println( item.toString() );
113             }
114         }
115         finally
116         {
117             printWriter.close();
118         }
119     }
120 
121     public RunEntryStatistics findOrCreate( ReportEntry reportEntry )
122     {
123         final RunEntryStatistics item = runEntryStatistics.get( reportEntry.getName() );
124         return item != null ? item : RunEntryStatistics.fromReportEntry( reportEntry );
125     }
126 
127     public RunEntryStatistics createNextGeneration( ReportEntry reportEntry )
128     {
129         final RunEntryStatistics newItem = findOrCreate( reportEntry );
130         final Integer elapsed = reportEntry.getElapsed();
131         return newItem.nextGeneration( elapsed != null ? elapsed : 0 );
132     }
133 
134     public RunEntryStatistics createNextGenerationFailure( ReportEntry reportEntry )
135     {
136         final RunEntryStatistics newItem = findOrCreate( reportEntry );
137         final Integer elapsed = reportEntry.getElapsed();
138         return newItem.nextGenerationFailure( elapsed != null ? elapsed : 0 );
139     }
140 
141     public void add( RunEntryStatistics item )
142     {
143         runEntryStatistics.put( item.getTestName(), item );
144     }
145 
146     class RunCountComparator
147         implements Comparator<RunEntryStatistics>
148     {
149         public int compare( RunEntryStatistics o, RunEntryStatistics o1 )
150         {
151             int runtime = o.getSuccessfulBuilds() - o1.getSuccessfulBuilds();
152             return runtime == 0 ? o.getRunTime() - o1.getRunTime() : runtime;
153         }
154     }
155 
156     public List<Class<?>> getPrioritizedTestsClassRunTime( List<Class<?>> testsToRun, int threadCount )
157     {
158         List<PrioritizedTest> prioritizedTests = getPrioritizedTests( testsToRun, new TestRuntimeComparator() );
159         ThreadedExecutionScheduler threadedExecutionScheduler = new ThreadedExecutionScheduler( threadCount );
160         for ( Object prioritizedTest1 : prioritizedTests )
161         {
162             threadedExecutionScheduler.addTest( (PrioritizedTest) prioritizedTest1 );
163         }
164 
165         return threadedExecutionScheduler.getResult();
166     }
167 
168     public List<Class<?>> getPrioritizedTestsByFailureFirst( List<Class<?>> testsToRun )
169     {
170         List<PrioritizedTest> prioritizedTests = getPrioritizedTests( testsToRun, new LeastFailureComparator() );
171         return transformToClasses( prioritizedTests );
172     }
173 
174     private List<PrioritizedTest> getPrioritizedTests( List<Class<?>> testsToRun,
175                                                        Comparator<Priority> priorityComparator )
176     {
177         Map classPriorities = getPriorities( priorityComparator );
178 
179         List<PrioritizedTest> tests = new ArrayList<PrioritizedTest>();
180         for ( Class<?> clazz : testsToRun )
181         {
182             Priority pri = (Priority) classPriorities.get( clazz.getName() );
183             if ( pri == null )
184             {
185                 pri = Priority.newTestClassPriority( clazz.getName() );
186             }
187             PrioritizedTest prioritizedTest = new PrioritizedTest( clazz, pri );
188             tests.add( prioritizedTest );
189         }
190         Collections.sort( tests, new PrioritizedTestComparator() );
191         return tests;
192     }
193 
194     private List<Class<?>> transformToClasses( List<PrioritizedTest> tests )
195     {
196         List<Class<?>> result = new ArrayList<Class<?>>();
197         for ( PrioritizedTest test : tests )
198         {
199             result.add( test.getClazz() );
200         }
201         return result;
202     }
203 
204     public Map getPriorities( Comparator<Priority> priorityComparator )
205     {
206         Map<String, Priority> priorities = new HashMap<String, Priority>();
207         for ( Object o : runEntryStatistics.keySet() )
208         {
209             String testNames = (String) o;
210             String clazzName = extractClassName( testNames );
211             Priority priority = priorities.get( clazzName );
212             if ( priority == null )
213             {
214                 priority = new Priority( clazzName );
215                 priorities.put( clazzName, priority );
216             }
217 
218             RunEntryStatistics itemStat = runEntryStatistics.get( testNames );
219             priority.addItem( itemStat );
220         }
221 
222         List<Priority> items = new ArrayList<Priority>( priorities.values() );
223         Collections.sort( items, priorityComparator );
224         Map<String, Priority> result = new HashMap<String, Priority>();
225         int i = 0;
226         for ( Priority pri : items )
227         {
228             pri.setPriority( i++ );
229             result.put( pri.getClassName(), pri );
230         }
231         return result;
232     }
233 
234     class PrioritizedTestComparator
235         implements Comparator<PrioritizedTest>
236     {
237         public int compare( PrioritizedTest o, PrioritizedTest o1 )
238         {
239             return o.getPriority() - o1.getPriority();
240         }
241     }
242 
243     class TestRuntimeComparator
244         implements Comparator<Priority>
245     {
246         public int compare( Priority o, Priority o1 )
247         {
248             return o1.getTotalRuntime() - o.getTotalRuntime();
249         }
250     }
251 
252     class LeastFailureComparator
253         implements Comparator<Priority>
254     {
255         public int compare( Priority o, Priority o1 )
256         {
257             return o.getMinSuccessRate() - o1.getMinSuccessRate();
258         }
259     }
260 
261 
262     private static final Pattern PARENS = Pattern.compile( "^" + "[^\\(\\)]+" //non-parens
263                                                                + "\\((" // then an open-paren (start matching a group)
264                                                                + "[^\\\\(\\\\)]+" //non-parens
265                                                                + ")\\)" + "$" ); // then a close-paren (end group match)
266 
267     String extractClassName( String displayName )
268     {
269         Matcher m = PARENS.matcher( displayName );
270         return m.find() ? m.group( 1 ) : displayName;
271     }
272 }