1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.surefire.report;
20
21 import java.io.BufferedOutputStream;
22 import java.io.File;
23 import java.io.FilterOutputStream;
24 import java.io.IOException;
25 import java.io.UncheckedIOException;
26 import java.nio.charset.Charset;
27 import java.nio.file.Files;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30
31 import org.apache.maven.plugin.surefire.booterclient.output.InPluginProcessDumpSingleton;
32 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
33 import org.apache.maven.surefire.api.report.TestSetReportEntry;
34
35 import static org.apache.maven.plugin.surefire.report.FileReporter.getReportFile;
36 import static org.apache.maven.surefire.api.util.internal.StringUtils.NL;
37
38
39
40
41
42
43
44 public class ConsoleOutputFileReporter implements TestcycleConsoleOutputReceiver {
45 private static final int STREAM_BUFFER_SIZE = 64 * 1024;
46
47 private final File reportsDirectory;
48 private final String reportNameSuffix;
49 private final boolean usePhrasedFileName;
50 private final Integer forkNumber;
51 private final String encoding;
52
53 private final Map<String, FilterOutputStream> outputStreams = new ConcurrentHashMap<>();
54
55 private volatile String reportEntryName;
56
57 public ConsoleOutputFileReporter(
58 File reportsDirectory,
59 String reportNameSuffix,
60 boolean usePhrasedFileName,
61 Integer forkNumber,
62 String encoding) {
63 this.reportsDirectory = reportsDirectory;
64 this.reportNameSuffix = reportNameSuffix;
65 this.usePhrasedFileName = usePhrasedFileName;
66 this.forkNumber = forkNumber;
67 this.encoding = encoding;
68 }
69
70 @Override
71 public void testSetStarting(TestSetReportEntry reportEntry) {
72 String className = usePhrasedFileName ? reportEntry.getSourceText() : reportEntry.getSourceName();
73 try {
74 File file = getReportFile(reportsDirectory, className, reportNameSuffix, "-output.txt");
75 if (!reportsDirectory.exists()) {
76 Files.createDirectories(reportsDirectory.toPath());
77 }
78 if (!Files.exists(file.toPath())) {
79 Files.createFile(file.toPath());
80 }
81 outputStreams.put(
82 className, new BufferedOutputStream(Files.newOutputStream(file.toPath()), STREAM_BUFFER_SIZE));
83 } catch (IOException e) {
84 throw new RuntimeException(e);
85 }
86 }
87
88 @Override
89 public void testSetCompleted(TestSetReportEntry report) {}
90
91 @Override
92 public void close() {
93
94 for (FilterOutputStream stream : outputStreams.values()) {
95 try {
96 stream.close();
97 } catch (IOException e) {
98 dumpException(e);
99 }
100 }
101 }
102
103 @Override
104 public synchronized void writeTestOutput(TestOutputReportEntry reportEntry) {
105 try {
106
107 String targetClassName = extractTestClassFromStack(reportEntry.getStack());
108 if (targetClassName == null) {
109 targetClassName = reportEntryName;
110 }
111
112 if (targetClassName == null) {
113 targetClassName = "null";
114 }
115
116
117 FilterOutputStream os = outputStreams.computeIfAbsent(targetClassName, className -> {
118 try {
119 if (!reportsDirectory.exists()) {
120
121 reportsDirectory.mkdirs();
122 }
123 File file = getReportFile(reportsDirectory, className, reportNameSuffix, "-output.txt");
124 return new BufferedOutputStream(Files.newOutputStream(file.toPath()), STREAM_BUFFER_SIZE);
125 } catch (IOException e) {
126 dumpException(e);
127 throw new UncheckedIOException(e);
128 }
129 });
130
131 String output = reportEntry.getLog();
132 if (output == null) {
133 output = "null";
134 }
135 Charset charset = Charset.forName(encoding);
136 os.write(output.getBytes(charset));
137 if (reportEntry.isNewLine()) {
138 os.write(NL.getBytes(charset));
139 }
140 } catch (IOException e) {
141 dumpException(e);
142 throw new UncheckedIOException(e);
143 }
144 }
145
146
147
148
149
150
151 private String extractTestClassFromStack(String stack) {
152 if (stack == null || stack.isEmpty()) {
153 return null;
154 }
155
156
157 String[] entries = stack.split(";");
158 for (String entry : entries) {
159 int hashIndex = entry.indexOf('#');
160 if (hashIndex > 0) {
161 String className = entry.substring(0, hashIndex);
162 if (outputStreams.containsKey(className)) {
163 return className;
164 }
165 }
166 }
167 return null;
168 }
169
170 private void dumpException(IOException e) {
171 if (forkNumber == null) {
172 InPluginProcessDumpSingleton.getSingleton().dumpException(e, e.getLocalizedMessage(), reportsDirectory);
173 } else {
174 InPluginProcessDumpSingleton.getSingleton()
175 .dumpException(e, e.getLocalizedMessage(), reportsDirectory, forkNumber);
176 }
177 }
178 }