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