apache/maven-surefire — pull request #42 by silencerby (2014-07-23 08:47:30+00:00)
Title: Testcase: 'java.io.IOException: Stream Closed' if test's console output exceeds 1Mb

A test case to reproduce 'java.io.IOException: Stream Closed'. Which is thrown when FailSafe runs a couple of integration tests with a custom Junit test runner and their console output exceeds 1Mb.

My comments below might help to pinpoint the cause of the problem.

Applicable only for JUnitCoreRunListener (forced in the provided test case by specifying groups in failsafe's configuration). And only if Test's and/or TestRunner's console output exceeds 1Mb.

If console output exceeds 1Mb limit set on Utf8RecodingDeferredFileOutputStream.deferredFileOutputStream field, the DeferredFileOutputStream will switch from DeferredFileOutputStream.memoryOutputStream to a stream backed by DeferredFileOutputStream.outputFile.

When TestSetRunListener.testSetCompleted event is received, TestSetRunListener will call Utf8RecodingDeferredFileOutputStream.free method that in turn will delete Utf8RecodingDeferredFileOutputStream.deferredFileOutputStream.getFile() file. And for a 'double tap' the same free() method will be called in TestSetStats.reset()

wrap.getStdout().free();
wrap.getStdErr().free();

globalStatistics.add( detailsForThis );
detailsForThis.reset();

But having TestSetRunListener.testSetCompleted event received is not a guarantee that Fork is finished. And an output of the next TestSet in the same Fork will be pushed into the already closed stream (backed by DeferredFileOutputStream.outputFile).

Hence, 'java.io.IOException: Stream Closed' exception.

I'm not familiar enough with the surefire's codebase to assert the impact of any fixes I might suggest.
But I think the whole usage of Utf8RecodingDeferredFileOutputStream/DeferredFileOutputStream in TestSetRunListener must be relooked at. I mean, underlying file of DeferredFileOutputStream is meticulously deleted on 'testSetCompleted' event but blissfully discarded in TestSetRunListener.clearCapture() method on testSucceeded, testError, testFailed and testSkipped events.