View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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                 false);
116         reporter.cleanTestHistoryMap();
117 
118         ReportEntry reportEntry = new SimpleReportEntry(
119                 NORMAL_RUN, 0L, getClass().getName(), null, getClass().getName(), null, 12);
120         WrappedReportEntry testSetReportEntry = new WrappedReportEntry(
121                 reportEntry, ReportEntryType.SUCCESS, 1771085631L, 12, null, null, systemProps());
122         stats.testSucceeded(testSetReportEntry);
123         reporter.testSetCompleted(testSetReportEntry, stats);
124 
125         expectedReportFile = new File(reportDir, "TEST-" + getClass().getName() + ".xml");
126         assertTrue(
127                 "Report file (" + expectedReportFile.getAbsolutePath() + ") doesn't exist",
128                 expectedReportFile.exists());
129     }
130 
131     public void testAllFieldsSerialized() throws IOException {
132         ReportEntry reportEntry =
133                 new SimpleReportEntry(NORMAL_RUN, 0L, getClass().getName(), null, TEST_ONE, null, 12);
134         WrappedReportEntry testSetReportEntry =
135                 new WrappedReportEntry(reportEntry, SUCCESS, 1771085631L, 12, null, null, systemProps());
136         expectedReportFile = new File(reportDir, "TEST-" + getClass().getName() + ".xml");
137 
138         stats.testSucceeded(testSetReportEntry);
139         StackTraceWriter stackTraceWriter = new DeserializedStacktraceWriter("A fud msg", "trimmed", "fail at foo");
140         Utf8RecodingDeferredFileOutputStream stdOut = new Utf8RecodingDeferredFileOutputStream("fds");
141         String stdOutPrefix;
142         String stdErrPrefix;
143         if (defaultCharsetSupportsSpecialChar()) {
144             stdErrPrefix = "std-\u0115rr";
145             stdOutPrefix = "st]]>d-o\u00DCt";
146         } else {
147             stdErrPrefix = "std-err";
148             stdOutPrefix = "st]]>d-out";
149         }
150 
151         stdOut.write(stdOutPrefix + "<null>!\u0020\u0000\u001F", false);
152 
153         Utf8RecodingDeferredFileOutputStream stdErr = new Utf8RecodingDeferredFileOutputStream("fds");
154 
155         stdErr.write(stdErrPrefix + "?&-&amp;&#163;\u0020\u0000\u001F", false);
156         WrappedReportEntry t2 = new WrappedReportEntry(
157                 new SimpleReportEntry(NORMAL_RUN, 0L, getClass().getName(), null, TEST_TWO, null, stackTraceWriter, 13),
158                 ReportEntryType.ERROR,
159                 1771085631L,
160                 13,
161                 stdOut,
162                 stdErr);
163 
164         stats.testSucceeded(t2);
165         StatelessXmlReporter reporter = new StatelessXmlReporter(
166                 reportDir,
167                 null,
168                 false,
169                 0,
170                 new ConcurrentHashMap<String, Deque<WrappedReportEntry>>(),
171                 XSD,
172                 "3.0.2",
173                 false,
174                 false,
175                 false,
176                 false,
177                 true,
178                 true,
179                 false);
180         reporter.testSetCompleted(testSetReportEntry, stats);
181 
182         FileInputStream fileInputStream = new FileInputStream(expectedReportFile);
183 
184         Xpp3Dom testSuite = Xpp3DomBuilder.build(new InputStreamReader(fileInputStream, UTF_8));
185         assertEquals("testsuite", testSuite.getName());
186         Xpp3Dom properties = testSuite.getChild("properties");
187         assertEquals(System.getProperties().size(), properties.getChildCount());
188         Xpp3Dom child = properties.getChild(1);
189         assertFalse(isEmpty(child.getAttribute("value")));
190         assertFalse(isEmpty(child.getAttribute("name")));
191 
192         Xpp3Dom[] testcase = testSuite.getChildren("testcase");
193         Xpp3Dom tca = testcase[0];
194         assertEquals(TEST_ONE, tca.getAttribute("name"));
195         assertEquals("0.012", tca.getAttribute("time"));
196         assertEquals(getClass().getName(), tca.getAttribute("classname"));
197 
198         Xpp3Dom tcb = testcase[1];
199         assertEquals(TEST_TWO, tcb.getAttribute("name"));
200         assertEquals("0.013", tcb.getAttribute("time"));
201         assertEquals(getClass().getName(), tcb.getAttribute("classname"));
202         Xpp3Dom errorNode = tcb.getChild("error");
203         assertNotNull(errorNode);
204         assertEquals("A fud msg", errorNode.getAttribute("message"));
205         assertEquals("fail at foo", errorNode.getAttribute("type"));
206         assertEquals(
207                 stdOutPrefix + "<null>! &amp#0;&amp#31;",
208                 tcb.getChild("system-out").getValue());
209 
210         assertEquals(
211                 stdErrPrefix + "?&-&amp;&#163; &amp#0;&amp#31;",
212                 tcb.getChild("system-err").getValue());
213     }
214 
215     public void testOutputRerunFlakyFailure() throws IOException {
216         WrappedReportEntry testSetReportEntry = new WrappedReportEntry(
217                 new SimpleReportEntry(NORMAL_RUN, 0L, getClass().getName(), null, TEST_ONE, null, 12),
218                 ReportEntryType.SUCCESS,
219                 1771085631L,
220                 12,
221                 null,
222                 null,
223                 systemProps());
224         expectedReportFile = new File(reportDir, "TEST-" + getClass().getName() + ".xml");
225 
226         stats.testSucceeded(testSetReportEntry);
227         StackTraceWriter stackTraceWriterOne = new DeserializedStacktraceWriter("A fud msg", "trimmed", "fail at foo");
228         StackTraceWriter stackTraceWriterTwo =
229                 new DeserializedStacktraceWriter("A fud msg two", "trimmed two", "fail at foo two");
230 
231         String firstRunOut = "first run out";
232         String firstRunErr = "first run err";
233         String secondRunOut = "second run out";
234         String secondRunErr = "second run err";
235 
236         String cls = getClass().getName();
237         WrappedReportEntry testTwoFirstError = new WrappedReportEntry(
238                 new SimpleReportEntry(NORMAL_RUN, 0L, cls, null, TEST_TWO, null, stackTraceWriterOne, 5),
239                 ReportEntryType.ERROR,
240                 1771085631L,
241                 5,
242                 createStdOutput(firstRunOut),
243                 createStdOutput(firstRunErr));
244 
245         WrappedReportEntry testTwoSecondError = new WrappedReportEntry(
246                 new SimpleReportEntry(RERUN_TEST_AFTER_FAILURE, 1L, cls, null, TEST_TWO, null, stackTraceWriterTwo, 13),
247                 ReportEntryType.ERROR,
248                 1771085631L,
249                 13,
250                 createStdOutput(secondRunOut),
251                 createStdOutput(secondRunErr));
252 
253         WrappedReportEntry testThreeFirstRun = new WrappedReportEntry(
254                 new SimpleReportEntry(NORMAL_RUN, 2L, cls, null, TEST_THREE, null, stackTraceWriterOne, 13),
255                 ReportEntryType.FAILURE,
256                 1771085631L,
257                 13,
258                 createStdOutput(firstRunOut),
259                 createStdOutput(firstRunErr));
260 
261         WrappedReportEntry testThreeSecondRun = new WrappedReportEntry(
262                 new SimpleReportEntry(
263                         RERUN_TEST_AFTER_FAILURE, 3L, cls, null, TEST_THREE, null, stackTraceWriterTwo, 2),
264                 ReportEntryType.SUCCESS,
265                 1771085631L,
266                 2,
267                 createStdOutput(secondRunOut),
268                 createStdOutput(secondRunErr));
269 
270         stats.testSucceeded(testTwoFirstError);
271         stats.testSucceeded(testThreeFirstRun);
272         rerunStats.testSucceeded(testTwoSecondError);
273         rerunStats.testSucceeded(testThreeSecondRun);
274 
275         StatelessXmlReporter reporter = new StatelessXmlReporter(
276                 reportDir,
277                 null,
278                 false,
279                 1,
280                 new HashMap<String, Deque<WrappedReportEntry>>(),
281                 XSD,
282                 "3.0.2",
283                 false,
284                 false,
285                 false,
286                 false,
287                 true,
288                 true,
289                 false);
290 
291         reporter.testSetCompleted(testSetReportEntry, stats);
292         reporter.testSetCompleted(testSetReportEntry, rerunStats);
293 
294         FileInputStream fileInputStream = new FileInputStream(expectedReportFile);
295 
296         Xpp3Dom testSuite = Xpp3DomBuilder.build(new InputStreamReader(fileInputStream, UTF_8));
297         assertEquals("testsuite", testSuite.getName());
298         assertEquals("0.012", testSuite.getAttribute("time"));
299         Xpp3Dom properties = testSuite.getChild("properties");
300         assertEquals(System.getProperties().size(), properties.getChildCount());
301         Xpp3Dom child = properties.getChild(1);
302         assertFalse(isEmpty(child.getAttribute("value")));
303         assertFalse(isEmpty(child.getAttribute("name")));
304 
305         Xpp3Dom[] testcase = testSuite.getChildren("testcase");
306         Xpp3Dom testCaseOne = testcase[0];
307         assertEquals(TEST_ONE, testCaseOne.getAttribute("name"));
308         assertEquals("0.012", testCaseOne.getAttribute("time"));
309         assertEquals(getClass().getName(), testCaseOne.getAttribute("classname"));
310 
311         Xpp3Dom testCaseTwo = testcase[1];
312         assertEquals(TEST_TWO, testCaseTwo.getAttribute("name"));
313         // Run time for a rerun failing test is the run time of the first run
314         assertEquals("0.005", testCaseTwo.getAttribute("time"));
315         assertEquals(getClass().getName(), testCaseTwo.getAttribute("classname"));
316         Xpp3Dom errorNode = testCaseTwo.getChild("error");
317         Xpp3Dom rerunErrorNode = testCaseTwo.getChild("rerunError");
318         assertNotNull(errorNode);
319         assertNotNull(rerunErrorNode);
320 
321         assertEquals("A fud msg", errorNode.getAttribute("message"));
322         assertEquals("fail at foo", errorNode.getAttribute("type"));
323 
324         // Check rerun error node contains all the information
325         assertEquals(firstRunOut, testCaseTwo.getChild("system-out").getValue());
326         assertEquals(firstRunErr, testCaseTwo.getChild("system-err").getValue());
327         assertEquals(secondRunOut, rerunErrorNode.getChild("system-out").getValue());
328         assertEquals(secondRunErr, rerunErrorNode.getChild("system-err").getValue());
329         assertEquals("A fud msg two", rerunErrorNode.getAttribute("message"));
330         assertEquals("fail at foo two", rerunErrorNode.getAttribute("type"));
331 
332         // Check flaky failure node
333         Xpp3Dom testCaseThree = testcase[2];
334         assertEquals(TEST_THREE, testCaseThree.getAttribute("name"));
335         // Run time for a flaky test is the run time of the first successful run
336         assertEquals("0.002", testCaseThree.getAttribute("time"));
337         assertEquals(getClass().getName(), testCaseThree.getAttribute("classname"));
338         Xpp3Dom flakyFailureNode = testCaseThree.getChild("flakyFailure");
339         assertNotNull(flakyFailureNode);
340         assertEquals(firstRunOut, flakyFailureNode.getChild("system-out").getValue());
341         assertEquals(firstRunErr, flakyFailureNode.getChild("system-err").getValue());
342         // system-out and system-err should not be present for flaky failures
343         assertNull(testCaseThree.getChild("system-out"));
344         assertNull(testCaseThree.getChild("system-err"));
345     }
346 
347     public void testOutputRerunFlakyAssumption() throws IOException {
348         expectedReportFile = new File(reportDir, "TEST-" + getClass().getName() + ".xml");
349 
350         StackTraceWriter stackTraceWriterOne = new DeserializedStacktraceWriter("A fud msg", "trimmed", "fail at foo");
351 
352         StackTraceWriter stackTraceWriterTwo =
353                 new DeserializedStacktraceWriter("A fud msg two", "trimmed two", "fail at foo two");
354 
355         String firstRunOut = "first run out";
356         String firstRunErr = "first run err";
357         String secondRunOut = "second run out";
358         String secondRunErr = "second run err";
359 
360         WrappedReportEntry testTwoFirstError = new WrappedReportEntry(
361                 new SimpleReportEntry(
362                         NORMAL_RUN, 1L, getClass().getName(), null, TEST_TWO, null, stackTraceWriterOne, 5),
363                 ERROR,
364                 1771085631L,
365                 5,
366                 createStdOutput(firstRunOut),
367                 createStdOutput(firstRunErr));
368 
369         stats.testSucceeded(testTwoFirstError);
370 
371         WrappedReportEntry testTwoSecondError = new WrappedReportEntry(
372                 new SimpleReportEntry(
373                         RERUN_TEST_AFTER_FAILURE,
374                         1L,
375                         getClass().getName(),
376                         null,
377                         TEST_TWO,
378                         null,
379                         stackTraceWriterTwo,
380                         13),
381                 SKIPPED,
382                 1771085631L,
383                 13,
384                 createStdOutput(secondRunOut),
385                 createStdOutput(secondRunErr));
386 
387         rerunStats.testSucceeded(testTwoSecondError);
388 
389         StatelessXmlReporter reporter = new StatelessXmlReporter(
390                 reportDir,
391                 null,
392                 false,
393                 1,
394                 new HashMap<>(),
395                 XSD,
396                 "3.0.2",
397                 false,
398                 false,
399                 false,
400                 false,
401                 true,
402                 true,
403                 false);
404 
405         WrappedReportEntry testSetReportEntry = new WrappedReportEntry(
406                 new SimpleReportEntry(
407                         RERUN_TEST_AFTER_FAILURE, 1L, getClass().getName(), null, null, null, stackTraceWriterOne, 5),
408                 ERROR,
409                 1771085631L,
410                 20,
411                 createStdOutput(firstRunOut),
412                 createStdOutput(firstRunErr));
413 
414         reporter.testSetCompleted(testSetReportEntry, stats);
415         reporter.testSetCompleted(testSetReportEntry, rerunStats);
416 
417         FileInputStream fileInputStream = new FileInputStream(expectedReportFile);
418 
419         Xpp3Dom testSuite = Xpp3DomBuilder.build(new InputStreamReader(fileInputStream, UTF_8));
420         assertEquals("testsuite", testSuite.getName());
421         assertEquals("0.02", testSuite.getAttribute("time"));
422 
423         Xpp3Dom[] testcase = testSuite.getChildren("testcase");
424         assertEquals(1, testcase.length);
425         Xpp3Dom testCaseOne = testcase[0];
426         assertEquals(getClass().getName(), testCaseOne.getAttribute("classname"));
427         assertEquals(TEST_TWO, testCaseOne.getAttribute("name"));
428         assertEquals("0.005", testCaseOne.getAttribute("time"));
429 
430         Xpp3Dom[] testCaseElements = testCaseOne.getChildren();
431         assertEquals(3, testCaseElements.length);
432         assertEquals("error", testCaseElements[0].getName());
433         assertEquals("system-out", testCaseElements[1].getName());
434         assertEquals("system-err", testCaseElements[2].getName());
435         long linesWithComments = readAllLines(expectedReportFile.toPath(), UTF_8).stream()
436                 .filter(line -> line.contains("<!-- a skipped test execution in re-run phase -->"))
437                 .count();
438         assertEquals(1, linesWithComments);
439     }
440 
441     public void testNoWritesOnDeferredFile() throws Exception {
442         Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream("test");
443         out.free();
444         out.write("a", false);
445         assertThat((boolean) getInternalState(out, "isDirty")).isFalse();
446     }
447 
448     public void testLengthOnDeferredFile() throws Exception {
449         Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream("test");
450 
451         assertThat(out.getByteCount()).isZero();
452 
453         File f = File.createTempFile("test", "tmp");
454         RandomAccessFile storage = new RandomAccessFile(f, "rw");
455         setInternalState(out, "storage", storage);
456         setInternalState(out, "file", f.toPath());
457         storage.writeByte(0);
458         storage.getFD().sync();
459         assertThat(out.getByteCount()).isEqualTo(1);
460 
461         storage.close();
462         assertThat(f.delete()).isTrue();
463         assertThat(out.getByteCount()).isZero();
464         out.free();
465     }
466 
467     @SuppressWarnings("checkstyle:magicnumber")
468     public void testWritesOnDeferredFile() throws Exception {
469         Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream("test");
470         for (int i = 0; i < 33_000; i++) {
471             out.write("A", false);
472             out.write("B", true);
473         }
474         out.write(null, false);
475         out.write(null, true);
476 
477         assertThat(out.getByteCount()).isEqualTo(33_000 * (1 + 1 + NL.length()) + 4 + 4 + NL.length());
478 
479         StringBuilder expectedContent = new StringBuilder(150_000);
480         for (int i = 0; i < 33_000; i++) {
481             expectedContent.append('A').append('B').append(NL);
482         }
483         expectedContent.append("null").append("null").append(NL);
484         ByteArrayOutputStream read = new ByteArrayOutputStream(150_000);
485         out.writeTo(read);
486         assertThat(read.toString()).isEqualTo(expectedContent.toString());
487 
488         out.free();
489     }
490 
491     public void testFreeOnDeferredFile() throws Exception {
492         Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream("test");
493         setInternalState(out, "cache", ByteBuffer.allocate(0));
494         Path path = mock(Path.class);
495         File file = mock(File.class);
496         when(path.toFile()).thenReturn(file);
497         setInternalState(out, "file", path);
498         RandomAccessFile storage = mock(RandomAccessFile.class);
499         doThrow(IOException.class).when(storage).close();
500         setInternalState(out, "storage", storage);
501         out.free();
502         assertThat((boolean) getInternalState(out, "closed")).isTrue();
503         verify(file, times(1)).deleteOnExit();
504     }
505 
506     public void testCacheOnDeferredFile() throws Exception {
507         Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream("test");
508         byte[] b1 = invokeMethod(out, "getLargeCache", 1);
509         byte[] b2 = invokeMethod(out, "getLargeCache", 1);
510         assertThat(b1).isSameAs(b2);
511         assertThat(b1).hasSize(1);
512 
513         byte[] b3 = invokeMethod(out, "getLargeCache", 2);
514         assertThat(b3).isNotSameAs(b1);
515         assertThat(b3).hasSize(2);
516 
517         byte[] b4 = invokeMethod(out, "getLargeCache", 1);
518         assertThat(b4).isSameAs(b3);
519         assertThat(b3).hasSize(2);
520     }
521 
522     public void testSyncOnDeferredFile() throws Exception {
523         Utf8RecodingDeferredFileOutputStream out = new Utf8RecodingDeferredFileOutputStream("test");
524         Buffer cache = ByteBuffer.wrap(new byte[] {1, 2, 3});
525         cache.position(3);
526         setInternalState(out, "cache", cache);
527         assertThat((boolean) getInternalState(out, "isDirty")).isFalse();
528         setInternalState(out, "isDirty", true);
529         File file = new File(reportDir, "test");
530         setInternalState(out, "file", file.toPath());
531         RandomAccessFile storage = new RandomAccessFile(file, "rw");
532         setInternalState(out, "storage", storage);
533         invokeMethod(out, "sync");
534         assertThat((boolean) getInternalState(out, "isDirty")).isFalse();
535         storage.seek(0L);
536         assertThat(storage.read()).isEqualTo(1);
537         assertThat(storage.read()).isEqualTo(2);
538         assertThat(storage.read()).isEqualTo(3);
539         assertThat(storage.read()).isEqualTo(-1);
540         assertThat(storage.length()).isEqualTo(3L);
541         assertThat(cache.position()).isEqualTo(0);
542         assertThat(cache.limit()).isEqualTo(3);
543         storage.seek(3L);
544         invokeMethod(out, "sync");
545         assertThat((boolean) getInternalState(out, "isDirty")).isFalse();
546         assertThat(storage.length()).isEqualTo(3L);
547         assertThat(out.getByteCount()).isEqualTo(3L);
548         assertThat((boolean) getInternalState(out, "closed")).isFalse();
549         out.free();
550         assertThat((boolean) getInternalState(out, "closed")).isTrue();
551         // todo assertThat( file ).doesNotExist();
552         out.free();
553         assertThat((boolean) getInternalState(out, "closed")).isTrue();
554     }
555 
556     public void testReporterHandlesATestWithoutMessageAndWithEmptyStackTrace() {
557         StackTraceWriter stackTraceWriterOne = new DeserializedStacktraceWriter(null, null, "");
558 
559         WrappedReportEntry testReport = new WrappedReportEntry(
560                 new SimpleReportEntry(
561                         NORMAL_RUN, 1L, getClass().getName(), null, "a test name", null, stackTraceWriterOne, 5),
562                 ERROR,
563                 1771085631L,
564                 5,
565                 null,
566                 null);
567 
568         StatelessXmlReporter reporter = new StatelessXmlReporter(
569                 reportDir,
570                 null,
571                 false,
572                 1,
573                 new HashMap<>(),
574                 XSD,
575                 "3.0.2",
576                 false,
577                 false,
578                 false,
579                 false,
580                 true,
581                 true,
582                 false);
583 
584         reporter.testSetCompleted(testReport, stats);
585     }
586 
587     private boolean defaultCharsetSupportsSpecialChar() {
588         // some charsets are not able to deal with \u0115 on both ways of the conversion
589         return "\u0115\u00DC".equals(new String("\u0115\u00DC".getBytes()));
590     }
591 
592     private Utf8RecodingDeferredFileOutputStream createStdOutput(String content) throws IOException {
593         Utf8RecodingDeferredFileOutputStream stdOut = new Utf8RecodingDeferredFileOutputStream("fds2");
594         stdOut.write(content, false);
595         return stdOut;
596     }
597 }