162 lines
5.5 KiB
Rust
162 lines
5.5 KiB
Rust
use std::io::{BufRead, BufReader, Cursor, Write};
|
|
use std::net::TcpStream;
|
|
use std::process::{Command, Stdio};
|
|
use std::time::Duration;
|
|
|
|
use runa_dap::framing::{read_frame, write_frame};
|
|
use serde_json::{Value, json};
|
|
|
|
fn run(input: &[u8], args: &[&str]) -> std::process::Output {
|
|
let mut child = Command::new(env!("CARGO_BIN_EXE_runa-dap"))
|
|
.args(args)
|
|
.env_remove("RUNA_CONNECT")
|
|
.env_remove("RUNA_DAP_LISTEN")
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
child.stdin.take().unwrap().write_all(input).unwrap();
|
|
child.wait_with_output().unwrap()
|
|
}
|
|
|
|
fn messages(output: &[u8]) -> Vec<Value> {
|
|
let mut reader = Cursor::new(output);
|
|
let mut messages = Vec::new();
|
|
while let Some(message) = read_frame(&mut reader).unwrap() {
|
|
messages.push(message);
|
|
}
|
|
messages
|
|
}
|
|
|
|
#[test]
|
|
fn executable_exchanges_standard_frames_and_disconnects_without_a_server() {
|
|
let mut input = Vec::new();
|
|
write_frame(
|
|
&mut input,
|
|
&json!({"seq": 1, "type": "request", "command": "initialize"}),
|
|
)
|
|
.unwrap();
|
|
write_frame(
|
|
&mut input,
|
|
&json!({"seq": 2, "type": "request", "command": "launch"}),
|
|
)
|
|
.unwrap();
|
|
write_frame(
|
|
&mut input,
|
|
&json!({"seq": 3, "type": "request", "command": "disconnect"}),
|
|
)
|
|
.unwrap();
|
|
let output = run(&input, &[]);
|
|
assert!(output.status.success(), "{:?}", output.stderr);
|
|
let messages = messages(&output.stdout);
|
|
assert_eq!(
|
|
messages[0]["body"]["supportsConfigurationDoneRequest"],
|
|
true
|
|
);
|
|
assert_eq!(messages[1]["success"], false);
|
|
assert_eq!(messages[2]["success"], true);
|
|
assert_eq!(messages[3]["event"], "terminated");
|
|
for (index, message) in messages.iter().enumerate() {
|
|
assert_eq!(message["seq"], index + 1);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn executable_reports_bad_framing_on_protocol_and_stderr() {
|
|
let output = run(b"Content-Length: 1048577\r\n\r\n", &[]);
|
|
assert!(!output.status.success());
|
|
assert!(!output.stderr.is_empty());
|
|
let messages = messages(&output.stdout);
|
|
assert_eq!(messages[0]["event"], "output");
|
|
assert!(
|
|
messages[0]["body"]["output"]
|
|
.as_str()
|
|
.unwrap()
|
|
.contains("Invalid DAP framing")
|
|
);
|
|
assert_eq!(messages[1]["event"], "terminated");
|
|
}
|
|
|
|
#[test]
|
|
fn cli_documents_connect_and_rejects_nonloopback_addresses() {
|
|
let help = run(&[], &["--help"]);
|
|
assert!(help.status.success());
|
|
let help = String::from_utf8(help.stdout).unwrap();
|
|
assert!(help.contains("RUNA_CONNECT"));
|
|
assert!(help.contains("127.0.0.1:3737"));
|
|
assert!(help.contains("--listen"));
|
|
assert!(help.contains("RUNA_DAP_LISTEN"));
|
|
for address in ["0.0.0.0:3737", "192.0.2.1:3737", "invalid"] {
|
|
let output = run(&[], &["--connect", address]);
|
|
assert!(!output.status.success());
|
|
assert!(!output.stderr.is_empty());
|
|
}
|
|
for address in ["0.0.0.0:0", "[::]:0", "192.0.2.1:3737", "invalid"] {
|
|
let output = run(&[], &["--listen", address]);
|
|
assert!(!output.status.success());
|
|
assert!(!output.stderr.is_empty());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn tcp_listener_accepts_framed_sessions_and_survives_invalid_framing() {
|
|
let mut child = Command::new(env!("CARGO_BIN_EXE_runa-dap"))
|
|
.args(["--listen", "127.0.0.1:0"])
|
|
.env_remove("RUNA_CONNECT")
|
|
.env_remove("RUNA_DAP_LISTEN")
|
|
.stdin(Stdio::null())
|
|
.stdout(Stdio::null())
|
|
.stderr(Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
let mut line = String::new();
|
|
let mut diagnostics = BufReader::new(child.stderr.take().unwrap());
|
|
diagnostics.read_line(&mut line).unwrap();
|
|
let address = line.trim().strip_prefix("runa-dap: listening on ").unwrap();
|
|
for malformed in [true, false, false] {
|
|
let mut socket = TcpStream::connect(address).unwrap();
|
|
socket
|
|
.set_read_timeout(Some(Duration::from_secs(5)))
|
|
.unwrap();
|
|
if malformed {
|
|
socket
|
|
.write_all(b"Content-Length: 1048577\r\n\r\n")
|
|
.unwrap();
|
|
let mut reader = BufReader::new(socket);
|
|
assert_eq!(read_frame(&mut reader).unwrap().unwrap()["event"], "output");
|
|
assert_eq!(
|
|
read_frame(&mut reader).unwrap().unwrap()["event"],
|
|
"terminated"
|
|
);
|
|
} else {
|
|
write_frame(
|
|
&mut socket,
|
|
&json!({
|
|
"seq": 1, "type": "request", "command": "initialize"
|
|
}),
|
|
)
|
|
.unwrap();
|
|
let mut reader = BufReader::new(socket.try_clone().unwrap());
|
|
assert_eq!(read_frame(&mut reader).unwrap().unwrap()["success"], true);
|
|
write_frame(
|
|
&mut socket,
|
|
&json!({
|
|
"seq": 2, "type": "request", "command": "disconnect"
|
|
}),
|
|
)
|
|
.unwrap();
|
|
assert_eq!(read_frame(&mut reader).unwrap().unwrap()["success"], true);
|
|
assert_eq!(
|
|
read_frame(&mut reader).unwrap().unwrap()["event"],
|
|
"terminated"
|
|
);
|
|
}
|
|
}
|
|
}));
|
|
child.kill().unwrap();
|
|
child.wait().unwrap();
|
|
result.unwrap();
|
|
}
|