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 import org.apache.maven.surefire.report.ReportEntry;
23 import org.apache.maven.surefire.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.StringTokenizer;
42 import java.util.concurrent.ConcurrentHashMap;
43
44 import static java.lang.Integer.parseInt;
45 import static java.nio.charset.StandardCharsets.UTF_8;
46 import static java.util.Collections.sort;
47 import static org.apache.maven.surefire.util.internal.StringUtils.NL;
48
49
50
51
52 public final class RunEntryStatisticsMap
53 {
54 private final Map<ClassMethod, RunEntryStatistics> runEntryStatistics;
55
56 private RunEntryStatisticsMap( Map<ClassMethod, RunEntryStatistics> runEntryStatistics )
57 {
58 this.runEntryStatistics = new ConcurrentHashMap<>( runEntryStatistics );
59 }
60
61 public RunEntryStatisticsMap()
62 {
63 runEntryStatistics = new ConcurrentHashMap<>();
64 }
65
66 public static RunEntryStatisticsMap fromFile( File file )
67 {
68 if ( file.exists() )
69 {
70 try
71 {
72 return fromStream( new FileInputStream( file ) );
73 }
74 catch ( IOException e )
75 {
76 throw new RuntimeException( e );
77 }
78 }
79 else
80 {
81 return new RunEntryStatisticsMap();
82 }
83 }
84
85 static RunEntryStatisticsMap fromStream( InputStream fileReader )
86 {
87 Map<ClassMethod, RunEntryStatistics> result = new HashMap<>();
88 try ( Scanner scanner = new Scanner( fileReader, "UTF-8" ) )
89 {
90 RunEntryStatistics previous = null;
91 while ( scanner.hasNextLine() )
92 {
93 String line = scanner.nextLine();
94
95 if ( line.charAt( 0 ) == ' ' )
96 {
97 previous = new RunEntryStatistics( previous.getRunTime(),
98 previous.getSuccessfulBuilds(),
99 previous.getClassMethod().getClazz(),
100 previous.getClassMethod().getMethod() + NL + line.substring( 1 ) );
101 }
102 else
103 {
104 if ( previous != null )
105 {
106 result.put( previous.getClassMethod(), previous );
107 }
108 StringTokenizer tokenizer = new StringTokenizer( line, "," );
109
110 int methodIndex = 3;
111
112 String successfulBuildsString = tokenizer.nextToken();
113 int successfulBuilds = parseInt( successfulBuildsString );
114
115 methodIndex += successfulBuildsString.length();
116
117 String runTimeString = tokenizer.nextToken();
118 int runTime = parseInt( runTimeString );
119
120 methodIndex += runTimeString.length();
121
122 String className = tokenizer.nextToken();
123
124 methodIndex += className.length();
125
126 String methodName = line.substring( methodIndex );
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 for ( Scanner scanner = new Scanner( test.getMethod() ); scanner.hasNextLine(); wasFirstLine = true )
161 {
162 String methodLine = scanner.nextLine();
163 if ( wasFirstLine )
164 {
165 writer.write( ' ' );
166 }
167 writer.write( methodLine );
168 if ( scanner.hasNextLine() )
169 {
170 writer.newLine();
171 }
172 }
173 if ( it.hasNext() )
174 {
175 writer.newLine();
176 }
177 }
178 }
179 }
180
181 private RunEntryStatistics findOrCreate( ReportEntry reportEntry )
182 {
183 ClassMethod classMethod = new ClassMethod( reportEntry.getSourceName(), reportEntry.getName() );
184 RunEntryStatistics item = runEntryStatistics.get( classMethod );
185 return item != null ? item : new RunEntryStatistics( reportEntry.getElapsed( 0 ), 0, classMethod );
186 }
187
188 public RunEntryStatistics createNextGeneration( ReportEntry reportEntry )
189 {
190 RunEntryStatistics newItem = findOrCreate( reportEntry );
191 return newItem.nextGeneration( reportEntry.getElapsed( 0 ) );
192 }
193
194 public RunEntryStatistics createNextGenerationFailure( ReportEntry reportEntry )
195 {
196 RunEntryStatistics newItem = findOrCreate( reportEntry );
197 return newItem.nextGenerationFailure( reportEntry.getElapsed( 0 ) );
198 }
199
200 public void add( RunEntryStatistics item )
201 {
202 runEntryStatistics.put( item.getClassMethod(), item );
203 }
204
205 static final class RunCountComparator
206 implements Comparator<RunEntryStatistics>
207 {
208 @Override
209 public int compare( RunEntryStatistics o, RunEntryStatistics o1 )
210 {
211 int runtime = o.getSuccessfulBuilds() - o1.getSuccessfulBuilds();
212 return runtime == 0 ? o.getRunTime() - o1.getRunTime() : runtime;
213 }
214 }
215
216 public List<Class<?>> getPrioritizedTestsClassRunTime( List<Class<?>> testsToRun, int threadCount )
217 {
218 List<PrioritizedTest> prioritizedTests = getPrioritizedTests( testsToRun, new TestRuntimeComparator() );
219 ThreadedExecutionScheduler threadedExecutionScheduler = new ThreadedExecutionScheduler( threadCount );
220 for ( Object prioritizedTest1 : prioritizedTests )
221 {
222 threadedExecutionScheduler.addTest( (PrioritizedTest) prioritizedTest1 );
223 }
224
225 return threadedExecutionScheduler.getResult();
226 }
227
228 public List<Class<?>> getPrioritizedTestsByFailureFirst( List<Class<?>> testsToRun )
229 {
230 List<PrioritizedTest> prioritizedTests = getPrioritizedTests( testsToRun, new LeastFailureComparator() );
231 return transformToClasses( prioritizedTests );
232 }
233
234 private List<PrioritizedTest> getPrioritizedTests( List<Class<?>> testsToRun,
235 Comparator<Priority> priorityComparator )
236 {
237 Map<String, Priority> classPriorities = getPriorities( priorityComparator );
238
239 List<PrioritizedTest> tests = new ArrayList<>();
240 for ( Class<?> clazz : testsToRun )
241 {
242 Priority pri = classPriorities.get( clazz.getName() );
243 if ( pri == null )
244 {
245 pri = Priority.newTestClassPriority( clazz.getName() );
246 }
247 PrioritizedTest prioritizedTest = new PrioritizedTest( clazz, pri );
248 tests.add( prioritizedTest );
249 }
250 sort( tests, new PrioritizedTestComparator() );
251 return tests;
252 }
253
254 private static List<Class<?>> transformToClasses( List<PrioritizedTest> tests )
255 {
256 List<Class<?>> result = new ArrayList<>();
257 for ( PrioritizedTest test : tests )
258 {
259 result.add( test.getClazz() );
260 }
261 return result;
262 }
263
264 private Map<String, Priority> getPriorities( Comparator<Priority> priorityComparator )
265 {
266 Map<String, Priority> priorities = new HashMap<>();
267 for ( Entry<ClassMethod, RunEntryStatistics> testNames : runEntryStatistics.entrySet() )
268 {
269 String clazzName = testNames.getKey().getClazz();
270 Priority priority = priorities.get( clazzName );
271 if ( priority == null )
272 {
273 priority = new Priority( clazzName );
274 priorities.put( clazzName, priority );
275 }
276 priority.addItem( testNames.getValue() );
277 }
278
279 List<Priority> items = new ArrayList<>( priorities.values() );
280 sort( items, priorityComparator );
281 Map<String, Priority> result = new HashMap<>();
282 int i = 0;
283 for ( Priority pri : items )
284 {
285 pri.setPriority( i++ );
286 result.put( pri.getClassName(), pri );
287 }
288 return result;
289 }
290
291 static final class PrioritizedTestComparator
292 implements Comparator<PrioritizedTest>
293 {
294 @Override
295 public int compare( PrioritizedTest o, PrioritizedTest o1 )
296 {
297 return o.getPriority() - o1.getPriority();
298 }
299 }
300
301 static final class TestRuntimeComparator
302 implements Comparator<Priority>
303 {
304 @Override
305 public int compare( Priority o, Priority o1 )
306 {
307 return o1.getTotalRuntime() - o.getTotalRuntime();
308 }
309 }
310
311 static final class LeastFailureComparator
312 implements Comparator<Priority>
313 {
314 @Override
315 public int compare( Priority o, Priority o1 )
316 {
317 return o.getMinSuccessRate() - o1.getMinSuccessRate();
318 }
319 }
320 }