1 package org.apache.maven.plugin.surefire.report;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import junit.framework.TestCase;
23 import org.apache.maven.plugin.surefire.booterclient.output.DeserializedStacktraceWriter;
24 import org.apache.maven.surefire.api.report.ReportEntry;
25 import org.apache.maven.surefire.api.report.SimpleReportEntry;
26 import org.apache.maven.surefire.api.report.StackTraceWriter;
27 import org.apache.maven.surefire.shared.utils.xml.Xpp3Dom;
28 import org.apache.maven.surefire.shared.utils.xml.Xpp3DomBuilder;
29
30 import java.io.ByteArrayOutputStream;
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.io.IOException;
34 import java.io.InputStreamReader;
35 import java.io.RandomAccessFile;
36 import java.nio.Buffer;
37 import java.nio.ByteBuffer;
38 import java.nio.file.Path;
39 import java.util.Deque;
40 import java.util.HashMap;
41 import java.util.concurrent.ConcurrentHashMap;
42 import java.util.concurrent.atomic.AtomicInteger;
43
44 import static java.nio.charset.StandardCharsets.UTF_8;
45 import static java.nio.file.Files.readAllLines;
46 import static org.apache.maven.plugin.surefire.report.ReportEntryType.ERROR;
47 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SKIPPED;
48 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SUCCESS;
49 import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
50 import static org.apache.maven.surefire.api.report.RunMode.RERUN_TEST_AFTER_FAILURE;
51 import static org.apache.maven.surefire.api.util.internal.ObjectUtils.systemProps;
52 import static org.apache.maven.surefire.shared.utils.StringUtils.isEmpty;
53 import static org.assertj.core.api.Assertions.assertThat;
54 import static org.mockito.Mockito.doThrow;
55 import static org.mockito.Mockito.mock;
56 import static org.mockito.Mockito.times;
57 import static org.mockito.Mockito.verify;
58 import static org.mockito.Mockito.when;
59 import static org.powermock.reflect.Whitebox.getInternalState;
60 import static org.powermock.reflect.Whitebox.invokeMethod;
61 import static org.powermock.reflect.Whitebox.setInternalState;
62 import static org.apache.maven.surefire.api.util.internal.StringUtils.NL;
63
64
65
66
67 @SuppressWarnings( { "ResultOfMethodCallIgnored", "checkstyle:magicnumber" } )
68 public class StatelessXmlReporterTest
69 extends TestCase
70 {
71 private static final String XSD =
72 "https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report-3.0.xsd";
73 private static final String TEST_ONE = "aTestMethod";
74 private static final String TEST_TWO = "bTestMethod";
75 private static final String TEST_THREE = "cTestMethod";
76 private static final AtomicInteger FOLDER_POSTFIX = new AtomicInteger();
77
78 private TestSetStats stats;
79 private TestSetStats rerunStats;
80 private File expectedReportFile;
81 private File reportDir;
82
83 @Override
84 protected void setUp()
85 throws Exception
86 {
87 stats = new TestSetStats( false, true );
88 rerunStats = new TestSetStats( false, true );
89
90 File basedir = new File( "." );
91 File target = new File( basedir.getCanonicalFile(), "target" );
92 target.mkdir();
93 String reportRelDir = getClass().getSimpleName() + "-" + FOLDER_POSTFIX.incrementAndGet();
94 reportDir = new File( target, reportRelDir );
95 reportDir.mkdir();
96 }
97
98 @Override
99 protected void tearDown()
100 {
101 if ( expectedReportFile != null )
102 {
103 expectedReportFile.delete();
104 }
105 }
106
107 public void testFileNameWithoutSuffix()
108 {
109 StatelessXmlReporter reporter =
110 new StatelessXmlReporter( reportDir, null, false, 0,
111 new ConcurrentHashMap<String, Deque<WrappedReportEntry>>(), XSD, "3.0",
112 false, false, false, false );
113 reporter.cleanTestHistoryMap();
114
115 ReportEntry reportEntry = new SimpleReportEntry( NORMAL_RUN, 0L,
116 getClass().getName(), null, getClass().getName(), null, 12 );
117 WrappedReportEntry testSetReportEntry = new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS,
118 12, null, null, systemProps() );
119 stats.testSucceeded( testSetReportEntry );
120 reporter.testSetCompleted( testSetReportEntry, stats );
121
122 expectedReportFile = new File( reportDir, "TEST-" + getClass().getName() + ".xml" );
123 assertTrue( "Report file (" + expectedReportFile.getAbsolutePath() + ") doesn't exist",
124 expectedReportFile.exists() );
125 }
126
127
128 public void testAllFieldsSerialized()
129 throws IOException
130 {
131 ReportEntry reportEntry = new SimpleReportEntry( NORMAL_RUN, 0L,
132 getClass().getName(), null, TEST_ONE, null, 12 );
133 WrappedReportEntry testSetReportEntry =
134 new WrappedReportEntry( reportEntry, SUCCESS, 12, null, null, systemProps() );
135 expectedReportFile = new File( reportDir, "TEST-" + getClass().getName() + ".xml" );
136
137 stats.testSucceeded( testSetReportEntry );
138 StackTraceWriter stackTraceWriter = new DeserializedStacktraceWriter( "A fud msg", "trimmed", "fail at foo" );
139 Utf8RecodingDeferredFileOutputStream stdOut = new Utf8RecodingDeferredFileOutputStream( "fds" );
140 String stdOutPrefix;
141 String stdErrPrefix;
142 if ( defaultCharsetSupportsSpecialChar() )
143 {
144 stdErrPrefix = "std-\u0115rr";
145 stdOutPrefix = "st]]>d-o\u00DCt";
146 }
147 else
148 {
149 stdErrPrefix = "std-err";
150 stdOutPrefix = "st]]>d-out";
151 }
152
153 stdOut.write( stdOutPrefix + "<null>!\u0020\u0000\u001F", false );
154
155 Utf8RecodingDeferredFileOutputStream stdErr = new Utf8RecodingDeferredFileOutputStream( "fds" );
156
157 stdErr.write( stdErrPrefix + "?&-&£\u0020\u0000\u001F", false );
158 WrappedReportEntry t2 = new WrappedReportEntry( new SimpleReportEntry( NORMAL_RUN, 0L,
159 getClass().getName(), null, TEST_TWO, null, stackTraceWriter, 13 ),
160 ReportEntryType.ERROR, 13, stdOut, stdErr );
161
162 stats.testSucceeded( t2 );
163 StatelessXmlReporter reporter = new StatelessXmlReporter( reportDir, null, false, 0,
164 new ConcurrentHashMap<String, Deque<WrappedReportEntry>>(), XSD, "3.0", false, false, false, false );
165 reporter.testSetCompleted( testSetReportEntry, stats );
166
167 FileInputStream fileInputStream = new FileInputStream( expectedReportFile );
168
169 Xpp3Dom testSuite = Xpp3DomBuilder.build( new InputStreamReader( fileInputStream, UTF_8 ) );
170 assertEquals( "testsuite", testSuite.getName() );
171 Xpp3Dom properties = testSuite.getChild( "properties" );
172 assertEquals( System.getProperties().size(), properties.getChildCount() );
173 Xpp3Dom child = properties.getChild( 1 );
174 assertFalse( isEmpty( child.getAttribute( "value" ) ) );
175 assertFalse( isEmpty( child.getAttribute( "name" ) ) );
176
177 Xpp3Dom[] testcase = testSuite.getChildren( "testcase" );
178 Xpp3Dom tca = testcase[0];
179 assertEquals( TEST_ONE, tca.getAttribute( "name" ) );
180 assertEquals( "0.012", tca.getAttribute( "time" ) );
181 assertEquals( getClass().getName(), tca.getAttribute( "classname" ) );
182
183 Xpp3Dom tcb = testcase[1];
184 assertEquals( TEST_TWO, tcb.getAttribute( "name" ) );
185 assertEquals( "0.013", tcb.getAttribute( "time" ) );
186 assertEquals( getClass().getName(), tcb.getAttribute( "classname" ) );
187 Xpp3Dom errorNode = tcb.getChild( "error" );
188 assertNotNull( errorNode );
189 assertEquals( "A fud msg", errorNode.getAttribute( "message" ) );
190 assertEquals( "fail at foo", errorNode.getAttribute( "type" ) );
191 assertEquals( stdOutPrefix + "<null>! &#0;&#31;", tcb.getChild( "system-out" ).getValue() );
192
193
194 assertEquals( stdErrPrefix + "?&-&£ &#0;&#31;", tcb.getChild( "system-err" ).getValue() );
195 }
196
197 public void testOutputRerunFlakyFailure()
198 throws IOException
199 {
200 WrappedReportEntry testSetReportEntry =
201 new WrappedReportEntry( new SimpleReportEntry( NORMAL_RUN, 0L,
202 getClass().getName(), null, TEST_ONE, null, 12 ),
203 ReportEntryType.SUCCESS, 12, null, null, systemProps() );
204 expectedReportFile = new File( reportDir, "TEST-" + getClass().getName() + ".xml" );
205
206 stats.testSucceeded( testSetReportEntry );
207 StackTraceWriter stackTraceWriterOne = new DeserializedStacktraceWriter( "A fud msg", "trimmed",
208 "fail at foo" );
209 StackTraceWriter stackTraceWriterTwo =
210 new DeserializedStacktraceWriter( "A fud msg two", "trimmed two", "fail at foo two" );
211
212 String firstRunOut = "first run out";
213 String firstRunErr = "first run err";
214 String secondRunOut = "second run out";
215 String secondRunErr = "second run err";
216
217 String cls = getClass().getName();
218 WrappedReportEntry testTwoFirstError = new WrappedReportEntry( new SimpleReportEntry( NORMAL_RUN, 0L,
219 cls, null, TEST_TWO, null, stackTraceWriterOne, 5 ),
220 ReportEntryType.ERROR, 5, createStdOutput( firstRunOut ), createStdOutput( firstRunErr ) );
221
222 WrappedReportEntry testTwoSecondError = new WrappedReportEntry( new SimpleReportEntry(
223 RERUN_TEST_AFTER_FAILURE, 1L, cls, null, TEST_TWO, null, stackTraceWriterTwo, 13 ),
224 ReportEntryType.ERROR, 13, createStdOutput( secondRunOut ), createStdOutput( secondRunErr ) );
225
226 WrappedReportEntry testThreeFirstRun = new WrappedReportEntry( new SimpleReportEntry( NORMAL_RUN, 2L,
227 cls, null, TEST_THREE, null, stackTraceWriterOne, 13 ),
228 ReportEntryType.FAILURE, 13, createStdOutput( firstRunOut ), createStdOutput( firstRunErr ) );
229
230 WrappedReportEntry testThreeSecondRun = new WrappedReportEntry( new SimpleReportEntry(
231 RERUN_TEST_AFTER_FAILURE, 3L, cls, null, TEST_THREE, null, stackTraceWriterTwo, 2 ),
232 ReportEntryType.SUCCESS, 2, createStdOutput( secondRunOut ), createStdOutput( secondRunErr ) );
233
234 stats.testSucceeded( testTwoFirstError );
235 stats.testSucceeded( testThreeFirstRun );
236 rerunStats.testSucceeded( testTwoSecondError );
237 rerunStats.testSucceeded( testThreeSecondRun );
238
239 StatelessXmlReporter reporter =
240 new StatelessXmlReporter( reportDir, null, false, 1,
241 new HashMap<String, Deque<WrappedReportEntry>>(), XSD, "3.0", false, false, false, false );
242
243 reporter.testSetCompleted( testSetReportEntry, stats );
244 reporter.testSetCompleted( testSetReportEntry, rerunStats );
245
246 FileInputStream fileInputStream = new FileInputStream( expectedReportFile );
247
248 Xpp3Dom testSuite = Xpp3DomBuilder.build( new InputStreamReader( fileInputStream, UTF_8 ) );
249 assertEquals( "testsuite", testSuite.getName() );
250 assertEquals( "0.012", testSuite.getAttribute( "time" ) );
251 Xpp3Dom properties = testSuite.getChild( "properties" );
252 assertEquals( System.getProperties().size(), properties.getChildCount() );
253 Xpp3Dom child = properties.getChild( 1 );
254 assertFalse( isEmpty( child.getAttribute( "value" ) ) );
255 assertFalse( isEmpty( child.getAttribute( "name" ) ) );
256
257 Xpp3Dom[] testcase = testSuite.getChildren( "testcase" );
258 Xpp3Dom testCaseOne = testcase[0];
259 assertEquals( TEST_ONE, testCaseOne.getAttribute( "name" ) );
260 assertEquals( "0.012", testCaseOne.getAttribute( "time" ) );
261 assertEquals( getClass().getName(), testCaseOne.getAttribute( "classname" ) );
262
263 Xpp3Dom testCaseTwo = testcase[1];
264 assertEquals( TEST_TWO, testCaseTwo.getAttribute( "name" ) );
265
266 assertEquals( "0.005", testCaseTwo.getAttribute( "time" ) );
267 assertEquals( getClass().getName(), testCaseTwo.getAttribute( "classname" ) );
268 Xpp3Dom errorNode = testCaseTwo.getChild( "error" );
269 Xpp3Dom rerunErrorNode = testCaseTwo.getChild( "rerunError" );
270 assertNotNull( errorNode );
271 assertNotNull( rerunErrorNode );
272
273 assertEquals( "A fud msg", errorNode.getAttribute( "message" ) );
274 assertEquals( "fail at foo", errorNode.getAttribute( "type" ) );
275
276
277 assertEquals( firstRunOut, testCaseTwo.getChild( "system-out" ).getValue() );
278 assertEquals( firstRunErr, testCaseTwo.getChild( "system-err" ).getValue() );
279 assertEquals( secondRunOut, rerunErrorNode.getChild( "system-out" ).getValue() );
280 assertEquals( secondRunErr, rerunErrorNode.getChild( "system-err" ).getValue() );
281 assertEquals( "A fud msg two", rerunErrorNode.getAttribute( "message" ) );
282 assertEquals( "fail at foo two", rerunErrorNode.getAttribute( "type" ) );
283
284
285 Xpp3Dom testCaseThree = testcase[2];
286 assertEquals( TEST_THREE, testCaseThree.getAttribute( "name" ) );
287
288 assertEquals( "0.002", testCaseThree.getAttribute( "time" ) );
289 assertEquals( getClass().getName(), testCaseThree.getAttribute( "classname" ) );
290 Xpp3Dom flakyFailureNode = testCaseThree.getChild( "flakyFailure" );
291 assertNotNull( flakyFailureNode );
292 assertEquals( firstRunOut, flakyFailureNode.getChild( "system-out" ).getValue() );
293 assertEquals( firstRunErr, flakyFailureNode.getChild( "system-err" ).getValue() );
294
295 assertNull( testCaseThree.getChild( "system-out" ) );
296 assertNull( testCaseThree.getChild( "system-err" ) );
297 }
298
299 public void testOutputRerunFlakyAssumption()
300 throws IOException
301 {
302 expectedReportFile = new File( reportDir, "TEST-" + getClass().getName() + ".xml" );
303
304 StackTraceWriter stackTraceWriterOne = new DeserializedStacktraceWriter( "A fud msg", "trimmed",
305 "fail at foo" );
306
307 StackTraceWriter stackTraceWriterTwo =
308 new DeserializedStacktraceWriter( "A fud msg two", "trimmed two", "fail at foo two" );
309
310 String firstRunOut = "first run out";
311 String firstRunErr = "first run err";
312 String secondRunOut = "second run out";
313 String secondRunErr = "second run err";
314
315 WrappedReportEntry testTwoFirstError =
316 new WrappedReportEntry( new SimpleReportEntry( NORMAL_RUN, 1L, getClass().getName(), null, TEST_TWO, null,
317 stackTraceWriterOne, 5 ), ERROR, 5, createStdOutput( firstRunOut ),
318 createStdOutput( firstRunErr ) );
319
320 stats.testSucceeded( testTwoFirstError );
321
322 WrappedReportEntry testTwoSecondError =
323 new WrappedReportEntry( new SimpleReportEntry( RERUN_TEST_AFTER_FAILURE, 1L, getClass().getName(), null,
324 TEST_TWO, null,
325 stackTraceWriterTwo, 13 ), SKIPPED, 13, createStdOutput( secondRunOut ),
326 createStdOutput( secondRunErr ) );
327
328 rerunStats.testSucceeded( testTwoSecondError );
329
330 StatelessXmlReporter reporter =
331 new StatelessXmlReporter( reportDir, null, false, 1,
332 new HashMap<>(), XSD, "3.0", false, false, false, false );
333
334 WrappedReportEntry testSetReportEntry =
335 new WrappedReportEntry( new SimpleReportEntry( RERUN_TEST_AFTER_FAILURE, 1L, getClass().getName(), null,
336 null, null,
337 stackTraceWriterOne, 5 ), ERROR, 20, createStdOutput( firstRunOut ),
338 createStdOutput( firstRunErr ) );
339
340 reporter.testSetCompleted( testSetReportEntry, stats );
341 reporter.testSetCompleted( testSetReportEntry, rerunStats );
342
343 FileInputStream fileInputStream = new FileInputStream( expectedReportFile );
344
345 Xpp3Dom testSuite = Xpp3DomBuilder.build( new InputStreamReader( fileInputStream, UTF_8 ) );
346 assertEquals( "testsuite", testSuite.getName() );
347 assertEquals( "0.02", testSuite.getAttribute( "time" ) );
348
349 Xpp3Dom[] testcase = testSuite.getChildren( "testcase" );
350 assertEquals( 1, testcase.length );
351 Xpp3Dom testCaseOne = testcase[0];
352 assertEquals( getClass().getName(), testCaseOne.getAttribute( "classname" ) );
353 assertEquals( TEST_TWO, testCaseOne.getAttribute( "name" ) );
354 assertEquals( "0.005", testCaseOne.getAttribute( "time" ) );
355
356 Xpp3Dom[] testCaseElements = testCaseOne.getChildren();
357 assertEquals( 3, testCaseElements.length );
358 assertEquals( "error", testCaseElements[0].getName() );
359 assertEquals( "system-out", testCaseElements[1].getName() );
360 assertEquals( "system-err", testCaseElements[2].getName() );
361 long linesWithComments = readAllLines( expectedReportFile.toPath(), UTF_8 )
362 .stream()
363 .filter( line -> line.contains( "<!-- a skipped test execution in re-run phase -->" ) )
364 .count();
365 assertEquals( 1, linesWithComments );
366 }
367
368 public void testNoWritesOnDeferredFile() throws Exception
369 {
370 Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream( "test" );
371 out.free();
372 out.write( "a", false );
373 assertThat( (boolean) getInternalState( out, "isDirty" ) )
374 .isFalse();
375 }
376
377 public void testLengthOnDeferredFile() throws Exception
378 {
379 Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream( "test" );
380
381 assertThat( out.getByteCount() ).isZero();
382
383 File f = File.createTempFile( "test", "tmp" );
384 RandomAccessFile storage = new RandomAccessFile( f, "rw" );
385 setInternalState( out, "storage", storage );
386 setInternalState( out, "file", f.toPath() );
387 storage.writeByte( 0 );
388 storage.getFD().sync();
389 assertThat( out.getByteCount() ).isEqualTo( 1 );
390
391 storage.close();
392 assertThat( f.delete() ).isTrue();
393 assertThat( out.getByteCount() ).isZero();
394 out.free();
395 }
396
397 @SuppressWarnings( "checkstyle:magicnumber" )
398 public void testWritesOnDeferredFile() throws Exception
399 {
400 Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream( "test" );
401 for ( int i = 0; i < 33_000; i++ )
402 {
403 out.write( "A", false );
404 out.write( "B", true );
405 }
406 out.write( null, false );
407 out.write( null, true );
408
409 assertThat( out.getByteCount() )
410 .isEqualTo( 33_000 * ( 1 + 1 + NL.length() ) + 4 + 4 + NL.length() );
411
412 StringBuilder expectedContent = new StringBuilder( 150_000 );
413 for ( int i = 0; i < 33_000; i++ )
414 {
415 expectedContent.append( 'A' ).append( 'B' ).append( NL );
416 }
417 expectedContent.append( "null" ).append( "null" ).append( NL );
418 ByteArrayOutputStream read = new ByteArrayOutputStream( 150_000 );
419 out.writeTo( read );
420 assertThat( read.toString() )
421 .isEqualTo( expectedContent.toString() );
422
423 out.free();
424 }
425
426 public void testFreeOnDeferredFile() throws Exception
427 {
428 Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream( "test" );
429 setInternalState( out, "cache", ByteBuffer.allocate( 0 ) );
430 Path path = mock( Path.class );
431 File file = mock( File.class );
432 when( path.toFile() ).thenReturn( file );
433 setInternalState( out, "file", path );
434 RandomAccessFile storage = mock( RandomAccessFile.class );
435 doThrow( IOException.class ).when( storage ).close();
436 setInternalState( out, "storage", storage );
437 out.free();
438 assertThat( (boolean) getInternalState( out, "closed" ) ).isTrue();
439 verify( file, times( 1 ) ).deleteOnExit();
440 }
441
442 public void testCacheOnDeferredFile() throws Exception
443 {
444 Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream( "test" );
445 byte[] b1 = invokeMethod( out, "getLargeCache", 1 );
446 byte[] b2 = invokeMethod( out, "getLargeCache", 1 );
447 assertThat( b1 ).isSameAs( b2 );
448 assertThat( b1 ).hasSize( 1 );
449
450 byte[] b3 = invokeMethod( out, "getLargeCache", 2 );
451 assertThat( b3 ).isNotSameAs( b1 );
452 assertThat( b3 ).hasSize( 2 );
453
454 byte[] b4 = invokeMethod( out, "getLargeCache", 1 );
455 assertThat( b4 ).isSameAs( b3 );
456 assertThat( b3 ).hasSize( 2 );
457 }
458
459 public void testSyncOnDeferredFile() throws Exception
460 {
461 Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream( "test" );
462 Buffer cache = ByteBuffer.wrap( new byte[] {1, 2, 3} );
463 cache.position( 3 );
464 setInternalState( out, "cache", cache );
465 assertThat( (boolean) getInternalState( out, "isDirty" ) ).isFalse();
466 setInternalState( out, "isDirty", true );
467 File file = new File( reportDir, "test" );
468 setInternalState( out, "file", file.toPath() );
469 RandomAccessFile storage = new RandomAccessFile( file, "rw" );
470 setInternalState( out, "storage", storage );
471 invokeMethod( out, "sync" );
472 assertThat( (boolean) getInternalState( out, "isDirty" ) ).isFalse();
473 storage.seek( 0L );
474 assertThat( storage.read() ).isEqualTo( 1 );
475 assertThat( storage.read() ).isEqualTo( 2 );
476 assertThat( storage.read() ).isEqualTo( 3 );
477 assertThat( storage.read() ).isEqualTo( -1 );
478 assertThat( storage.length() ).isEqualTo( 3L );
479 assertThat( cache.position() ).isEqualTo( 0 );
480 assertThat( cache.limit() ).isEqualTo( 3 );
481 storage.seek( 3L );
482 invokeMethod( out, "sync" );
483 assertThat( (boolean) getInternalState( out, "isDirty" ) ).isFalse();
484 assertThat( storage.length() ).isEqualTo( 3L );
485 assertThat( out.getByteCount() ).isEqualTo( 3L );
486 assertThat( (boolean) getInternalState( out, "closed" ) ).isFalse();
487 out.free();
488 assertThat( (boolean) getInternalState( out, "closed" ) ).isTrue();
489
490 out.free();
491 assertThat( (boolean) getInternalState( out, "closed" ) ).isTrue();
492 }
493
494 private boolean defaultCharsetSupportsSpecialChar()
495 {
496
497 return "\u0115\u00DC".equals( new String( "\u0115\u00DC".getBytes() ) );
498 }
499
500 private Utf8RecodingDeferredFileOutputStream createStdOutput( String content )
501 throws IOException
502 {
503 Utf8RecodingDeferredFileOutputStream stdOut = new Utf8RecodingDeferredFileOutputStream( "fds2" );
504 stdOut.write( content, false );
505 return stdOut;
506 }
507 }