mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 11:04:34 +02:00
8144906: Fix jshell's ToolBasicTest
Various fixes to fix the ToolBasicTest - line endings normalization, ordering for output from RemoteAgent, synchronization. Reviewed-by: rfield
This commit is contained in:
parent
05799f8d81
commit
1ee440e9bc
5 changed files with 220 additions and 20 deletions
|
@ -28,6 +28,9 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
@ -35,7 +38,9 @@ import java.net.Socket;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static jdk.internal.jshell.remote.RemoteCodes.*;
|
import static jdk.internal.jshell.remote.RemoteCodes.*;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
@ -59,7 +64,10 @@ class RemoteAgent {
|
||||||
void commandLoop(Socket socket) throws IOException {
|
void commandLoop(Socket socket) throws IOException {
|
||||||
// in before out -- so we don't hang the controlling process
|
// in before out -- so we don't hang the controlling process
|
||||||
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
|
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
|
||||||
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
|
OutputStream socketOut = socket.getOutputStream();
|
||||||
|
System.setOut(new PrintStream(new MultiplexingOutputStream("out", socketOut), true));
|
||||||
|
System.setErr(new PrintStream(new MultiplexingOutputStream("err", socketOut), true));
|
||||||
|
ObjectOutputStream out = new ObjectOutputStream(new MultiplexingOutputStream("command", socketOut));
|
||||||
while (true) {
|
while (true) {
|
||||||
int cmd = in.readInt();
|
int cmd = in.readInt();
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
@ -260,4 +268,64 @@ class RemoteAgent {
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class MultiplexingOutputStream extends OutputStream {
|
||||||
|
|
||||||
|
private static final int PACKET_SIZE = 127;
|
||||||
|
|
||||||
|
private final byte[] name;
|
||||||
|
private final OutputStream delegate;
|
||||||
|
|
||||||
|
public MultiplexingOutputStream(String name, OutputStream delegate) {
|
||||||
|
try {
|
||||||
|
this.name = name.getBytes("UTF-8");
|
||||||
|
this.delegate = delegate;
|
||||||
|
} catch (UnsupportedEncodingException ex) {
|
||||||
|
throw new IllegalStateException(ex); //should not happen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
synchronized (delegate) {
|
||||||
|
delegate.write(name.length); //assuming the len is small enough to fit into byte
|
||||||
|
delegate.write(name);
|
||||||
|
delegate.write(1);
|
||||||
|
delegate.write(b);
|
||||||
|
delegate.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
synchronized (delegate) {
|
||||||
|
int i = 0;
|
||||||
|
while (len > 0) {
|
||||||
|
int size = Math.min(PACKET_SIZE, len);
|
||||||
|
|
||||||
|
delegate.write(name.length); //assuming the len is small enough to fit into byte
|
||||||
|
delegate.write(name);
|
||||||
|
delegate.write(size);
|
||||||
|
delegate.write(b, off + i, size);
|
||||||
|
i += size;
|
||||||
|
len -= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() throws IOException {
|
||||||
|
super.flush();
|
||||||
|
delegate.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
super.close();
|
||||||
|
delegate.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,12 @@
|
||||||
package jdk.jshell;
|
package jdk.jshell;
|
||||||
|
|
||||||
import static jdk.internal.jshell.remote.RemoteCodes.*;
|
import static jdk.internal.jshell.remote.RemoteCodes.*;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import com.sun.jdi.*;
|
import com.sun.jdi.*;
|
||||||
|
@ -69,7 +72,9 @@ class ExecutionControl {
|
||||||
socket = listener.accept();
|
socket = listener.accept();
|
||||||
// out before in -- match remote creation so we don't hang
|
// out before in -- match remote creation so we don't hang
|
||||||
out = new ObjectOutputStream(socket.getOutputStream());
|
out = new ObjectOutputStream(socket.getOutputStream());
|
||||||
in = new ObjectInputStream(socket.getInputStream());
|
PipeInputStream commandIn = new PipeInputStream();
|
||||||
|
new DemultiplexInput(socket.getInputStream(), commandIn, proc.out, proc.err).start();
|
||||||
|
in = new ObjectInputStream(commandIn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,11 +122,13 @@ class ExecutionControl {
|
||||||
String result = in.readUTF();
|
String result = in.readUTF();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} catch (EOFException ex) {
|
|
||||||
env.shutdown();
|
|
||||||
} catch (IOException | ClassNotFoundException ex) {
|
} catch (IOException | ClassNotFoundException ex) {
|
||||||
|
if (!env.connection().isRunning()) {
|
||||||
|
env.shutdown();
|
||||||
|
} else {
|
||||||
proc.debug(DBG_GEN, "Exception on remote invoke: %s\n", ex);
|
proc.debug(DBG_GEN, "Exception on remote invoke: %s\n", ex);
|
||||||
return "Execution failure: " + ex.getMessage();
|
return "Execution failure: " + ex.getMessage();
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
synchronized (STOP_LOCK) {
|
synchronized (STOP_LOCK) {
|
||||||
userCodeRunning = false;
|
userCodeRunning = false;
|
||||||
|
@ -310,4 +317,112 @@ class ExecutionControl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class DemultiplexInput extends Thread {
|
||||||
|
|
||||||
|
private final DataInputStream delegate;
|
||||||
|
private final PipeInputStream command;
|
||||||
|
private final PrintStream out;
|
||||||
|
private final PrintStream err;
|
||||||
|
|
||||||
|
public DemultiplexInput(InputStream input,
|
||||||
|
PipeInputStream command,
|
||||||
|
PrintStream out,
|
||||||
|
PrintStream err) {
|
||||||
|
super("output reader");
|
||||||
|
this.delegate = new DataInputStream(input);
|
||||||
|
this.command = command;
|
||||||
|
this.out = out;
|
||||||
|
this.err = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
int nameLen = delegate.read();
|
||||||
|
if (nameLen == (-1))
|
||||||
|
break;
|
||||||
|
byte[] name = new byte[nameLen];
|
||||||
|
DemultiplexInput.this.delegate.readFully(name);
|
||||||
|
int dataLen = delegate.read();
|
||||||
|
byte[] data = new byte[dataLen];
|
||||||
|
DemultiplexInput.this.delegate.readFully(data);
|
||||||
|
switch (new String(name, "UTF-8")) {
|
||||||
|
case "err":
|
||||||
|
err.write(data);
|
||||||
|
break;
|
||||||
|
case "out":
|
||||||
|
out.write(data);
|
||||||
|
break;
|
||||||
|
case "command":
|
||||||
|
for (byte b : data) {
|
||||||
|
command.write(Byte.toUnsignedInt(b));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
proc.debug(ex, "Failed reading output");
|
||||||
|
} finally {
|
||||||
|
command.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class PipeInputStream extends InputStream {
|
||||||
|
public static final int INITIAL_SIZE = 128;
|
||||||
|
|
||||||
|
private int[] buffer = new int[INITIAL_SIZE];
|
||||||
|
private int start;
|
||||||
|
private int end;
|
||||||
|
private boolean closed;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int read() {
|
||||||
|
while (start == end) {
|
||||||
|
if (closed) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return buffer[start];
|
||||||
|
} finally {
|
||||||
|
start = (start + 1) % buffer.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void write(int b) {
|
||||||
|
if (closed)
|
||||||
|
throw new IllegalStateException("Already closed.");
|
||||||
|
int newEnd = (end + 1) % buffer.length;
|
||||||
|
if (newEnd == start) {
|
||||||
|
//overflow:
|
||||||
|
int[] newBuffer = new int[buffer.length * 2];
|
||||||
|
int rightPart = (end > start ? end : buffer.length) - start;
|
||||||
|
int leftPart = end > start ? 0 : start - 1;
|
||||||
|
System.arraycopy(buffer, start, newBuffer, 0, rightPart);
|
||||||
|
System.arraycopy(buffer, 0, newBuffer, rightPart, leftPart);
|
||||||
|
buffer = newBuffer;
|
||||||
|
start = 0;
|
||||||
|
end = rightPart + leftPart;
|
||||||
|
newEnd = end + 1;
|
||||||
|
}
|
||||||
|
buffer[end] = b;
|
||||||
|
end = newEnd;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void close() {
|
||||||
|
closed = true;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,7 @@ class JDIConnection {
|
||||||
return vm;
|
return vm;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean setConnectorArg(String name, String value) {
|
synchronized boolean setConnectorArg(String name, String value) {
|
||||||
/*
|
/*
|
||||||
* Too late if the connection already made
|
* Too late if the connection already made
|
||||||
*/
|
*/
|
||||||
|
@ -165,7 +165,7 @@ class JDIConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isOpen() {
|
synchronized boolean isOpen() {
|
||||||
return (vm != null);
|
return (vm != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,13 +173,17 @@ class JDIConnection {
|
||||||
return (connector instanceof LaunchingConnector);
|
return (connector instanceof LaunchingConnector);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disposeVM() {
|
synchronized boolean isRunning() {
|
||||||
|
return process != null && process.isAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void disposeVM() {
|
||||||
try {
|
try {
|
||||||
if (vm != null) {
|
if (vm != null) {
|
||||||
vm.dispose(); // This could NPE, so it is caught below
|
vm.dispose(); // This could NPE, so it is caught below
|
||||||
vm = null;
|
vm = null;
|
||||||
}
|
}
|
||||||
} catch (VMDisconnectedException | NullPointerException ex) {
|
} catch (VMDisconnectedException ex) {
|
||||||
// Ignore if already closed
|
// Ignore if already closed
|
||||||
} finally {
|
} finally {
|
||||||
if (process != null) {
|
if (process != null) {
|
||||||
|
|
|
@ -152,13 +152,13 @@ public class ReplToolTesting {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCommandOutput() {
|
public String getCommandOutput() {
|
||||||
String s = cmdout.toString();
|
String s = normalizeLineEndings(cmdout.toString());
|
||||||
cmdout.reset();
|
cmdout.reset();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCommandErrorOutput() {
|
public String getCommandErrorOutput() {
|
||||||
String s = cmderr.toString();
|
String s = normalizeLineEndings(cmderr.toString());
|
||||||
cmderr.reset();
|
cmderr.reset();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -168,13 +168,13 @@ public class ReplToolTesting {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserOutput() {
|
public String getUserOutput() {
|
||||||
String s = userout.toString();
|
String s = normalizeLineEndings(userout.toString());
|
||||||
userout.reset();
|
userout.reset();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserErrorOutput() {
|
public String getUserErrorOutput() {
|
||||||
String s = usererr.toString();
|
String s = normalizeLineEndings(usererr.toString());
|
||||||
usererr.reset();
|
usererr.reset();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -461,6 +461,10 @@ public class ReplToolTesting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalizeLineEndings(String text) {
|
||||||
|
return text.replace(System.getProperty("line.separator"), "\n");
|
||||||
|
}
|
||||||
|
|
||||||
public static abstract class MemberInfo {
|
public static abstract class MemberInfo {
|
||||||
public final String source;
|
public final String source;
|
||||||
public final String type;
|
public final String type;
|
||||||
|
|
|
@ -23,14 +23,16 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8143037 8142447 8144095 8140265
|
* @bug 8143037 8142447 8144095 8140265 8144906
|
||||||
|
* @requires os.family != "solaris"
|
||||||
* @summary Tests for Basic tests for REPL tool
|
* @summary Tests for Basic tests for REPL tool
|
||||||
* @library /tools/lib
|
* @library /tools/lib
|
||||||
* @ignore 8139873
|
* @ignore 8139873
|
||||||
* @build KullaTesting TestingInputStream ToolBox Compiler
|
* @build KullaTesting TestingInputStream ToolBox Compiler
|
||||||
* @run testng ToolBasicTest
|
* @run testng/timeout=600 ToolBasicTest
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
@ -460,8 +462,7 @@ public class ToolBasicTest extends ReplToolTesting {
|
||||||
Path unknown = compiler.getPath("UNKNOWN.jar");
|
Path unknown = compiler.getPath("UNKNOWN.jar");
|
||||||
test(true, new String[]{unknown.toString()},
|
test(true, new String[]{unknown.toString()},
|
||||||
"| File '" + unknown
|
"| File '" + unknown
|
||||||
+ "' is not found: " + unknown
|
+ "' is not found: " + unresolvableMessage(unknown) + "\n");
|
||||||
+ " (No such file or directory)\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReset() {
|
public void testReset() {
|
||||||
|
@ -514,8 +515,7 @@ public class ToolBasicTest extends ReplToolTesting {
|
||||||
test(
|
test(
|
||||||
(a) -> assertCommand(a, s + " " + unknown,
|
(a) -> assertCommand(a, s + " " + unknown,
|
||||||
"| File '" + unknown
|
"| File '" + unknown
|
||||||
+ "' is not found: " + unknown
|
+ "' is not found: " + unresolvableMessage(unknown) + "\n")
|
||||||
+ " (No such file or directory)\n")
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -874,6 +874,15 @@ public class ToolBasicTest extends ReplToolTesting {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String unresolvableMessage(Path p) {
|
||||||
|
try {
|
||||||
|
new FileInputStream(p.toFile());
|
||||||
|
throw new AssertionError("Expected exception did not occur.");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
return ex.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testCommandPrefix() {
|
public void testCommandPrefix() {
|
||||||
test(a -> assertCommandCheckOutput(a, "/s",
|
test(a -> assertCommandCheckOutput(a, "/s",
|
||||||
assertStartsWith("| Command: /s is ambiguous: /seteditor, /save, /setstart")),
|
assertStartsWith("| Command: /s is ambiguous: /seteditor, /save, /setstart")),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue