mirror of
https://github.com/denoland/deno.git
synced 2024-11-29 16:30:56 -05:00
1adf8ee545
This commit changes flow in inspector code to no longer require "Runtime.runIfWaitingForDebugger" message to complete a handshake. Even though clients like Chrome DevTools always send this message on startup, it is against the protocol to require this message to start an inspector session. Instead "Runtime.runIfWaitingForDebugger" is required only when running with "--inspect-brk" flag, which matches behavior of Node.js.
747 lines
22 KiB
Rust
747 lines
22 KiB
Rust
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
|
|
use deno_core::futures;
|
|
use deno_core::futures::prelude::*;
|
|
use deno_core::futures::stream::SplitSink;
|
|
use deno_core::serde_json;
|
|
use deno_core::url;
|
|
use deno_runtime::deno_fetch::reqwest;
|
|
use deno_runtime::deno_websocket::tokio_tungstenite;
|
|
use deno_runtime::deno_websocket::tokio_tungstenite::tungstenite;
|
|
use std::io::BufRead;
|
|
use std::pin::Pin;
|
|
use test_util as util;
|
|
use tokio::net::TcpStream;
|
|
|
|
fn inspect_flag_with_unique_port(flag_prefix: &str) -> String {
|
|
use std::sync::atomic::{AtomicU16, Ordering};
|
|
static PORT: AtomicU16 = AtomicU16::new(9229);
|
|
let port = PORT.fetch_add(1, Ordering::Relaxed);
|
|
format!("{}=127.0.0.1:{}", flag_prefix, port)
|
|
}
|
|
|
|
fn extract_ws_url_from_stderr(
|
|
stderr_lines: &mut impl std::iter::Iterator<Item = String>,
|
|
) -> url::Url {
|
|
let stderr_first_line = stderr_lines.next().unwrap();
|
|
assert!(stderr_first_line.starts_with("Debugger listening on "));
|
|
let v: Vec<_> = stderr_first_line.match_indices("ws:").collect();
|
|
assert_eq!(v.len(), 1);
|
|
let ws_url_index = v[0].0;
|
|
let ws_url = &stderr_first_line[ws_url_index..];
|
|
url::Url::parse(ws_url).unwrap()
|
|
}
|
|
|
|
fn assert_stderr_for_inspect(
|
|
stderr_lines: &mut impl std::iter::Iterator<Item = String>,
|
|
) {
|
|
assert_eq!(
|
|
&stderr_lines.next().unwrap(),
|
|
"Visit chrome://inspect to connect to the debugger."
|
|
);
|
|
}
|
|
|
|
fn assert_stderr_for_inspect_brk(
|
|
stderr_lines: &mut impl std::iter::Iterator<Item = String>,
|
|
) {
|
|
assert_eq!(
|
|
&stderr_lines.next().unwrap(),
|
|
"Visit chrome://inspect to connect to the debugger."
|
|
);
|
|
assert_eq!(
|
|
&stderr_lines.next().unwrap(),
|
|
"Deno is waiting for debugger to connect."
|
|
);
|
|
}
|
|
|
|
async fn assert_inspector_messages(
|
|
socket_tx: &mut SplitSink<
|
|
tokio_tungstenite::WebSocketStream<
|
|
tokio_tungstenite::MaybeTlsStream<TcpStream>,
|
|
>,
|
|
tungstenite::Message,
|
|
>,
|
|
messages: &[&str],
|
|
socket_rx: &mut Pin<Box<dyn Stream<Item = String>>>,
|
|
responses: &[&str],
|
|
notifications: &[&str],
|
|
) {
|
|
for msg in messages {
|
|
socket_tx.send(msg.to_string().into()).await.unwrap();
|
|
}
|
|
|
|
let expected_messages = responses.len() + notifications.len();
|
|
let mut responses_idx = 0;
|
|
let mut notifications_idx = 0;
|
|
|
|
for _ in 0..expected_messages {
|
|
let msg = socket_rx.next().await.unwrap();
|
|
|
|
if msg.starts_with(r#"{"id":"#) {
|
|
assert!(
|
|
msg.starts_with(responses[responses_idx]),
|
|
"Doesn't start with {}, instead received {}",
|
|
responses[responses_idx],
|
|
msg
|
|
);
|
|
responses_idx += 1;
|
|
} else {
|
|
assert!(
|
|
msg.starts_with(notifications[notifications_idx]),
|
|
"Doesn't start with {}, instead received {}",
|
|
notifications[notifications_idx],
|
|
msg
|
|
);
|
|
notifications_idx += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn inspector_connect() {
|
|
let script = util::testdata_path().join("inspector1.js");
|
|
let mut child = util::deno_cmd()
|
|
.arg("run")
|
|
.arg(inspect_flag_with_unique_port("--inspect"))
|
|
.arg(script)
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr = child.stderr.as_mut().unwrap();
|
|
let mut stderr_lines =
|
|
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
|
|
let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
|
|
|
|
// We use tokio_tungstenite as a websocket client because warp (which is
|
|
// a dependency of Deno) uses it.
|
|
let (_socket, response) = tokio_tungstenite::connect_async(ws_url)
|
|
.await
|
|
.expect("Can't connect");
|
|
assert_eq!("101 Switching Protocols", response.status().to_string());
|
|
child.kill().unwrap();
|
|
child.wait().unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn inspector_break_on_first_line() {
|
|
let script = util::testdata_path().join("inspector2.js");
|
|
let mut child = util::deno_cmd()
|
|
.arg("run")
|
|
.arg(inspect_flag_with_unique_port("--inspect-brk"))
|
|
.arg(script)
|
|
.stdout(std::process::Stdio::piped())
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr = child.stderr.as_mut().unwrap();
|
|
let mut stderr_lines =
|
|
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
|
|
let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
|
|
|
|
let (socket, response) = tokio_tungstenite::connect_async(ws_url)
|
|
.await
|
|
.expect("Can't connect");
|
|
assert_eq!(response.status(), 101); // Switching protocols.
|
|
|
|
let (mut socket_tx, socket_rx) = socket.split();
|
|
let mut socket_rx = socket_rx
|
|
.map(|msg| msg.unwrap().to_string())
|
|
.filter(|msg| {
|
|
let pass = !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#);
|
|
futures::future::ready(pass)
|
|
})
|
|
.boxed_local();
|
|
|
|
let stdout = child.stdout.as_mut().unwrap();
|
|
let mut stdout_lines =
|
|
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
|
|
|
|
assert_stderr_for_inspect_brk(&mut stderr_lines);
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[
|
|
r#"{"id":1,"method":"Runtime.enable"}"#,
|
|
r#"{"id":2,"method":"Debugger.enable"}"#,
|
|
],
|
|
&mut socket_rx,
|
|
&[
|
|
r#"{"id":1,"result":{}}"#,
|
|
r#"{"id":2,"result":{"debuggerId":"#,
|
|
],
|
|
&[
|
|
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
|
|
],
|
|
)
|
|
.await;
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[r#"{"id":3,"method":"Runtime.runIfWaitingForDebugger"}"#],
|
|
&mut socket_rx,
|
|
&[r#"{"id":3,"result":{}}"#],
|
|
&[r#"{"method":"Debugger.paused","#],
|
|
)
|
|
.await;
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[
|
|
r#"{"id":4,"method":"Runtime.evaluate","params":{"expression":"Deno.core.print(\"hello from the inspector\\n\")","contextId":1,"includeCommandLineAPI":true,"silent":false,"returnByValue":true}}"#,
|
|
],
|
|
&mut socket_rx,
|
|
&[r#"{"id":4,"result":{"result":{"type":"undefined"}}}"#],
|
|
&[],
|
|
)
|
|
.await;
|
|
|
|
assert_eq!(&stdout_lines.next().unwrap(), "hello from the inspector");
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[r#"{"id":5,"method":"Debugger.resume"}"#],
|
|
&mut socket_rx,
|
|
&[r#"{"id":5,"result":{}}"#],
|
|
&[],
|
|
)
|
|
.await;
|
|
|
|
assert_eq!(&stdout_lines.next().unwrap(), "hello from the script");
|
|
|
|
child.kill().unwrap();
|
|
child.wait().unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn inspector_pause() {
|
|
let script = util::testdata_path().join("inspector1.js");
|
|
let mut child = util::deno_cmd()
|
|
.arg("run")
|
|
.arg(inspect_flag_with_unique_port("--inspect"))
|
|
.arg(script)
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr = child.stderr.as_mut().unwrap();
|
|
let mut stderr_lines =
|
|
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
|
|
let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
|
|
|
|
// We use tokio_tungstenite as a websocket client because warp (which is
|
|
// a dependency of Deno) uses it.
|
|
let (mut socket, _) = tokio_tungstenite::connect_async(ws_url)
|
|
.await
|
|
.expect("Can't connect");
|
|
|
|
/// Returns the next websocket message as a string ignoring
|
|
/// Debugger.scriptParsed messages.
|
|
async fn ws_read_msg(
|
|
socket: &mut tokio_tungstenite::WebSocketStream<
|
|
tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
|
|
>,
|
|
) -> String {
|
|
use deno_core::futures::stream::StreamExt;
|
|
while let Some(msg) = socket.next().await {
|
|
let msg = msg.unwrap().to_string();
|
|
// FIXME(bartlomieju): fails because there's a file loaded
|
|
// called 150_errors.js
|
|
// assert!(!msg.contains("error"));
|
|
if !msg.contains("Debugger.scriptParsed") {
|
|
return msg;
|
|
}
|
|
}
|
|
unreachable!()
|
|
}
|
|
|
|
socket
|
|
.send(r#"{"id":6,"method":"Debugger.enable"}"#.into())
|
|
.await
|
|
.unwrap();
|
|
|
|
let msg = ws_read_msg(&mut socket).await;
|
|
println!("response msg 1 {}", msg);
|
|
assert!(msg.starts_with(r#"{"id":6,"result":{"debuggerId":"#));
|
|
|
|
socket
|
|
.send(r#"{"id":31,"method":"Debugger.pause"}"#.into())
|
|
.await
|
|
.unwrap();
|
|
|
|
let msg = ws_read_msg(&mut socket).await;
|
|
println!("response msg 2 {}", msg);
|
|
assert_eq!(msg, r#"{"id":31,"result":{}}"#);
|
|
|
|
child.kill().unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn inspector_port_collision() {
|
|
// Skip this test on WSL, which allows multiple processes to listen on the
|
|
// same port, rather than making `bind()` fail with `EADDRINUSE`.
|
|
if cfg!(target_os = "linux") && std::env::var_os("WSL_DISTRO_NAME").is_some()
|
|
{
|
|
return;
|
|
}
|
|
|
|
let script = util::testdata_path().join("inspector1.js");
|
|
let inspect_flag = inspect_flag_with_unique_port("--inspect");
|
|
|
|
let mut child1 = util::deno_cmd()
|
|
.arg("run")
|
|
.arg(&inspect_flag)
|
|
.arg(script.clone())
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr_1 = child1.stderr.as_mut().unwrap();
|
|
let mut stderr_1_lines = std::io::BufReader::new(stderr_1)
|
|
.lines()
|
|
.map(|r| r.unwrap());
|
|
let _ = extract_ws_url_from_stderr(&mut stderr_1_lines);
|
|
|
|
let mut child2 = util::deno_cmd()
|
|
.arg("run")
|
|
.arg(&inspect_flag)
|
|
.arg(script)
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr_2 = child2.stderr.as_mut().unwrap();
|
|
let stderr_2_error_message = std::io::BufReader::new(stderr_2)
|
|
.lines()
|
|
.map(|r| r.unwrap())
|
|
.inspect(|line| assert!(!line.contains("Debugger listening")))
|
|
.find(|line| line.contains("Cannot start inspector server"));
|
|
assert!(stderr_2_error_message.is_some());
|
|
|
|
child1.kill().unwrap();
|
|
child1.wait().unwrap();
|
|
child2.wait().unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn inspector_does_not_hang() {
|
|
let script = util::testdata_path().join("inspector3.js");
|
|
let mut child = util::deno_cmd()
|
|
.arg("run")
|
|
.arg(inspect_flag_with_unique_port("--inspect-brk"))
|
|
.env("NO_COLOR", "1")
|
|
.arg(script)
|
|
.stdout(std::process::Stdio::piped())
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr = child.stderr.as_mut().unwrap();
|
|
let mut stderr_lines =
|
|
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
|
|
let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
|
|
|
|
let (socket, response) = tokio_tungstenite::connect_async(ws_url)
|
|
.await
|
|
.expect("Can't connect");
|
|
assert_eq!(response.status(), 101); // Switching protocols.
|
|
|
|
let (mut socket_tx, socket_rx) = socket.split();
|
|
let mut socket_rx = socket_rx
|
|
.map(|msg| msg.unwrap().to_string())
|
|
.filter(|msg| {
|
|
let pass = !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#);
|
|
futures::future::ready(pass)
|
|
})
|
|
.boxed_local();
|
|
|
|
let stdout = child.stdout.as_mut().unwrap();
|
|
let mut stdout_lines =
|
|
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
|
|
|
|
assert_stderr_for_inspect_brk(&mut stderr_lines);
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[
|
|
r#"{"id":1,"method":"Runtime.enable"}"#,
|
|
r#"{"id":2,"method":"Debugger.enable"}"#,
|
|
],
|
|
&mut socket_rx,
|
|
&[
|
|
r#"{"id":1,"result":{}}"#,
|
|
r#"{"id":2,"result":{"debuggerId":"#
|
|
],
|
|
&[
|
|
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#
|
|
],
|
|
)
|
|
.await;
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[r#"{"id":3,"method":"Runtime.runIfWaitingForDebugger"}"#],
|
|
&mut socket_rx,
|
|
&[r#"{"id":3,"result":{}}"#],
|
|
&[r#"{"method":"Debugger.paused","#],
|
|
)
|
|
.await;
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[r#"{"id":4,"method":"Debugger.resume"}"#],
|
|
&mut socket_rx,
|
|
&[r#"{"id":4,"result":{}}"#],
|
|
&[r#"{"method":"Debugger.resumed","params":{}}"#],
|
|
)
|
|
.await;
|
|
|
|
for i in 0..128u32 {
|
|
let request_id = i + 10;
|
|
// Expect the number {i} on stdout.
|
|
let s = i.to_string();
|
|
assert_eq!(stdout_lines.next().unwrap(), s);
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[],
|
|
&mut socket_rx,
|
|
&[],
|
|
&[
|
|
r#"{"method":"Runtime.consoleAPICalled","#,
|
|
r#"{"method":"Debugger.paused","#,
|
|
],
|
|
)
|
|
.await;
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[&format!(
|
|
r#"{{"id":{},"method":"Debugger.resume"}}"#,
|
|
request_id
|
|
)],
|
|
&mut socket_rx,
|
|
&[&format!(r#"{{"id":{},"result":{{}}}}"#, request_id)],
|
|
&[r#"{"method":"Debugger.resumed","params":{}}"#],
|
|
)
|
|
.await;
|
|
}
|
|
|
|
// Check that we can gracefully close the websocket connection.
|
|
socket_tx.close().await.unwrap();
|
|
socket_rx.for_each(|_| async {}).await;
|
|
|
|
assert_eq!(&stdout_lines.next().unwrap(), "done");
|
|
assert!(child.wait().unwrap().success());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn inspector_without_brk_runs_code() {
|
|
let script = util::testdata_path().join("inspector4.js");
|
|
let mut child = util::deno_cmd()
|
|
.arg("run")
|
|
.arg(inspect_flag_with_unique_port("--inspect"))
|
|
.arg(script)
|
|
.stdout(std::process::Stdio::piped())
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr = child.stderr.as_mut().unwrap();
|
|
let mut stderr_lines =
|
|
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
|
|
let _ = extract_ws_url_from_stderr(&mut stderr_lines);
|
|
|
|
// Check that inspector actually runs code without waiting for inspector
|
|
// connection.
|
|
let stdout = child.stdout.as_mut().unwrap();
|
|
let mut stdout_lines =
|
|
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
|
|
let stdout_first_line = stdout_lines.next().unwrap();
|
|
assert_eq!(stdout_first_line, "hello");
|
|
|
|
child.kill().unwrap();
|
|
child.wait().unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn inspector_runtime_evaluate_does_not_crash() {
|
|
let mut child = util::deno_cmd()
|
|
.arg("repl")
|
|
.arg(inspect_flag_with_unique_port("--inspect"))
|
|
.stdin(std::process::Stdio::piped())
|
|
.stdout(std::process::Stdio::piped())
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr = child.stderr.as_mut().unwrap();
|
|
let mut stderr_lines = std::io::BufReader::new(stderr)
|
|
.lines()
|
|
.map(|r| r.unwrap())
|
|
.filter(|s| s.as_str() != "Debugger session started.");
|
|
let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
|
|
|
|
let (socket, response) = tokio_tungstenite::connect_async(ws_url)
|
|
.await
|
|
.expect("Can't connect");
|
|
assert_eq!(response.status(), 101); // Switching protocols.
|
|
|
|
let (mut socket_tx, socket_rx) = socket.split();
|
|
let mut socket_rx = socket_rx
|
|
.map(|msg| msg.unwrap().to_string())
|
|
.filter(|msg| {
|
|
let pass = !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#);
|
|
futures::future::ready(pass)
|
|
})
|
|
.boxed_local();
|
|
|
|
let stdin = child.stdin.take().unwrap();
|
|
|
|
let stdout = child.stdout.as_mut().unwrap();
|
|
let mut stdout_lines = std::io::BufReader::new(stdout)
|
|
.lines()
|
|
.map(|r| r.unwrap())
|
|
.filter(|s| !s.starts_with("Deno "));
|
|
|
|
assert_stderr_for_inspect(&mut stderr_lines);
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[
|
|
r#"{"id":1,"method":"Runtime.enable"}"#,
|
|
r#"{"id":2,"method":"Debugger.enable"}"#,
|
|
],
|
|
&mut socket_rx,
|
|
&[
|
|
r#"{"id":1,"result":{}}"#,
|
|
r#"{"id":2,"result":{"debuggerId":"#,
|
|
],
|
|
&[
|
|
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
|
|
],
|
|
)
|
|
.await;
|
|
|
|
assert_eq!(
|
|
&stdout_lines.next().unwrap(),
|
|
"exit using ctrl+d or close()"
|
|
);
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[
|
|
r#"{"id":3,"method":"Runtime.compileScript","params":{"expression":"Deno.cwd()","sourceURL":"","persistScript":false,"executionContextId":1}}"#,
|
|
],
|
|
&mut socket_rx,
|
|
&[r#"{"id":3,"result":{}}"#], &[]
|
|
).await;
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[
|
|
r#"{"id":4,"method":"Runtime.evaluate","params":{"expression":"Deno.cwd()","objectGroup":"console","includeCommandLineAPI":true,"silent":false,"contextId":1,"returnByValue":true,"generatePreview":true,"userGesture":true,"awaitPromise":false,"replMode":true}}"#,
|
|
],
|
|
&mut socket_rx,
|
|
&[r#"{"id":4,"result":{"result":{"type":"string","value":""#],
|
|
&[],
|
|
).await;
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[
|
|
r#"{"id":5,"method":"Runtime.evaluate","params":{"expression":"console.error('done');","objectGroup":"console","includeCommandLineAPI":true,"silent":false,"contextId":1,"returnByValue":true,"generatePreview":true,"userGesture":true,"awaitPromise":false,"replMode":true}}"#,
|
|
],
|
|
&mut socket_rx,
|
|
&[r#"{"id":5,"result":{"result":{"type":"undefined"}}}"#],
|
|
&[r#"{"method":"Runtime.consoleAPICalled"#],
|
|
).await;
|
|
|
|
assert_eq!(&stderr_lines.next().unwrap(), "done");
|
|
|
|
drop(stdin);
|
|
child.wait().unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn inspector_json() {
|
|
let script = util::testdata_path().join("inspector1.js");
|
|
let mut child = util::deno_cmd()
|
|
.arg("run")
|
|
.arg(inspect_flag_with_unique_port("--inspect"))
|
|
.arg(script)
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr = child.stderr.as_mut().unwrap();
|
|
let mut stderr_lines =
|
|
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
|
|
let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
|
|
let mut url = ws_url.clone();
|
|
let _ = url.set_scheme("http");
|
|
url.set_path("/json");
|
|
let resp = reqwest::get(url).await.unwrap();
|
|
assert_eq!(resp.status(), reqwest::StatusCode::OK);
|
|
let endpoint_list: Vec<deno_core::serde_json::Value> =
|
|
serde_json::from_str(&resp.text().await.unwrap()).unwrap();
|
|
let matching_endpoint = endpoint_list
|
|
.iter()
|
|
.find(|e| e["webSocketDebuggerUrl"] == ws_url.as_str());
|
|
assert!(matching_endpoint.is_some());
|
|
child.kill().unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn inspector_json_list() {
|
|
let script = util::testdata_path().join("inspector1.js");
|
|
let mut child = util::deno_cmd()
|
|
.arg("run")
|
|
.arg(inspect_flag_with_unique_port("--inspect"))
|
|
.arg(script)
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr = child.stderr.as_mut().unwrap();
|
|
let mut stderr_lines =
|
|
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
|
|
let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
|
|
let mut url = ws_url.clone();
|
|
let _ = url.set_scheme("http");
|
|
url.set_path("/json/list");
|
|
let resp = reqwest::get(url).await.unwrap();
|
|
assert_eq!(resp.status(), reqwest::StatusCode::OK);
|
|
let endpoint_list: Vec<deno_core::serde_json::Value> =
|
|
serde_json::from_str(&resp.text().await.unwrap()).unwrap();
|
|
let matching_endpoint = endpoint_list
|
|
.iter()
|
|
.find(|e| e["webSocketDebuggerUrl"] == ws_url.as_str());
|
|
assert!(matching_endpoint.is_some());
|
|
child.kill().unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn inspector_connect_non_ws() {
|
|
// https://github.com/denoland/deno/issues/11449
|
|
// Verify we don't panic if non-WS connection is being established
|
|
let script = util::testdata_path().join("inspector1.js");
|
|
let mut child = util::deno_cmd()
|
|
.arg("run")
|
|
.arg(inspect_flag_with_unique_port("--inspect"))
|
|
.arg(script)
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr = child.stderr.as_mut().unwrap();
|
|
let mut stderr_lines =
|
|
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
|
|
let mut ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
|
|
// Change scheme to URL and try send a request. We're not interested
|
|
// in the request result, just that the process doesn't panic.
|
|
ws_url.set_scheme("http").unwrap();
|
|
let resp = reqwest::get(ws_url).await.unwrap();
|
|
assert_eq!("400 Bad Request", resp.status().to_string());
|
|
child.kill().unwrap();
|
|
child.wait().unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn inspector_break_on_first_line_in_test() {
|
|
let script = util::testdata_path().join("inspector_test.js");
|
|
let mut child = util::deno_cmd()
|
|
.arg("test")
|
|
.arg(inspect_flag_with_unique_port("--inspect-brk"))
|
|
.arg(script)
|
|
.stdout(std::process::Stdio::piped())
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let stderr = child.stderr.as_mut().unwrap();
|
|
let mut stderr_lines =
|
|
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
|
|
let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
|
|
|
|
let (socket, response) = tokio_tungstenite::connect_async(ws_url)
|
|
.await
|
|
.expect("Can't connect");
|
|
assert_eq!(response.status(), 101); // Switching protocols.
|
|
|
|
let (mut socket_tx, socket_rx) = socket.split();
|
|
let mut socket_rx = socket_rx
|
|
.map(|msg| msg.unwrap().to_string())
|
|
.filter(|msg| {
|
|
let pass = !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#);
|
|
futures::future::ready(pass)
|
|
})
|
|
.boxed_local();
|
|
|
|
let stdout = child.stdout.as_mut().unwrap();
|
|
let mut stdout_lines =
|
|
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
|
|
|
|
assert_stderr_for_inspect_brk(&mut stderr_lines);
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[
|
|
r#"{"id":1,"method":"Runtime.enable"}"#,
|
|
r#"{"id":2,"method":"Debugger.enable"}"#,
|
|
],
|
|
&mut socket_rx,
|
|
&[
|
|
r#"{"id":1,"result":{}}"#,
|
|
r#"{"id":2,"result":{"debuggerId":"#,
|
|
],
|
|
&[
|
|
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
|
|
],
|
|
)
|
|
.await;
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[r#"{"id":3,"method":"Runtime.runIfWaitingForDebugger"}"#],
|
|
&mut socket_rx,
|
|
&[r#"{"id":3,"result":{}}"#],
|
|
&[r#"{"method":"Debugger.paused","#],
|
|
)
|
|
.await;
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[
|
|
r#"{"id":4,"method":"Runtime.evaluate","params":{"expression":"Deno.core.print(\"hello from the inspector\\n\")","contextId":1,"includeCommandLineAPI":true,"silent":false,"returnByValue":true}}"#,
|
|
],
|
|
&mut socket_rx,
|
|
&[r#"{"id":4,"result":{"result":{"type":"undefined"}}}"#],
|
|
&[],
|
|
)
|
|
.await;
|
|
|
|
assert_eq!(&stdout_lines.next().unwrap(), "hello from the inspector");
|
|
|
|
assert_inspector_messages(
|
|
&mut socket_tx,
|
|
&[r#"{"id":5,"method":"Debugger.resume"}"#],
|
|
&mut socket_rx,
|
|
&[r#"{"id":5,"result":{}}"#],
|
|
&[],
|
|
)
|
|
.await;
|
|
|
|
assert!(&stdout_lines
|
|
.next()
|
|
.unwrap()
|
|
.starts_with("running 1 test from"));
|
|
assert!(&stdout_lines
|
|
.next()
|
|
.unwrap()
|
|
.contains("test has finished running"));
|
|
|
|
child.kill().unwrap();
|
|
child.wait().unwrap();
|
|
}
|