1 package org.apache.maven.surefire.api.runorder;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.surefire.api.report.ReportEntry;
23 import org.apache.maven.surefire.api.util.internal.ClassMethod;
24
25 import java.io.BufferedWriter;
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.io.OutputStreamWriter;
33 import java.util.ArrayList;
34 import java.util.Comparator;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Map.Entry;
40 import java.util.Scanner;
41 import java.util.concurrent.ConcurrentHashMap;
42
43 import static java.lang.Integer.parseInt;
44 import static java.nio.charset.StandardCharsets.UTF_8;
45 import static java.util.Collections.sort;
46 import static org.apache.maven.surefire.api.util.internal.StringUtils.NL;
47
48
49
50
51 public final class RunEntryStatisticsMap
52 {
53 private final Map<ClassMethod, RunEntryStatistics> runEntryStatistics;
54
55 private RunEntryStatisticsMap( Map<ClassMethod, RunEntryStatistics> runEntryStatistics )
56 {
57 this.runEntryStatistics = new ConcurrentHashMap<>( runEntryStatistics );
58 }
59
60 public RunEntryStatisticsMap()
61 {
62 runEntryStatistics = new ConcurrentHashMap<>();
63 }
64
65 public static RunEntryStatisticsMap fromFile( File file )
66 {
67 if ( file.exists() )
68 {
69 try
70 {
71 return fromStream( new FileInputStream( file ) );
72 }
73 catch ( IOException e )
74 {
75 throw new RuntimeException( e );
76 }
77 }
78 else
79 {
80 return new RunEntryStatisticsMap();
81 }
82 }
83
84 public static RunEntryStatisticsMap fromStream( InputStream fileReader )
85 {
86 Map<ClassMethod, RunEntryStatistics> result = new HashMap<>();
87 try ( Scanner scanner = new Scanner( fileReader, "UTF-8" ) )
88 {
89 RunEntryStatistics previous = null;
90 while ( scanner.hasNextLine() )
91 {
92 String line = scanner.nextLine();
93
94 if ( line.charAt( 0 ) == ' ' )
95 {
96 previous = new RunEntryStatistics( previous.getRunTime(),
97 previous.getSuccessfulBuilds(),
98 previous.getClassMethod().getClazz(),
99 previous.getClassMethod().getMethod() + NL + line.substring( 1 ) );
100 }
101 else
102 {
103 if ( previous != null )
104 {
105 result.put( previous.getClassMethod(), previous );
106 }
107
108 int to = line.indexOf( ',' );
109
110 String successfulBuildsString = line.substring( 0, to );
111 int successfulBuilds = parseInt( successfulBuildsString );
112
113 int from = 1 + to;
114 to = line.indexOf( ',', from + 1 );
115
116 String runTimeString = line.substring( from, to );
117 int runTime = parseInt( runTimeString );
118
119 from = 1 + to;
120 to = line.indexOf( ',', from + 1 );
121
122 String className = to == -1 ? line.substring( from ) : line.substring( from, to );
123
124 from = 1 + to;
125
126 String methodName = to == -1 ? null : line.substring( from );
127
128 ClassMethod classMethod = new ClassMethod( className, methodName );
129 previous = new RunEntryStatistics( runTime, successfulBuilds, classMethod );
130 }
131 }
132 if ( previous != null )
133 {
134 result.put( previous.getClassMethod(), previous );
135 }
136 }
137 return new RunEntryStatisticsMap( result );
138 }
139
140 public void serialize( File statsFile )
141 throws IOException
142 {
143 if ( statsFile.isFile() )
144 {
145
146 statsFile.delete();
147 }
148 OutputStream os = new FileOutputStream( statsFile );
149 try ( BufferedWriter writer = new BufferedWriter( new OutputStreamWriter( os, UTF_8 ), 64 * 1024 ) )
150 {
151 List<RunEntryStatistics> items = new ArrayList<>( runEntryStatistics.values() );
152 sort( items, new RunCountComparator() );
153 for ( Iterator<RunEntryStatistics> it = items.iterator(); it.hasNext(); )
154 {
155 RunEntryStatistics item = it.next();
156 ClassMethod test = item.getClassMethod();
157 String line = item.getSuccessfulBuilds() + "," + item.getRunTime() + "," + test.getClazz() + ",";
158 writer.write( line );
159 boolean wasFirstLine = false;
160 String method = test.getMethod();
161 if ( method == null )
162 {
163 continue;
164 }
165
166 for ( Scanner scanner = new Scanner( method ); scanner.hasNextLine(); wasFirstLine = true )
167 {
168 String methodLine = scanner.nextLine();
169 if ( wasFirstLine )
170 {
171 writer.write( ' ' );
172 }
173 writer.write( methodLine );
174 if ( scanner.hasNextLine() )
175 {
176 writer.newLine();
177 }
178 }
179
180 if ( it.hasNext() )
181 {
182 writer.newLine();
183 }
184 }
185 }
186 }
187
188 private RunEntryStatistics findOrCreate( ReportEntry reportEntry )
189 {
190 ClassMethod classMethod = new ClassMethod( reportEntry.getSourceName(), reportEntry.getName() );
191 RunEntryStatistics item = runEntryStatistics.get( classMethod );
192 return item != null ? item : new RunEntryStatistics( reportEntry.getElapsed( 0 ), 0, classMethod );
193 }
194
195 public RunEntryStatistics createNextGeneration( ReportEntry reportEntry )
196 {
197 RunEntryStatistics newItem = findOrCreate( reportEntry );
198 return newItem.nextGeneration( reportEntry.getElapsed( 0 ) );
199 }
200
201 public RunEntryStatistics createNextGenerationFailure( ReportEntry reportEntry )
202 {
203 RunEntryStatistics newItem = findOrCreate( reportEntry );
204 return newItem.nextGenerationFailure( reportEntry.getElapsed( 0 ) );
205 }
206
207 public void add( RunEntryStatistics item )
208 {
209 runEntryStatistics.put( item.getClassMethod(), item );
210 }
211
212 static final class RunCountComparator
213 implements Comparator<RunEntryStatistics>
214 {
215 @Override
216 public int compare( RunEntryStatistics o, RunEntryStatistics o1 )
217 {
218 int runtime = o.getSuccessfulBuilds() - o1.getSuccessfulBuilds();
219 return runtime == 0 ? o.getRunTime() - o1.getRunTime() : runtime;
220 }
221 }
222
223 public List<Class<?>> getPrioritizedTestsClassRunTime( List<Class<?>> testsToRun, int threadCount )
224 {
225 List<PrioritizedTest> prioritizedTests = getPrioritizedTests( testsToRun, new TestRuntimeComparator() );
226 ThreadedExecutionScheduler threadedExecutionScheduler = new ThreadedExecutionScheduler( threadCount );
227 for ( Object prioritizedTest1 : prioritizedTests )
228 {
229 threadedExecutionScheduler.addTest( (PrioritizedTest) prioritizedTest1 );
230 }
231
232 return threadedExecutionScheduler.getResult();
233 }
234
235 public List<Class<?>> getPrioritizedTestsByFailureFirst( List<Class<?>> testsToRun )
236 {
237 List<PrioritizedTest> prioritizedTests = getPrioritizedTests( testsToRun, new LeastFailureComparator() );
238 return transformToClasses( prioritizedTests );
239 }
240
241 private List<PrioritizedTest> getPrioritizedTests( List<Class<?>> testsToRun,
242 Comparator<Priority> priorityComparator )
243 {
244 Map<String, Priority> classPriorities = getPriorities( priorityComparator );
245
246 List<PrioritizedTest> tests = new ArrayList<>();
247 for ( Class<?> clazz : testsToRun )
248 {
249 Priority pri = classPriorities.get( clazz.getName() );
250 if ( pri == null )
251 {
252 pri = Priority.newTestClassPriority( clazz.getName() );
253 }
254 PrioritizedTest prioritizedTest = new PrioritizedTest( clazz, pri );
255 tests.add( prioritizedTest );
256 }
257 sort( tests, new PrioritizedTestComparator() );
258 return tests;
259 }
260
261 private static List<Class<?>> transformToClasses( List<PrioritizedTest> tests )
262 {
263 List<Class<?>> result = new ArrayList<>();
264 for ( PrioritizedTest test : tests )
265 {
266 result.add( test.getClazz() );
267 }
268 return result;
269 }
270
271 private Map<String, Priority> getPriorities( Comparator<Priority> priorityComparator )
272 {
273 Map<String, Priority> priorities = new HashMap<>();
274 for ( Entry<ClassMethod, RunEntryStatistics> testNames : runEntryStatistics.entrySet() )
275 {
276 String clazzName = testNames.getKey().getClazz();
277 Priority priority = priorities.get( clazzName );
278 if ( priority == null )
279 {
280 priority = new Priority( clazzName );
281 priorities.put( clazzName, priority );
282 }
283 priority.addItem( testNames.getValue() );
284 }
285
286 List<Priority> items = new ArrayList<>( priorities.values() );
287 sort( items, priorityComparator );
288 Map<String, Priority> result = new HashMap<>();
289 int i = 0;
290 for ( Priority pri : items )
291 {
292 pri.setPriority( i++ );
293 result.put( pri.getClassName(), pri );
294 }
295 return result;
296 }
297
298 static final class PrioritizedTestComparator
299 implements Comparator<PrioritizedTest>
300 {
301 @Override
302 public int compare( PrioritizedTest o, PrioritizedTest o1 )
303 {
304 return o.getPriority() - o1.getPriority();
305 }
306 }
307
308 static final class TestRuntimeComparator
309 implements Comparator<Priority>
310 {
311 @Override
312 public int compare( Priority o, Priority o1 )
313 {
314 return o1.getTotalRuntime() - o.getTotalRuntime();
315 }
316 }
317
318 static final class LeastFailureComparator
319 implements Comparator<Priority>
320 {
321 @Override
322 public int compare( Priority o, Priority o1 )
323 {
324 return o.getMinSuccessRate() - o1.getMinSuccessRate();
325 }
326 }
327 }