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