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();
+ }
+ }
+ }
}