Output command test output as well as consuming it (#248)

* Output command test output as well as consuming it

CommandTestCase currently consumes stdout & stderr for the command being
tested.  Unfortunately, this results in us not being able to see the command
output.  Add an output splitter so that output gets written to the original
stream in addition to being captured.

A simpler approach would be to print the captured data after command
completion.  However, this won't work for tests that become hung and also
won't display results in real-time.

Tested: Ran a command test with verboseTestOutput=true, verified that standard
output was visible.

* Save and restore original stdout/err in cmd tests

We have to restore the original stdout/stderr print streams otherwise we end
up nesting them across tests which eventually causes the RDE tests to OOM.
This commit is contained in:
Michael Muller 2019-09-17 13:23:30 -04:00 committed by GitHub
parent b86a35bca1
commit d14f0fe485
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -36,8 +36,11 @@ import google.registry.tools.params.ParameterFactory;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.List; import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
@ -54,8 +57,13 @@ import org.mockito.junit.MockitoRule;
@RunWith(JUnit4.class) @RunWith(JUnit4.class)
public abstract class CommandTestCase<C extends Command> { public abstract class CommandTestCase<C extends Command> {
// Lock for stdout/stderr. Note that this is static: since we're dealing with globals, we need
// to lock for the entire JVM.
private static final ReentrantLock streamsLock = new ReentrantLock();
private final ByteArrayOutputStream stdout = new ByteArrayOutputStream(); private final ByteArrayOutputStream stdout = new ByteArrayOutputStream();
private final ByteArrayOutputStream stderr = new ByteArrayOutputStream(); private final ByteArrayOutputStream stderr = new ByteArrayOutputStream();
private PrintStream oldStdout, oldStderr;
protected C command; protected C command;
@ -75,8 +83,22 @@ public abstract class CommandTestCase<C extends Command> {
// Ensure the UNITTEST environment has been set before constructing a new command instance. // Ensure the UNITTEST environment has been set before constructing a new command instance.
RegistryToolEnvironment.UNITTEST.setup(systemPropertyRule); RegistryToolEnvironment.UNITTEST.setup(systemPropertyRule);
command = newCommandInstance(); command = newCommandInstance();
System.setOut(new PrintStream(stdout));
System.setErr(new PrintStream(stderr)); // Capture standard output/error. This is problematic because gradle tests run in parallel in
// the same JVM. So first lock out any other tests in this JVM that are trying to do this
// trick.
streamsLock.lock();
oldStdout = System.out;
System.setOut(new PrintStream(new OutputSplitter(System.out, stdout)));
oldStderr = System.err;
System.setErr(new PrintStream(new OutputSplitter(System.err, stderr)));
}
@After
public final void afterCommandTestCase() throws Exception {
System.setOut(oldStdout);
System.setErr(oldStderr);
streamsLock.unlock();
} }
void runCommandInEnvironment(RegistryToolEnvironment env, String... args) throws Exception { void runCommandInEnvironment(RegistryToolEnvironment env, String... args) throws Exception {
@ -221,4 +243,50 @@ public abstract class CommandTestCase<C extends Command> {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
/**
* Splits an output stream, writing it to two other output streams.
*
* <p>We use this as a replacement for standard out/error so that we can both capture the output
* of the command and display it to the console for debugging.
*/
static class OutputSplitter extends OutputStream {
OutputStream a, b;
OutputSplitter(OutputStream a, OutputStream b) {
this.a = a;
this.b = b;
}
@Override
public void write(byte[] data) throws IOException {
a.write(data);
b.write(data);
}
@Override
public void write(byte[] data, int off, int len) throws IOException {
a.write(data, off, len);
b.write(data, off, len);
}
@Override
public void close() throws IOException {
a.close();
b.close();
}
@Override
public void flush() throws IOException {
a.flush();
b.flush();
}
@Override
public void write(int val) throws IOException {
a.write(val);
b.write(val);
}
}
} }