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 import org.apache.maven.surefire.report.ConsoleLogger;
26 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
27 import org.apache.maven.surefire.report.ReportEntry;
28 import org.apache.maven.surefire.report.RunListener;
29 import org.apache.maven.surefire.report.SafeThrowable;
30 import org.apache.maven.surefire.report.StackTraceWriter;
31 import org.apache.maven.surefire.util.internal.ByteBuffer;
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 = new Integer( 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 * 6 + 1];
173 int i = StringUtils.escapeJavaStyleString( 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 byte[] buf = message.getBytes();
216 ByteBuffer byteBuffer = new ByteBuffer( 7 + buf.length * 6 );
217 byteBuffer.append( BOOTERCODE_CONSOLE );
218 byteBuffer.comma();
219 byteBuffer.append( testSetChannelId );
220 byteBuffer.comma();
221 final int i =
222 StringUtils.escapeJavaStyleString( byteBuffer.getData(), byteBuffer.getlength(), buf, 0, buf.length );
223 byteBuffer.advance( i );
224 byteBuffer.append( '\n' );
225 synchronized ( target )
226 {
227 target.write( byteBuffer.getData(), 0, byteBuffer.getlength() );
228 target.flush();
229 }
230 }
231
232 private String toPropertyString( String key, String value )
233 {
234 StringBuffer stringBuffer = new StringBuffer();
235 append( stringBuffer, BOOTERCODE_SYSPROPS );
236 comma( stringBuffer );
237 append( stringBuffer, Integer.toHexString( testSetChannelId.intValue() ) );
238 comma( stringBuffer );
239 StringUtils.escapeJavaStyleString( stringBuffer, key );
240 append( stringBuffer, "," );
241 StringUtils.escapeJavaStyleString( stringBuffer, value );
242 stringBuffer.append( "\n" );
243 return stringBuffer.toString();
244 }
245
246 private String toString( byte operationCode, ReportEntry reportEntry, Integer testSetChannelId )
247 {
248 StringBuffer stringBuffer = new StringBuffer();
249 append( stringBuffer, operationCode );
250 comma( stringBuffer );
251 append( stringBuffer, Integer.toHexString( testSetChannelId.intValue() ) );
252 comma( stringBuffer );
253 nullableEncoding( stringBuffer, reportEntry.getSourceName() );
254 comma( stringBuffer );
255 nullableEncoding( stringBuffer, reportEntry.getName() );
256 comma( stringBuffer );
257 nullableEncoding( stringBuffer, reportEntry.getGroup() );
258 comma( stringBuffer );
259 nullableEncoding( stringBuffer, reportEntry.getMessage() );
260 comma( stringBuffer );
261 nullableEncoding( stringBuffer, reportEntry.getElapsed() );
262 encode( stringBuffer, reportEntry.getStackTraceWriter() );
263 stringBuffer.append( "\n" );
264 return stringBuffer.toString();
265 }
266
267 private static void comma( StringBuffer stringBuffer )
268 {
269 stringBuffer.append( "," );
270 }
271
272 private ForkingRunListener append( StringBuffer stringBuffer, String message )
273 {
274 stringBuffer.append( encode( message ) );
275 return this;
276 }
277
278 private ForkingRunListener append( StringBuffer stringBuffer, byte b )
279 {
280 stringBuffer.append( (char) b );
281 return this;
282 }
283
284 private void nullableEncoding( StringBuffer stringBuffer, Integer source )
285 {
286 if ( source == null )
287 {
288 stringBuffer.append( "null" );
289 }
290 else
291 {
292 stringBuffer.append( source.toString() );
293 }
294 }
295
296 private String encode( String source )
297 {
298 return source;
299 }
300
301
302 private static void nullableEncoding( StringBuffer stringBuffer, String source )
303 {
304 if ( source == null || source.length() == 0 )
305 {
306 stringBuffer.append( "null" );
307 }
308 else
309 {
310 StringUtils.escapeJavaStyleString( stringBuffer, source );
311 }
312 }
313
314 private void encode( StringBuffer stringBuffer, StackTraceWriter stackTraceWriter )
315 {
316 encode( stringBuffer, stackTraceWriter, trimStackTraces );
317 }
318
319 public static void encode( StringBuffer stringBuffer, StackTraceWriter stackTraceWriter, boolean trimStackTraces )
320 {
321 if ( stackTraceWriter != null )
322 {
323 comma( stringBuffer );
324
325 final SafeThrowable throwable = stackTraceWriter.getThrowable();
326 if ( throwable != null )
327 {
328 String message = throwable.getLocalizedMessage();
329 nullableEncoding( stringBuffer, message );
330 }
331 comma( stringBuffer );
332 nullableEncoding( stringBuffer, stackTraceWriter.smartTrimmedStackTrace() );
333 comma( stringBuffer );
334 nullableEncoding( stringBuffer, trimStackTraces
335 ? stackTraceWriter.writeTrimmedTraceToString()
336 : stackTraceWriter.writeTraceToString() );
337 }
338 }
339 }