Lazy output stream for test.xml, does not produce a file if no test xml data was produced This fixes #5289. The general problem is, if an exception is thrown during creation of the `TestSuiteModel`, the test runner will exit, closing off the OutputStream it had created for writing the test.xml. Unfortunately, if nothing was actually written to the stream, closing it will create a 0-byte test.xml on disk regardless. This PR wraps the OutputStream in a lazy producer, only creating the actual stream if something was written to it. This guarantees test.xml will contain actual content. Closes #6000. PiperOrigin-RevId: 211799139
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/ProvideXmlStreamFactory.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/ProvideXmlStreamFactory.java index dc953e9..e69dd8c 100644 --- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/ProvideXmlStreamFactory.java +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/ProvideXmlStreamFactory.java
@@ -16,6 +16,7 @@ import com.google.testing.junit.runner.util.Factory; import com.google.testing.junit.runner.util.Supplier; +import java.io.IOException; import java.io.OutputStream; /** @@ -25,18 +26,82 @@ private final Supplier<JUnit4Config> configSupplier; public ProvideXmlStreamFactory(Supplier<JUnit4Config> configSupplier) { - assert configSupplier != null; + if (configSupplier == null) { + throw new IllegalStateException(); + } + this.configSupplier = configSupplier; } @Override public OutputStream get() { - OutputStream outputStream = JUnit4RunnerModule.provideXmlStream(configSupplier.get()); - assert outputStream != null; + OutputStream outputStream = + new LazyOutputStream( + new Supplier<OutputStream>() { + @Override + public OutputStream get() { + return JUnit4RunnerModule.provideXmlStream(configSupplier.get()); + } + }); + return outputStream; } public static Factory<OutputStream> create(Supplier<JUnit4Config> configSupplier) { return new ProvideXmlStreamFactory(configSupplier); } + + private static class LazyOutputStream extends OutputStream { + private Supplier<OutputStream> supplier; + private volatile OutputStream delegate; + + public LazyOutputStream(Supplier<OutputStream> supplier) { + this.supplier = supplier; + } + + private OutputStream ensureDelegate() { + OutputStream delegate0 = delegate; + if (delegate0 != null) { + return delegate0; + } + + synchronized (this) { + if (delegate == null) { + delegate = supplier.get(); + supplier = null; + } + } + + return delegate; + } + + @Override + public void write(int b) throws IOException { + ensureDelegate().write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + ensureDelegate().write(b, off, len); + } + + @Override + public void write(byte[] b) throws IOException { + ensureDelegate().write(b); + } + + @Override + public void close() throws IOException { + if (delegate != null) { + delegate.close(); + } + } + + @Override + public void flush() throws IOException { + if (delegate != null) { + delegate.flush(); + } + } + } }