8157917: JShell: shutdown could cause remote JDWP errors to be visible to users

8157918: JShell tests: StartOptionTest displays insufficient information to diagnose failures

Reviewed-by: vromero
This commit is contained in:
Robert Field 2016-05-26 07:58:01 -07:00
parent f9526a87fe
commit 1fb5067c57
3 changed files with 58 additions and 51 deletions

View File

@ -50,6 +50,7 @@ import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
class JDIConnection { class JDIConnection {
private VirtualMachine vm; private VirtualMachine vm;
private boolean active = true;
private Process process = null; private Process process = null;
private int outputCompleteCount = 0; private int outputCompleteCount = 0;
@ -175,6 +176,11 @@ class JDIConnection {
return process != null && process.isAlive(); return process != null && process.isAlive();
} }
// Beginning shutdown, ignore any random dying squeals
void beginShutdown() {
active = false;
}
public synchronized void disposeVM() { public synchronized void disposeVM() {
try { try {
if (vm != null) { if (vm != null) {
@ -233,14 +239,19 @@ class JDIConnection {
int i; int i;
try { try {
while ((i = in.read()) != -1) { while ((i = in.read()) != -1) {
pStream.print((char) i); // directly copy input to output, but skip if asked to close
if (active) {
pStream.print((char) i);
}
} }
} catch (IOException ex) { } catch (IOException ex) {
String s = ex.getMessage(); String s = ex.getMessage();
if (!s.startsWith("Bad file number")) { if (active && !s.startsWith("Bad file number")) {
throw ex; throw ex;
} }
// else we got a Bad file number IOException which just means // else we are being shutdown (and don't want any spurious death
// throws to ripple) or
// we got a Bad file number IOException which just means
// that the debuggee has gone away. We'll just treat it the // that the debuggee has gone away. We'll just treat it the
// same as if we got an EOF. // same as if we got an EOF.
} }

View File

@ -109,11 +109,14 @@ public class JDIExecutionControl implements ExecutionControl {
@Override @Override
public void close() { public void close() {
try { try {
JDIConnection c = jdiEnv.connection();
if (c != null) {
c.beginShutdown();
}
if (remoteOut != null) { if (remoteOut != null) {
remoteOut.writeInt(CMD_EXIT); remoteOut.writeInt(CMD_EXIT);
remoteOut.flush(); remoteOut.flush();
} }
JDIConnection c = jdiEnv.connection();
if (c != null) { if (c != null) {
c.disposeVM(); c.disposeVM();
} }

View File

@ -34,7 +34,6 @@
*/ */
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Path; import java.nio.file.Path;
@ -48,85 +47,76 @@ import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
@Test @Test
public class StartOptionTest { public class StartOptionTest {
private ByteArrayOutputStream out; private ByteArrayOutputStream cmdout;
private ByteArrayOutputStream err; private ByteArrayOutputStream cmderr;
private ByteArrayOutputStream console;
private ByteArrayOutputStream userout;
private ByteArrayOutputStream usererr;
private JShellTool getShellTool() { private JShellTool getShellTool() {
class NoOutputAllowedStream extends OutputStream {
private final String label;
NoOutputAllowedStream(String label) {
this.label = label;
}
@Override
public void write(int b) { fail("Unexpected output to: " + label); }
}
return new JShellTool( return new JShellTool(
new TestingInputStream(), new TestingInputStream(),
new PrintStream(out), new PrintStream(cmdout),
new PrintStream(err), new PrintStream(cmderr),
new PrintStream(new NoOutputAllowedStream("console")), new PrintStream(console),
new TestingInputStream(), new TestingInputStream(),
new PrintStream(new NoOutputAllowedStream("userout")), new PrintStream(userout),
new PrintStream(new NoOutputAllowedStream("usererr")), new PrintStream(usererr),
new ReplToolTesting.MemoryPreferences(), new ReplToolTesting.MemoryPreferences(),
Locale.ROOT); Locale.ROOT);
} }
private String getOutput() { private void check(ByteArrayOutputStream str, Consumer<String> checkOut, String label) {
byte[] bytes = out.toByteArray(); byte[] bytes = str.toByteArray();
out.reset(); str.reset();
return new String(bytes, StandardCharsets.UTF_8); String out = new String(bytes, StandardCharsets.UTF_8);
} if (checkOut != null) {
checkOut.accept(out);
private String getError() { } else {
byte[] bytes = err.toByteArray(); assertEquals("", out, label + ": Expected empty -- ");
err.reset(); }
return new String(bytes, StandardCharsets.UTF_8);
} }
private void start(Consumer<String> checkOutput, Consumer<String> checkError, String... args) throws Exception { private void start(Consumer<String> checkOutput, Consumer<String> checkError, String... args) throws Exception {
JShellTool tool = getShellTool(); JShellTool tool = getShellTool();
tool.start(args); tool.start(args);
if (checkOutput != null) { check(cmdout, checkOutput, "cmdout");
checkOutput.accept(getOutput()); check(cmderr, checkError, "cmderr");
} else { check(console, null, "console");
assertEquals("", getOutput(), "Output: "); check(userout, null, "userout");
} check(usererr, null, "usererr");
if (checkError != null) {
checkError.accept(getError());
} else {
assertEquals("", getError(), "Error: ");
}
} }
private void start(String expectedOutput, String expectedError, String... args) throws Exception { private void start(String expectedOutput, String expectedError, String... args) throws Exception {
start(s -> assertEquals(s.trim(), expectedOutput, "Output: "), s -> assertEquals(s.trim(), expectedError, "Error: "), args); start(s -> assertEquals(s.trim(), expectedOutput, "cmdout: "), s -> assertEquals(s.trim(), expectedError, "cmderr: "), args);
} }
@BeforeMethod @BeforeMethod
public void setUp() { public void setUp() {
out = new ByteArrayOutputStream(); cmdout = new ByteArrayOutputStream();
err = new ByteArrayOutputStream(); cmderr = new ByteArrayOutputStream();
console = new ByteArrayOutputStream();
userout = new ByteArrayOutputStream();
usererr = new ByteArrayOutputStream();
} }
@Test @Test
public void testUsage() throws Exception { public void testUsage() throws Exception {
start(s -> { start(s -> {
assertTrue(s.split("\n").length >= 7, s); assertTrue(s.split("\n").length >= 7, "Not enough usage lines: " + s);
assertTrue(s.startsWith("Usage: jshell <options>"), s); assertTrue(s.startsWith("Usage: jshell <options>"), "Unexpect usage start: " + s);
}, null, "-help"); }, null, "-help");
} }
@Test @Test
public void testUnknown() throws Exception { public void testUnknown() throws Exception {
start(s -> { start(s -> {
assertTrue(s.split("\n").length >= 7, s); assertTrue(s.split("\n").length >= 7, "Not enough usage lines (unknown): " + s);
assertTrue(s.startsWith("Usage: jshell <options>"), s); assertTrue(s.startsWith("Usage: jshell <options>"), "Unexpect usage start (unknown): " + s);
}, s -> assertEquals(s.trim(), "Unknown option: -unknown"), "-unknown"); }, s -> assertEquals(s.trim(), "Unknown option: -unknown"), "-unknown");
} }
@ -157,12 +147,15 @@ public class StartOptionTest {
@Test @Test
public void testVersion() throws Exception { public void testVersion() throws Exception {
start(s -> assertTrue(s.startsWith("jshell")), null, "-version"); start(s -> assertTrue(s.startsWith("jshell"), "unexpected version: " + s), null, "-version");
} }
@AfterMethod @AfterMethod
public void tearDown() { public void tearDown() {
out = null; cmdout = null;
err = null; cmderr = null;
console = null;
userout = null;
usererr = null;
} }
} }