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