1 package org.apache.maven.surefire.booter;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.PrintStream;
23 import java.util.Enumeration;
24 import java.util.Properties;
25
26 import org.apache.maven.surefire.report.ConsoleLogger;
27 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
28 import org.apache.maven.surefire.report.ReportEntry;
29 import org.apache.maven.surefire.report.RunListener;
30 import org.apache.maven.surefire.report.SafeThrowable;
31 import org.apache.maven.surefire.report.StackTraceWriter;
32 import org.apache.maven.surefire.util.internal.StringUtils;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 public class ForkingRunListener
52 implements RunListener, ConsoleLogger, ConsoleOutputReceiver
53 {
54 public static final byte BOOTERCODE_TESTSET_STARTING = (byte) '1';
55
56 public static final byte BOOTERCODE_TESTSET_COMPLETED = (byte) '2';
57
58 public static final byte BOOTERCODE_STDOUT = (byte) '3';
59
60 public static final byte BOOTERCODE_STDERR = (byte) '4';
61
62 public static final byte BOOTERCODE_TEST_STARTING = (byte) '5';
63
64 public static final byte BOOTERCODE_TEST_SUCCEEDED = (byte) '6';
65
66 public static final byte BOOTERCODE_TEST_ERROR = (byte) '7';
67
68 public static final byte BOOTERCODE_TEST_FAILED = (byte) '8';
69
70 public static final byte BOOTERCODE_TEST_SKIPPED = (byte) '9';
71
72 public static final byte BOOTERCODE_TEST_ASSUMPTIONFAILURE = (byte) 'G';
73
74 public static final byte BOOTERCODE_CONSOLE = (byte) 'H';
75
76 public static final byte BOOTERCODE_SYSPROPS = (byte) 'I';
77
78 public static final byte BOOTERCODE_NEXT_TEST = (byte) 'N';
79
80 public static final byte BOOTERCODE_ERROR = (byte) 'X';
81
82 public static final byte BOOTERCODE_BYE = (byte) 'Z';
83
84
85 private final PrintStream target;
86
87 private final Integer testSetChannelId;
88
89 private final boolean trimStackTraces;
90
91 private final byte[] stdOutHeader;
92
93 private final byte[] stdErrHeader;
94
95 public ForkingRunListener( PrintStream target, int testSetChannelId, boolean trimStackTraces )
96 {
97 this.target = target;
98 this.testSetChannelId = testSetChannelId;
99 this.trimStackTraces = trimStackTraces;
100 stdOutHeader = createHeader( BOOTERCODE_STDOUT, testSetChannelId );
101 stdErrHeader = createHeader( BOOTERCODE_STDERR, testSetChannelId );
102 sendProps();
103 }
104
105 public void testSetStarting( ReportEntry report )
106 {
107 target.print( toString( BOOTERCODE_TESTSET_STARTING, report, testSetChannelId ) );
108 }
109
110 public void testSetCompleted( ReportEntry report )
111 {
112 target.print( toString( BOOTERCODE_TESTSET_COMPLETED, report, testSetChannelId ) );
113 }
114
115 public void testStarting( ReportEntry report )
116 {
117 target.print( toString( BOOTERCODE_TEST_STARTING, report, testSetChannelId ) );
118 }
119
120 public void testSucceeded( ReportEntry report )
121 {
122 target.print( toString( BOOTERCODE_TEST_SUCCEEDED, report, testSetChannelId ) );
123 }
124
125 public void testAssumptionFailure( ReportEntry report )
126 {
127 target.print( toString( BOOTERCODE_TEST_ASSUMPTIONFAILURE, report, testSetChannelId ) );
128 }
129
130 public void testError( ReportEntry report )
131 {
132 target.print( toString( BOOTERCODE_TEST_ERROR, report, testSetChannelId ) );
133 }
134
135 public void testFailed( ReportEntry report )
136 {
137 target.print( toString( BOOTERCODE_TEST_FAILED, report, testSetChannelId ) );
138 }
139
140 public void testSkipped( ReportEntry report )
141 {
142 target.print( toString( BOOTERCODE_TEST_SKIPPED, report, testSetChannelId ) );
143 }
144
145 void sendProps()
146 {
147 Properties systemProperties = System.getProperties();
148
149 if ( systemProperties != null )
150 {
151 Enumeration propertyKeys = systemProperties.propertyNames();
152
153 while ( propertyKeys.hasMoreElements() )
154 {
155 String key = (String) propertyKeys.nextElement();
156
157 String value = systemProperties.getProperty( key );
158
159 if ( value == null )
160 {
161 value = "null";
162 }
163 target.print( toPropertyString( key, value ) );
164 }
165 }
166 }
167
168 public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
169 {
170 byte[] header = stdout ? stdOutHeader : stdErrHeader;
171 byte[] content =
172 new byte[buf.length * 3 + 1];
173 int i = StringUtils.escapeBytesToPrintable( content, 0, buf, off, len );
174 content[i++] = (byte) '\n';
175
176 synchronized ( target )
177 {
178 target.write( header, 0, header.length );
179 target.write( content, 0, i );
180 }
181 }
182
183 public static byte[] createHeader( byte booterCode, int testSetChannel )
184 {
185 byte[] header = new byte[7];
186 header[0] = booterCode;
187 header[1] = (byte) ',';
188 header[6] = (byte) ',';
189
190 int i = testSetChannel;
191 int charPos = 6;
192 int radix = 1 << 4;
193 int mask = radix - 1;
194 do
195 {
196 header[--charPos] = (byte) digits[i & mask];
197 i >>>= 4;
198 }
199 while ( i != 0 );
200
201 while ( charPos > 2 )
202 {
203 header[--charPos] = (byte) '0';
204 }
205 return header;
206 }
207
208 private final static char[] digits =
209 { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
210 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
211
212
213 public void info( String message )
214 {
215 if ( message == null )
216 {
217 return;
218 }
219
220 StringBuilder sb = new StringBuilder( 7 + message.length() * 5 );
221 append( sb, BOOTERCODE_CONSOLE );comma( sb );
222 append( sb, Integer.toHexString( testSetChannelId ) );comma( sb );
223 StringUtils.escapeToPrintable( sb, message );
224
225 sb.append( '\n' );
226 target.print( sb.toString() );
227 }
228
229 private String toPropertyString( String key, String value )
230 {
231 StringBuilder stringBuilder = new StringBuilder();
232
233 append( stringBuilder, BOOTERCODE_SYSPROPS );comma( stringBuilder );
234 append( stringBuilder, Integer.toHexString( testSetChannelId ) );comma( stringBuilder );
235
236 StringUtils.escapeToPrintable( stringBuilder, key );
237 comma( stringBuilder );
238 StringUtils.escapeToPrintable( stringBuilder, value );
239 stringBuilder.append( "\n" );
240 return stringBuilder.toString();
241 }
242
243 private String toString( byte operationCode, ReportEntry reportEntry, Integer testSetChannelId )
244 {
245 StringBuilder stringBuilder = new StringBuilder();
246 append( stringBuilder, operationCode ); comma( stringBuilder );
247 append( stringBuilder, Integer.toHexString( testSetChannelId ) );comma( stringBuilder );
248
249 nullableEncoding( stringBuilder, reportEntry.getSourceName() );
250 comma( stringBuilder );
251 nullableEncoding( stringBuilder, reportEntry.getName() );
252 comma( stringBuilder );
253 nullableEncoding( stringBuilder, reportEntry.getGroup() );
254 comma( stringBuilder );
255 nullableEncoding( stringBuilder, reportEntry.getMessage() );
256 comma( stringBuilder );
257 nullableEncoding( stringBuilder, reportEntry.getElapsed() );
258 encode( stringBuilder, reportEntry.getStackTraceWriter() );
259 stringBuilder.append( "\n" );
260 return stringBuilder.toString();
261 }
262
263 private static void comma( StringBuilder stringBuilder )
264 {
265 stringBuilder.append( "," );
266 }
267
268 private ForkingRunListener append( StringBuilder stringBuilder, String message )
269 {
270 stringBuilder.append( encode( message ) );
271 return this;
272 }
273
274 private ForkingRunListener append( StringBuilder stringBuilder, byte b )
275 {
276 stringBuilder.append( (char) b );
277 return this;
278 }
279
280 private void nullableEncoding( StringBuilder stringBuilder, Integer source )
281 {
282 if ( source == null )
283 {
284 stringBuilder.append( "null" );
285 }
286 else
287 {
288 stringBuilder.append( source.toString() );
289 }
290 }
291
292 private String encode( String source )
293 {
294 return source;
295 }
296
297
298 private static void nullableEncoding( StringBuilder stringBuilder, String source )
299 {
300 if ( source == null || source.length() == 0 )
301 {
302 stringBuilder.append( "null" );
303 }
304 else
305 {
306 StringUtils.escapeToPrintable( stringBuilder, source );
307 }
308 }
309
310 private void encode( StringBuilder stringBuilder, StackTraceWriter stackTraceWriter )
311 {
312 encode( stringBuilder, stackTraceWriter, trimStackTraces );
313 }
314
315 public static void encode( StringBuilder stringBuilder, StackTraceWriter stackTraceWriter, boolean trimStackTraces )
316 {
317 if ( stackTraceWriter != null )
318 {
319 comma( stringBuilder );
320
321 final SafeThrowable throwable = stackTraceWriter.getThrowable();
322 if ( throwable != null )
323 {
324 String message = throwable.getLocalizedMessage();
325 nullableEncoding( stringBuilder, message );
326 }
327 comma( stringBuilder );
328 nullableEncoding( stringBuilder, stackTraceWriter.smartTrimmedStackTrace() );
329 comma( stringBuilder );
330 nullableEncoding( stringBuilder, trimStackTraces
331 ? stackTraceWriter.writeTrimmedTraceToString()
332 : stackTraceWriter.writeTraceToString() );
333 }
334 }
335 }