1 package org.apache.maven.plugin.surefire.booterclient.output;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.BufferedReader;
23 import java.io.IOException;
24 import java.io.StringReader;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.NoSuchElementException;
29 import java.util.Properties;
30 import java.util.StringTokenizer;
31
32 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream;
33 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
34 import org.apache.maven.shared.utils.cli.StreamConsumer;
35 import org.apache.maven.surefire.booter.ForkingRunListener;
36 import org.apache.maven.surefire.report.CategorizedReportEntry;
37 import org.apache.maven.surefire.report.ConsoleLogger;
38 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
39 import org.apache.maven.surefire.report.ReportEntry;
40 import org.apache.maven.surefire.report.ReporterException;
41 import org.apache.maven.surefire.report.RunListener;
42 import org.apache.maven.surefire.report.StackTraceWriter;
43 import org.apache.maven.surefire.util.internal.StringUtils;
44
45
46
47
48
49
50 public class ForkClient
51 implements StreamConsumer
52 {
53
54 private final DefaultReporterFactory defaultReporterFactory;
55
56 private final TestProvidingInputStream testProvidingInputStream;
57
58 private final Map<Integer, RunListener> testSetReporters =
59 Collections.synchronizedMap( new HashMap<Integer, RunListener>() );
60
61 private final Properties testVmSystemProperties;
62
63 private volatile boolean saidGoodBye = false;
64
65 private volatile StackTraceWriter errorInFork = null;
66
67 public ForkClient( DefaultReporterFactory defaultReporterFactory, Properties testVmSystemProperties )
68 {
69 this( defaultReporterFactory, testVmSystemProperties, null );
70 }
71
72 public ForkClient( DefaultReporterFactory defaultReporterFactory, Properties testVmSystemProperties,
73 TestProvidingInputStream testProvidingInputStream )
74 {
75 this.defaultReporterFactory = defaultReporterFactory;
76 this.testVmSystemProperties = testVmSystemProperties;
77 this.testProvidingInputStream = testProvidingInputStream;
78 }
79
80 public DefaultReporterFactory getDefaultReporterFactory()
81 {
82 return defaultReporterFactory;
83 }
84
85 public void consumeLine( String s )
86 {
87 try
88 {
89 if ( s.length() == 0 )
90 {
91 return;
92 }
93 final byte operationId = (byte) s.charAt( 0 );
94 int commma = s.indexOf( ",", 3 );
95 if ( commma < 0 )
96 {
97 System.out.println( s );
98 return;
99 }
100 final Integer channelNumber = Integer.parseInt( s.substring( 2, commma ), 16 );
101 int rest = s.indexOf( ",", commma );
102 final String remaining = s.substring( rest + 1 );
103
104 switch ( operationId )
105 {
106 case ForkingRunListener.BOOTERCODE_TESTSET_STARTING:
107 getOrCreateReporter( channelNumber ).testSetStarting( createReportEntry( remaining ) );
108 break;
109 case ForkingRunListener.BOOTERCODE_TESTSET_COMPLETED:
110 getOrCreateReporter( channelNumber ).testSetCompleted( createReportEntry( remaining ) );
111 break;
112 case ForkingRunListener.BOOTERCODE_TEST_STARTING:
113 getOrCreateReporter( channelNumber ).testStarting( createReportEntry( remaining ) );
114 break;
115 case ForkingRunListener.BOOTERCODE_TEST_SUCCEEDED:
116 getOrCreateReporter( channelNumber ).testSucceeded( createReportEntry( remaining ) );
117 break;
118 case ForkingRunListener.BOOTERCODE_TEST_FAILED:
119 getOrCreateReporter( channelNumber ).testFailed( createReportEntry( remaining ) );
120 break;
121 case ForkingRunListener.BOOTERCODE_TEST_SKIPPED:
122 getOrCreateReporter( channelNumber ).testSkipped( createReportEntry( remaining ) );
123 break;
124 case ForkingRunListener.BOOTERCODE_TEST_ERROR:
125 getOrCreateReporter( channelNumber ).testError( createReportEntry( remaining ) );
126 break;
127 case ForkingRunListener.BOOTERCODE_TEST_ASSUMPTIONFAILURE:
128 getOrCreateReporter( channelNumber ).testAssumptionFailure( createReportEntry( remaining ) );
129 break;
130 case ForkingRunListener.BOOTERCODE_SYSPROPS:
131 int keyEnd = remaining.indexOf( "," );
132 StringBuilder key = new StringBuilder();
133 StringBuilder value = new StringBuilder();
134 StringUtils.unescapeString( key, remaining.substring( 0, keyEnd ) );
135 StringUtils.unescapeString( value, remaining.substring( keyEnd + 1 ) );
136
137 synchronized ( testVmSystemProperties )
138 {
139 testVmSystemProperties.put( key.toString(), value.toString() );
140 }
141 break;
142 case ForkingRunListener.BOOTERCODE_STDOUT:
143 byte[] bytes = new byte[remaining.length()];
144 int len = StringUtils.unescapeBytes( bytes, remaining );
145 getOrCreateConsoleOutputReceiver( channelNumber ).writeTestOutput( bytes, 0, len, true );
146 break;
147 case ForkingRunListener.BOOTERCODE_STDERR:
148 bytes = new byte[remaining.length()];
149 len = StringUtils.unescapeBytes( bytes, remaining );
150 getOrCreateConsoleOutputReceiver( channelNumber ).writeTestOutput( bytes, 0, len, false );
151 break;
152 case ForkingRunListener.BOOTERCODE_CONSOLE:
153 getOrCreateConsoleLogger( channelNumber ).info( createConsoleMessage( remaining ) );
154 break;
155 case ForkingRunListener.BOOTERCODE_NEXT_TEST:
156 if ( null != testProvidingInputStream )
157 {
158 testProvidingInputStream.provideNewTest();
159 }
160 break;
161 case ForkingRunListener.BOOTERCODE_ERROR:
162 errorInFork = deserializeStackStraceWriter( new StringTokenizer( remaining, "," ) );
163 break;
164 case ForkingRunListener.BOOTERCODE_BYE:
165 saidGoodBye = true;
166 break;
167 default:
168 System.out.println( s );
169 }
170 }
171 catch ( NumberFormatException e )
172 {
173 System.out.println( "SUREFIRE-859: " + s );
174 }
175 catch ( NoSuchElementException e )
176 {
177 System.out.println( "SUREFIRE-859: " + s );
178 }
179 catch ( ReporterException e )
180 {
181 throw new RuntimeException( e );
182 }
183 }
184
185 public void consumeMultiLineContent( String s )
186 throws IOException
187 {
188 BufferedReader stringReader = new BufferedReader( new StringReader( s ) );
189 String s1;
190 while ( ( s1 = stringReader.readLine() ) != null )
191 {
192 consumeLine( s1 );
193 }
194 }
195
196 private String createConsoleMessage( String remaining )
197 {
198 return unescape( remaining );
199 }
200
201 private ReportEntry createReportEntry( String untokenized )
202 {
203 StringTokenizer tokens = new StringTokenizer( untokenized, "," );
204 try
205 {
206 String source = nullableCsv( tokens.nextToken() );
207 String name = nullableCsv( tokens.nextToken() );
208 String group = nullableCsv( tokens.nextToken() );
209 String message = nullableCsv( tokens.nextToken() );
210 String elapsedStr = tokens.nextToken();
211 Integer elapsed = "null".equals( elapsedStr ) ? null : Integer.decode( elapsedStr );
212 final StackTraceWriter stackTraceWriter =
213 tokens.hasMoreTokens() ? deserializeStackStraceWriter( tokens ) : null;
214
215 return CategorizedReportEntry.reportEntry( source, name, group, stackTraceWriter, elapsed, message );
216 }
217 catch ( RuntimeException e )
218 {
219 throw new RuntimeException( untokenized, e );
220 }
221 }
222
223 private StackTraceWriter deserializeStackStraceWriter( StringTokenizer tokens )
224 {
225 StackTraceWriter stackTraceWriter;
226 String stackTraceMessage = nullableCsv( tokens.nextToken() );
227 String smartStackTrace = nullableCsv( tokens.nextToken() );
228 String stackTrace = tokens.hasMoreTokens() ? nullableCsv( tokens.nextToken() ) : null;
229 stackTraceWriter =
230 stackTrace != null ? new DeserializedStacktraceWriter( stackTraceMessage, smartStackTrace, stackTrace )
231 : null;
232 return stackTraceWriter;
233 }
234
235 private String nullableCsv( String source )
236 {
237 if ( "null".equals( source ) )
238 {
239 return null;
240 }
241 return unescape( source );
242 }
243
244 private String unescape( String source )
245 {
246 StringBuilder stringBuffer = new StringBuilder( source.length() );
247
248 StringUtils.unescapeString( stringBuffer, source );
249 return stringBuffer.toString();
250 }
251
252
253
254
255
256
257
258 public RunListener getReporter( Integer channelNumber )
259 {
260 return testSetReporters.get( channelNumber );
261 }
262
263 private RunListener getOrCreateReporter( Integer channelNumber )
264 {
265 RunListener reporter = testSetReporters.get( channelNumber );
266 if ( reporter == null )
267 {
268 reporter = defaultReporterFactory.createReporter();
269 testSetReporters.put( channelNumber, reporter );
270 }
271 return reporter;
272 }
273
274 private ConsoleOutputReceiver getOrCreateConsoleOutputReceiver( Integer channelNumber )
275 {
276 return (ConsoleOutputReceiver) getOrCreateReporter( channelNumber );
277 }
278
279 private ConsoleLogger getOrCreateConsoleLogger( Integer channelNumber )
280 {
281 return (ConsoleLogger) getOrCreateReporter( channelNumber );
282 }
283
284 public void close( boolean hadTimeout )
285 {
286 }
287
288 public boolean isSaidGoodBye()
289 {
290 return saidGoodBye;
291 }
292
293 public StackTraceWriter getErrorInFork()
294 {
295 return errorInFork;
296 }
297
298 public boolean isErrorInFork()
299 {
300 return errorInFork != null;
301 }
302 }