mirror of
https://github.com/google/nomulus.git
synced 2025-05-29 00:40:09 +02:00
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:
parent
b86a35bca1
commit
d14f0fe485
1 changed files with 70 additions and 2 deletions
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue