1 package org.apache.maven.plugin.surefire.runorder;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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.Comparator;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.concurrent.ConcurrentHashMap;
39 import java.util.regex.Matcher;
40 import java.util.regex.Pattern;
41
42 import static java.util.Collections.sort;
43 import static org.apache.maven.plugin.surefire.runorder.RunEntryStatistics.fromReportEntry;
44 import static org.apache.maven.plugin.surefire.runorder.RunEntryStatistics.fromString;
45
46
47
48
49 public final class RunEntryStatisticsMap
50 {
51 private final Map<String, RunEntryStatistics> runEntryStatistics;
52
53 public RunEntryStatisticsMap( Map<String, RunEntryStatistics> runEntryStatistics )
54 {
55 this.runEntryStatistics = new ConcurrentHashMap<String, RunEntryStatistics>( runEntryStatistics );
56 }
57
58 public RunEntryStatisticsMap()
59 {
60 runEntryStatistics = new ConcurrentHashMap<>();
61 }
62
63 public static RunEntryStatisticsMap fromFile( File file )
64 {
65 if ( file.exists() )
66 {
67 try
68 {
69 return fromReader( new FileReader( file ) );
70 }
71 catch ( IOException e )
72 {
73 throw new RuntimeException( e );
74 }
75 }
76 else
77 {
78 return new RunEntryStatisticsMap();
79 }
80 }
81
82 static RunEntryStatisticsMap fromReader( Reader fileReader )
83 throws IOException
84 {
85 Map<String, RunEntryStatistics> result = new HashMap<>();
86 BufferedReader bufferedReader = new BufferedReader( fileReader );
87 String line = bufferedReader.readLine();
88 while ( line != null )
89 {
90 if ( !line.startsWith( "#" ) )
91 {
92 final RunEntryStatistics stats = fromString( line );
93 result.put( stats.getTestName(), stats );
94 }
95 line = bufferedReader.readLine();
96 }
97 return new RunEntryStatisticsMap( result );
98 }
99
100 public void serialize( File file )
101 throws FileNotFoundException
102 {
103 FileOutputStream fos = new FileOutputStream( file );
104 try ( PrintWriter printWriter = new PrintWriter( fos ) )
105 {
106 List<RunEntryStatistics> items = new ArrayList<>( runEntryStatistics.values() );
107 sort( items, new RunCountComparator() );
108 for ( RunEntryStatistics item : items )
109 {
110 printWriter.println( item.toString() );
111 }
112 }
113 }
114
115 public RunEntryStatistics findOrCreate( ReportEntry reportEntry )
116 {
117 final RunEntryStatistics item = runEntryStatistics.get( reportEntry.getName() );
118 return item != null ? item : 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 static final class RunCountComparator
141 implements Comparator<RunEntryStatistics>
142 {
143 @Override
144 public int compare( RunEntryStatistics o, RunEntryStatistics o1 )
145 {
146 int runtime = o.getSuccessfulBuilds() - o1.getSuccessfulBuilds();
147 return runtime == 0 ? o.getRunTime() - o1.getRunTime() : runtime;
148 }
149 }
150
151 public List<Class<?>> getPrioritizedTestsClassRunTime( List<Class<?>> testsToRun, int threadCount )
152 {
153 List<PrioritizedTest> prioritizedTests = getPrioritizedTests( testsToRun, new TestRuntimeComparator() );
154 ThreadedExecutionScheduler threadedExecutionScheduler = new ThreadedExecutionScheduler( threadCount );
155 for ( Object prioritizedTest1 : prioritizedTests )
156 {
157 threadedExecutionScheduler.addTest( (PrioritizedTest) prioritizedTest1 );
158 }
159
160 return threadedExecutionScheduler.getResult();
161 }
162
163 public List<Class<?>> getPrioritizedTestsByFailureFirst( List<Class<?>> testsToRun )
164 {
165 List<PrioritizedTest> prioritizedTests = getPrioritizedTests( testsToRun, new LeastFailureComparator() );
166 return transformToClasses( prioritizedTests );
167 }
168
169 private List<PrioritizedTest> getPrioritizedTests( List<Class<?>> testsToRun,
170 Comparator<Priority> priorityComparator )
171 {
172 Map classPriorities = getPriorities( priorityComparator );
173
174 List<PrioritizedTest> tests = new ArrayList<>();
175 for ( Class<?> clazz : testsToRun )
176 {
177 Priority pri = (Priority) classPriorities.get( clazz.getName() );
178 if ( pri == null )
179 {
180 pri = Priority.newTestClassPriority( clazz.getName() );
181 }
182 PrioritizedTest prioritizedTest = new PrioritizedTest( clazz, pri );
183 tests.add( prioritizedTest );
184 }
185 sort( tests, new PrioritizedTestComparator() );
186 return tests;
187 }
188
189 private List<Class<?>> transformToClasses( List<PrioritizedTest> tests )
190 {
191 List<Class<?>> result = new ArrayList<>();
192 for ( PrioritizedTest test : tests )
193 {
194 result.add( test.getClazz() );
195 }
196 return result;
197 }
198
199 private Map getPriorities( Comparator<Priority> priorityComparator )
200 {
201 Map<String, Priority> priorities = new HashMap<>();
202 for ( Object o : runEntryStatistics.keySet() )
203 {
204 String testNames = (String) o;
205 String clazzName = extractClassName( testNames );
206 Priority priority = priorities.get( clazzName );
207 if ( priority == null )
208 {
209 priority = new Priority( clazzName );
210 priorities.put( clazzName, priority );
211 }
212
213 RunEntryStatistics itemStat = runEntryStatistics.get( testNames );
214 priority.addItem( itemStat );
215 }
216
217 List<Priority> items = new ArrayList<>( priorities.values() );
218 sort( items, priorityComparator );
219 Map<String, Priority> result = new HashMap<>();
220 int i = 0;
221 for ( Priority pri : items )
222 {
223 pri.setPriority( i++ );
224 result.put( pri.getClassName(), pri );
225 }
226 return result;
227 }
228
229 static final class PrioritizedTestComparator
230 implements Comparator<PrioritizedTest>
231 {
232 @Override
233 public int compare( PrioritizedTest o, PrioritizedTest o1 )
234 {
235 return o.getPriority() - o1.getPriority();
236 }
237 }
238
239 static final class TestRuntimeComparator
240 implements Comparator<Priority>
241 {
242 @Override
243 public int compare( Priority o, Priority o1 )
244 {
245 return o1.getTotalRuntime() - o.getTotalRuntime();
246 }
247 }
248
249 static final class LeastFailureComparator
250 implements Comparator<Priority>
251 {
252 @Override
253 public int compare( Priority o, Priority o1 )
254 {
255 return o.getMinSuccessRate() - o1.getMinSuccessRate();
256 }
257 }
258
259
260 private static final Pattern PARENS = Pattern.compile( "^" + "[^\\(\\)]+"
261 + "\\(("
262 + "[^\\\\(\\\\)]+"
263 + ")\\)" + "$" );
264
265 String extractClassName( String displayName )
266 {
267 Matcher m = PARENS.matcher( displayName );
268 return m.find() ? m.group( 1 ) : displayName;
269 }
270 }