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