From 258ad9b3ba07b3c9d205d9fd65f3b48729f44ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Sun, 12 Apr 2020 13:33:21 +0200 Subject: [PATCH 1/9] simple test --- cli/tests/inspector_coverage.js | 16 +++++++ cli/tests/integration_tests.rs | 75 +++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 cli/tests/inspector_coverage.js diff --git a/cli/tests/inspector_coverage.js b/cli/tests/inspector_coverage.js new file mode 100644 index 00000000000000..3db0e5a19b6788 --- /dev/null +++ b/cli/tests/inspector_coverage.js @@ -0,0 +1,16 @@ +function a() { + console.log("hello a"); +} + +function b() { + console.log("hello b"); +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function c() { + console.log("hello c"); +} + +a(); +b(); +b(); diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 819ccda728a2f3..5566c95097bd9e 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -2213,6 +2213,81 @@ async fn inspector_break_on_first_line() { child.kill().unwrap(); } +#[tokio::test] +async fn inspector_coverage() { + let script = deno::test_util::root_path() + .join("cli") + .join("tests") + .join("inspector_coverage.js"); + let mut child = util::deno_cmd() + .arg("run") + // Warning: each inspector test should be on its own port to avoid + // conflicting with another inspector test. + .arg("--inspect=127.0.0.1:9232") + .arg(script) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn() + .unwrap(); + + let stderr = child.stderr.as_mut().unwrap(); + let ws_url = extract_ws_url_from_stderr(stderr); + 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, mut socket_rx) = socket.split(); + + let stdout = child.stdout.as_mut().unwrap(); + let mut stdout_lines = std::io::BufReader::new(stdout).lines(); + + use TestStep::*; + let test_steps = vec![ + WsSend(r#"{"id":1,"method":"Runtime.enable"}"#), + WsSend(r#"{"id":2,"method":"Profiler.enable"}"#), + WsSend( + r#"{"id":3,"method":"Profiler.startPreciseCoverage", "params": {"callCount": false, "detailed": true } }"#, + ), + WsRecv( + r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#, + ), + WsRecv(r#"{"id":1,"result":{}}"#), + WsRecv(r#"{"id":2,"result":{}}"#), + WsRecv(r#"{"id":3,"result":{"timestamp":"#), + WsSend(r#"{"id":4,"method":"Runtime.runIfWaitingForDebugger"}"#), + WsRecv(r#"{"id":4,"result":{}}"#), + StdOut("hello a"), + StdOut("hello b"), + StdOut("hello b"), + WsSend(r#"{"id":5,"method":"Profiler.takePreciseCoverage"}"#), + WsSend(r#"{"id":6,"method":"Profiler.stopPreciseCoverage"}"#), + WsRecv(r#"{"id":5,"result":{"result":[{"#), + ]; + + for step in test_steps { + match step { + StdOut(s) => match stdout_lines.next() { + Some(Ok(line)) => assert_eq!(line, s), + other => panic!(other), + }, + WsRecv(s) => loop { + let msg = match socket_rx.next().await { + Some(Ok(msg)) => msg.to_string(), + other => panic!(other), + }; + if !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#) { + assert!(msg.starts_with(s)); + break; + } + }, + WsSend(s) => socket_tx.send(s.into()).await.unwrap(), + } + } + + child.kill().unwrap(); +} + #[tokio::test] async fn inspector_pause() { let script = deno::test_util::root_path() From c43318b0ef1875fe1c4e9e1d57c152f771d2accb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Sun, 12 Apr 2020 14:41:54 +0200 Subject: [PATCH 2/9] try to use inspect-brk --- cli/tests/integration_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 5566c95097bd9e..cabc7a6af65018 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -2223,7 +2223,7 @@ async fn inspector_coverage() { .arg("run") // Warning: each inspector test should be on its own port to avoid // conflicting with another inspector test. - .arg("--inspect=127.0.0.1:9232") + .arg("--inspect-brk=127.0.0.1:9231") .arg(script) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) From 4bf1bdce47322de64fa22e034034e5c959b72b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Apr 2020 15:24:56 +0200 Subject: [PATCH 3/9] prototype --- cli/Cargo.toml | 3 +- cli/coverage.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ cli/lib.rs | 32 ++++++++++++++++++- 3 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 cli/coverage.rs diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 92fc58208f38c6..87b8df5f275667 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -64,6 +64,7 @@ walkdir = "2.3.1" warp = "0.2.2" semver-parser = "0.9.0" uuid = { version = "0.8.1", features = ["v4"] } +tokio-tungstenite = { version = "0.10.1", features = ["connect"] } [target.'cfg(windows)'.dependencies] winapi = "0.3.8" @@ -75,7 +76,7 @@ nix = "0.17.0" [dev-dependencies] os_pipe = "0.9.1" # Used for testing inspector. Keep in-sync with warp. -tokio-tungstenite = { version = "0.10.1", features = ["connect"] } +# tokio-tungstenite = { version = "0.10.1", features = ["connect"] } [target.'cfg(unix)'.dev-dependencies] pty = "0.2.2" diff --git a/cli/coverage.rs b/cli/coverage.rs new file mode 100644 index 00000000000000..3c82678820fb08 --- /dev/null +++ b/cli/coverage.rs @@ -0,0 +1,85 @@ +use url::Url; + +pub struct CoverageCollector { + url: Url, +} + +impl CoverageCollector { + pub fn new(url: Url) -> Self { + Self { + url, + } + } + + pub fn connect(&self) { + todo!() + } + + pub fn start_collecting(&self) { + todo!() + } + + pub fn stop_collecting(&self) { + todo!() + } + + pub fn get_report(&self) -> String { + todo!() + } +} + + +// pub fn run_coverage_collector_thread( + +// ) -> Result<(JoinHandle<()>, WebWorkerHandle), ErrBox> { +// let (handle_sender, handle_receiver) = +// std::sync::mpsc::sync_channel::>(1); + +// let builder = +// std::thread::Builder::new().name("deno-coverage-collector".to_string()); + +// let join_handle = std::thread::spawn(|| { +// let fut = async move { +// let (socket, response) = tokio_tungstenite::connect_async(inspector_url) +// .await +// .expect("Can't connect"); +// assert_eq!(response.status(), 101); + +// let mut msg_id = 1; + +// let (mut socket_tx, mut socket_rx) = socket.split(); + +// // let test_steps = vec![ +// // WsSend(r#"{"id":1,"method":"Runtime.enable"}"#), +// // WsSend(r#"{"id":2,"method":"Profiler.enable"}"#), +// // WsSend( +// // r#"{"id":3,"method":"Profiler.startPreciseCoverage", "params": {"callCount": false, "detailed": true } }"#, +// // ), +// // WsRecv( +// // r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#, +// // ), +// // WsRecv(r#"{"id":1,"result":{}}"#), +// // WsRecv(r#"{"id":2,"result":{}}"#), +// // WsRecv(r#"{"id":3,"result":{"timestamp":"#), +// // WsSend(r#"{"id":4,"method":"Runtime.runIfWaitingForDebugger"}"#), +// // WsRecv(r#"{"id":4,"result":{}}"#), +// // StdOut("hello a"), +// // StdOut("hello b"), +// // StdOut("hello b"), +// // WsSend(r#"{"id":5,"method":"Profiler.takePreciseCoverage"}"#), +// // WsSend(r#"{"id":6,"method":"Profiler.stopPreciseCoverage"}"#), +// // WsRecv(r#"{"id":5,"result":{"result":[{"#), +// // ]; + +// socket_tx.send(r#"{"id":1,"method":"Runtime.enable"}"#).await.unwrap(); +// socket_tx.send(r#"{"id":2,"method":"Profiler.enable"}"#).await.unwrap(); +// socket_tx.send(r#"{"id":3,"method":"Profiler.startPreciseCoverage", "params": {"callCount": false, "detailed": true } }"#).await.unwrap(); + +// }.boxed_local(); + +// tokio_util::run_basic(fut) +// }); + +// } + + diff --git a/cli/lib.rs b/cli/lib.rs index 467c05708ff325..766e19de607e1f 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -24,6 +24,7 @@ extern crate url; mod checksum; pub mod colors; pub mod compilers; +mod coverage; pub mod deno_dir; pub mod diagnostics; mod disk_cache; @@ -66,6 +67,7 @@ pub use dprint_plugin_typescript::swc_ecma_ast; pub use dprint_plugin_typescript::swc_ecma_parser; use crate::compilers::TargetLib; +use crate::coverage::CoverageCollector; use crate::doc::parser::DocFileLoader; use crate::file_fetcher::SourceFile; use crate::file_fetcher::SourceFileFetcher; @@ -503,8 +505,20 @@ async fn test_command( test_runner::render_test_file(test_modules, fail_fast, filter); let main_module = ModuleSpecifier::resolve_url(&test_file_url.to_string()).unwrap(); + + // TODO(bartlomieju): + // * for --coverage create new `tokio_tungstenite` connection here, + // probably on separate thread + // * ensure that --inspect flag is on so inspector is available + + let inspector_url = Url::parse("ws://127.0.0.1:9229").unwrap(); + let coverage_collector = CoverageCollector::new(inspector_url); + let mut worker = create_main_worker(global_state.clone(), main_module.clone())?; + + coverage_collector.connect(); + // Create a dummy source file. let source_file = SourceFile { filename: test_file_url.to_file_path().unwrap(), @@ -521,11 +535,27 @@ async fn test_command( .global_state .file_fetcher .save_source_file_in_cache(&main_module, source_file); + + // TODO: * start collecting coverage + coverage_collector.start_collecting(); + let execute_result = worker.execute_module(&main_module).await; execute_result?; worker.execute("window.dispatchEvent(new Event('load'))")?; (&mut *worker).await?; - worker.execute("window.dispatchEvent(new Event('unload'))") + worker.execute("window.dispatchEvent(new Event('unload'))")?; + + // TODO(bartlomieju): + // * stop collecting collecting coverage + // * wait for inspector client thread to join and return + // coverage JSON struct + // * parse coverage report to CoverageParser together with list + // of test files and prepare a test/JSON report + coverage_collector.stop_collecting(); + let coverage_report = coverage_collector.get_report(); + eprintln!("coverage report: {}", coverage_report); + + Ok(()) } pub fn main() { From e359a353aa0a33b8ec3003541f1e1d2a8b35cd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Apr 2020 15:55:57 +0200 Subject: [PATCH 4/9] prototype2 --- cli/coverage.rs | 130 +++++++++++++++++++----------------------------- cli/lib.rs | 18 +++---- 2 files changed, 60 insertions(+), 88 deletions(-) diff --git a/cli/coverage.rs b/cli/coverage.rs index 3c82678820fb08..4a03ebecfa8495 100644 --- a/cli/coverage.rs +++ b/cli/coverage.rs @@ -1,85 +1,59 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +#![allow(unused)] + +use crate::tokio_util; +use deno_core::ErrBox; +use std; +use tokio_tungstenite; use url::Url; +use crate::futures::StreamExt; +use crate::futures::SinkExt; pub struct CoverageCollector { - url: Url, + msg_id: usize, + socket: tokio_tungstenite::WebSocketStream, } impl CoverageCollector { - pub fn new(url: Url) -> Self { - Self { - url, - } - } - - pub fn connect(&self) { - todo!() - } - - pub fn start_collecting(&self) { - todo!() - } - - pub fn stop_collecting(&self) { - todo!() - } - - pub fn get_report(&self) -> String { - todo!() - } + pub async fn connect(url: Url) -> Result { + let (socket, response) = tokio_tungstenite::connect_async(url) + .await + .expect("Can't connect"); + assert_eq!(response.status(), 101); + + let mut collector = Self { + msg_id: 1, + socket, + }; + + eprintln!("start"); + collector.socket.send(r#"{"id":1,"method":"Runtime.enable"}"#.into()).await.unwrap(); + eprintln!("start1"); + let msg = collector.socket.next().await.unwrap(); + eprintln!("start2"); + dbg!(msg); + collector.socket.send(r#"{"id":2,"method":"Profiler.enable"}"#.into()).await.unwrap(); + let msg = collector.socket.next().await.unwrap(); + dbg!(msg); + collector.socket.send(r#"{"id":3,"method":"Profiler.startPreciseCoverage", "params": {"callCount": false, "detailed": true } }"#.into()).await.unwrap(); + let msg = collector.socket.next().await.unwrap(); + dbg!(msg); + collector.socket.send(r#"{"id":4,"method":"Runtime.runIfWaitingForDebugger" }"#.into()).await.unwrap(); + let msg = collector.socket.next().await.unwrap(); + dbg!(msg); + + Ok(collector) + } + + pub async fn get_report(&mut self) -> Result { + self.socket.send(r#"{"id":5,"method":"Runtime.takePreciseCoverage" }"#.into()).await.unwrap(); + let msg = self.socket.next().await.unwrap(); + dbg!(msg); + self.socket.send(r#"{"id":6,"method":"Runtime.stopPreciseCoverage" }"#.into()).await.unwrap(); + let msg = self.socket.next().await.unwrap(); + dbg!(msg); + + todo!() + } } - - -// pub fn run_coverage_collector_thread( - -// ) -> Result<(JoinHandle<()>, WebWorkerHandle), ErrBox> { -// let (handle_sender, handle_receiver) = -// std::sync::mpsc::sync_channel::>(1); - -// let builder = -// std::thread::Builder::new().name("deno-coverage-collector".to_string()); - -// let join_handle = std::thread::spawn(|| { -// let fut = async move { -// let (socket, response) = tokio_tungstenite::connect_async(inspector_url) -// .await -// .expect("Can't connect"); -// assert_eq!(response.status(), 101); - -// let mut msg_id = 1; - -// let (mut socket_tx, mut socket_rx) = socket.split(); - -// // let test_steps = vec![ -// // WsSend(r#"{"id":1,"method":"Runtime.enable"}"#), -// // WsSend(r#"{"id":2,"method":"Profiler.enable"}"#), -// // WsSend( -// // r#"{"id":3,"method":"Profiler.startPreciseCoverage", "params": {"callCount": false, "detailed": true } }"#, -// // ), -// // WsRecv( -// // r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#, -// // ), -// // WsRecv(r#"{"id":1,"result":{}}"#), -// // WsRecv(r#"{"id":2,"result":{}}"#), -// // WsRecv(r#"{"id":3,"result":{"timestamp":"#), -// // WsSend(r#"{"id":4,"method":"Runtime.runIfWaitingForDebugger"}"#), -// // WsRecv(r#"{"id":4,"result":{}}"#), -// // StdOut("hello a"), -// // StdOut("hello b"), -// // StdOut("hello b"), -// // WsSend(r#"{"id":5,"method":"Profiler.takePreciseCoverage"}"#), -// // WsSend(r#"{"id":6,"method":"Profiler.stopPreciseCoverage"}"#), -// // WsRecv(r#"{"id":5,"result":{"result":[{"#), -// // ]; - -// socket_tx.send(r#"{"id":1,"method":"Runtime.enable"}"#).await.unwrap(); -// socket_tx.send(r#"{"id":2,"method":"Profiler.enable"}"#).await.unwrap(); -// socket_tx.send(r#"{"id":3,"method":"Profiler.startPreciseCoverage", "params": {"callCount": false, "detailed": true } }"#).await.unwrap(); - -// }.boxed_local(); - -// tokio_util::run_basic(fut) -// }); - -// } - - diff --git a/cli/lib.rs b/cli/lib.rs index 766e19de607e1f..5c5a1d51611edc 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -506,19 +506,16 @@ async fn test_command( let main_module = ModuleSpecifier::resolve_url(&test_file_url.to_string()).unwrap(); - // TODO(bartlomieju): + // TODO(bartlomieju): // * for --coverage create new `tokio_tungstenite` connection here, // probably on separate thread // * ensure that --inspect flag is on so inspector is available let inspector_url = Url::parse("ws://127.0.0.1:9229").unwrap(); - let coverage_collector = CoverageCollector::new(inspector_url); let mut worker = create_main_worker(global_state.clone(), main_module.clone())?; - coverage_collector.connect(); - // Create a dummy source file. let source_file = SourceFile { filename: test_file_url.to_file_path().unwrap(), @@ -537,7 +534,8 @@ async fn test_command( .save_source_file_in_cache(&main_module, source_file); // TODO: * start collecting coverage - coverage_collector.start_collecting(); + // coverage_collector.start_collecting(); + let mut coverage_collector = CoverageCollector::connect(inspector_url).await?; let execute_result = worker.execute_module(&main_module).await; execute_result?; @@ -545,14 +543,14 @@ async fn test_command( (&mut *worker).await?; worker.execute("window.dispatchEvent(new Event('unload'))")?; - // TODO(bartlomieju): + // TODO(bartlomieju): // * stop collecting collecting coverage - // * wait for inspector client thread to join and return + // * wait for inspector client thread to join and return // coverage JSON struct // * parse coverage report to CoverageParser together with list - // of test files and prepare a test/JSON report - coverage_collector.stop_collecting(); - let coverage_report = coverage_collector.get_report(); + // of test files and prepare a test/JSON report + // coverage_collector.stop_collecting(); + let coverage_report = coverage_collector.get_report().await?; eprintln!("coverage report: {}", coverage_report); Ok(()) From 8d9f8f7c90f338425a4d73f8634375e1aab7e0d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Apr 2020 16:19:05 +0200 Subject: [PATCH 5/9] receive some basic coverage data --- cli/coverage.rs | 29 +++++++++++++++++++---------- cli/lib.rs | 9 ++++++++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/cli/coverage.rs b/cli/coverage.rs index 4a03ebecfa8495..4b0e741b77ca3b 100644 --- a/cli/coverage.rs +++ b/cli/coverage.rs @@ -29,28 +29,37 @@ impl CoverageCollector { eprintln!("start"); collector.socket.send(r#"{"id":1,"method":"Runtime.enable"}"#.into()).await.unwrap(); + collector.socket.send(r#"{"id":2,"method":"Profiler.enable"}"#.into()).await.unwrap(); + collector.socket.send(r#"{"id":3,"method":"Profiler.startPreciseCoverage", "params": {"callCount": false, "detailed": true } }"#.into()).await.unwrap(); + collector.socket.send(r#"{"id":4,"method":"Runtime.runIfWaitingForDebugger" }"#.into()).await.unwrap(); eprintln!("start1"); - let msg = collector.socket.next().await.unwrap(); + + Ok(collector) + } + + pub async fn stop_collecting(&mut self) -> Result<(), ErrBox> { + let msg = self.socket.next().await.unwrap(); + dbg!(msg); + let msg = self.socket.next().await.unwrap(); eprintln!("start2"); dbg!(msg); - collector.socket.send(r#"{"id":2,"method":"Profiler.enable"}"#.into()).await.unwrap(); - let msg = collector.socket.next().await.unwrap(); + let msg = self.socket.next().await.unwrap(); dbg!(msg); - collector.socket.send(r#"{"id":3,"method":"Profiler.startPreciseCoverage", "params": {"callCount": false, "detailed": true } }"#.into()).await.unwrap(); - let msg = collector.socket.next().await.unwrap(); + let msg = self.socket.next().await.unwrap(); dbg!(msg); - collector.socket.send(r#"{"id":4,"method":"Runtime.runIfWaitingForDebugger" }"#.into()).await.unwrap(); - let msg = collector.socket.next().await.unwrap(); + let msg = self.socket.next().await.unwrap(); dbg!(msg); - Ok(collector) + self.socket.send(r#"{"id":5,"method":"Profiler.takePreciseCoverage" }"#.into()).await.unwrap(); + self.socket.send(r#"{"id":6,"method":"Profiler.stopPreciseCoverage" }"#.into()).await.unwrap(); + Ok(()) } pub async fn get_report(&mut self) -> Result { - self.socket.send(r#"{"id":5,"method":"Runtime.takePreciseCoverage" }"#.into()).await.unwrap(); + dbg!("before recv"); let msg = self.socket.next().await.unwrap(); + dbg!("after recv"); dbg!(msg); - self.socket.send(r#"{"id":6,"method":"Runtime.stopPreciseCoverage" }"#.into()).await.unwrap(); let msg = self.socket.next().await.unwrap(); dbg!(msg); diff --git a/cli/lib.rs b/cli/lib.rs index 5c5a1d51611edc..b5e1cf30670d91 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -511,7 +511,7 @@ async fn test_command( // probably on separate thread // * ensure that --inspect flag is on so inspector is available - let inspector_url = Url::parse("ws://127.0.0.1:9229").unwrap(); + let inspector_url = Url::parse("ws://127.0.0.1:9229/ws/463ebedf-b2f2-4a73-96b1-f1987cc22300").unwrap(); let mut worker = create_main_worker(global_state.clone(), main_module.clone())?; @@ -550,6 +550,13 @@ async fn test_command( // * parse coverage report to CoverageParser together with list // of test files and prepare a test/JSON report // coverage_collector.stop_collecting(); + coverage_collector.stop_collecting().await?; + eprintln!("stop collecting"); + (&mut *worker).await?; + (&mut *worker).await?; + (&mut *worker).await?; + (&mut *worker).await?; + eprintln!("polled worker"); let coverage_report = coverage_collector.get_report().await?; eprintln!("coverage report: {}", coverage_report); From 6ad1de96a9656ec554445e3904d9ccec8d7d2214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Apr 2020 17:04:36 +0200 Subject: [PATCH 6/9] wip --- cli/coverage.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++---- cli/lib.rs | 11 ++++++++-- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/cli/coverage.rs b/cli/coverage.rs index 4b0e741b77ca3b..a0fab064e4a4bf 100644 --- a/cli/coverage.rs +++ b/cli/coverage.rs @@ -9,6 +9,7 @@ use tokio_tungstenite; use url::Url; use crate::futures::StreamExt; use crate::futures::SinkExt; +use serde::Deserialize; pub struct CoverageCollector { msg_id: usize, @@ -55,14 +56,58 @@ impl CoverageCollector { Ok(()) } - pub async fn get_report(&mut self) -> Result { + pub async fn get_report(&mut self) -> Result, ErrBox> { dbg!("before recv"); let msg = self.socket.next().await.unwrap(); dbg!("after recv"); - dbg!(msg); + let msg = msg.unwrap(); + let msg_text = msg.to_text()?; + + let coverage_result: CoverageResultMsg = serde_json::from_str(msg_text).unwrap(); + // eprintln!("cover result {:#?}", coverage_result); + + // dbg!(msg); let msg = self.socket.next().await.unwrap(); dbg!(msg); - - todo!() + + Ok(coverage_result.result.result) } } + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CoverageRange { + pub start_offset: usize, + pub end_offset: usize, + pub count: usize, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct FunctionCoverageResult { + pub function_name: String, + pub ranges: Vec, + pub is_block_coverage: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CoverageResult { + pub script_id: String, + pub url: String, + pub functions: Vec, +} + + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Res { + result: Vec, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct CoverageResultMsg { + id: usize, + result: Res, +} diff --git a/cli/lib.rs b/cli/lib.rs index b5e1cf30670d91..d97f6909d3550b 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -68,6 +68,7 @@ pub use dprint_plugin_typescript::swc_ecma_parser; use crate::compilers::TargetLib; use crate::coverage::CoverageCollector; +use crate::coverage::CoverageResult; use crate::doc::parser::DocFileLoader; use crate::file_fetcher::SourceFile; use crate::file_fetcher::SourceFileFetcher; @@ -502,7 +503,7 @@ async fn test_command( let test_file_url = Url::from_file_path(&test_file_path).expect("Should be valid file url"); let test_file = - test_runner::render_test_file(test_modules, fail_fast, filter); + test_runner::render_test_file(test_modules.clone(), fail_fast, filter); let main_module = ModuleSpecifier::resolve_url(&test_file_url.to_string()).unwrap(); @@ -558,7 +559,13 @@ async fn test_command( (&mut *worker).await?; eprintln!("polled worker"); let coverage_report = coverage_collector.get_report().await?; - eprintln!("coverage report: {}", coverage_report); + + let test_modules = test_modules.into_iter().map(|u| u.to_string()).collect::>(); + let filtered_report = coverage_report.into_iter().filter(|e| { + test_modules.contains(&e.url) + }).collect::>(); + eprintln!("test modules {:#?}", test_modules); + eprintln!("coverage report: {:#?}", filtered_report); Ok(()) } From 1eeca309627c23c2c2c7264a7b4686846a1fbae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Sat, 18 Apr 2020 12:56:02 +0200 Subject: [PATCH 7/9] get debugger url from DenoInspector --- cli/coverage.rs | 77 ++++++++++++++++++++++++++++++------------------- cli/lib.rs | 19 +++++++----- 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/cli/coverage.rs b/cli/coverage.rs index a0fab064e4a4bf..1c03bebc4d5206 100644 --- a/cli/coverage.rs +++ b/cli/coverage.rs @@ -2,14 +2,14 @@ #![allow(unused)] +use crate::futures::SinkExt; +use crate::futures::StreamExt; use crate::tokio_util; use deno_core::ErrBox; +use serde::Deserialize; use std; use tokio_tungstenite; use url::Url; -use crate::futures::StreamExt; -use crate::futures::SinkExt; -use serde::Deserialize; pub struct CoverageCollector { msg_id: usize, @@ -19,22 +19,31 @@ pub struct CoverageCollector { impl CoverageCollector { pub async fn connect(url: Url) -> Result { let (socket, response) = tokio_tungstenite::connect_async(url) - .await - .expect("Can't connect"); + .await + .expect("Can't connect"); assert_eq!(response.status(), 101); - let mut collector = Self { - msg_id: 1, - socket, - }; + let mut collector = Self { msg_id: 1, socket }; eprintln!("start"); - collector.socket.send(r#"{"id":1,"method":"Runtime.enable"}"#.into()).await.unwrap(); - collector.socket.send(r#"{"id":2,"method":"Profiler.enable"}"#.into()).await.unwrap(); + collector + .socket + .send(r#"{"id":1,"method":"Runtime.enable"}"#.into()) + .await + .unwrap(); + collector + .socket + .send(r#"{"id":2,"method":"Profiler.enable"}"#.into()) + .await + .unwrap(); collector.socket.send(r#"{"id":3,"method":"Profiler.startPreciseCoverage", "params": {"callCount": false, "detailed": true } }"#.into()).await.unwrap(); - collector.socket.send(r#"{"id":4,"method":"Runtime.runIfWaitingForDebugger" }"#.into()).await.unwrap(); + collector + .socket + .send(r#"{"id":4,"method":"Runtime.runIfWaitingForDebugger" }"#.into()) + .await + .unwrap(); eprintln!("start1"); - + Ok(collector) } @@ -51,8 +60,16 @@ impl CoverageCollector { let msg = self.socket.next().await.unwrap(); dbg!(msg); - self.socket.send(r#"{"id":5,"method":"Profiler.takePreciseCoverage" }"#.into()).await.unwrap(); - self.socket.send(r#"{"id":6,"method":"Profiler.stopPreciseCoverage" }"#.into()).await.unwrap(); + self + .socket + .send(r#"{"id":5,"method":"Profiler.takePreciseCoverage" }"#.into()) + .await + .unwrap(); + self + .socket + .send(r#"{"id":6,"method":"Profiler.stopPreciseCoverage" }"#.into()) + .await + .unwrap(); Ok(()) } @@ -63,13 +80,14 @@ impl CoverageCollector { let msg = msg.unwrap(); let msg_text = msg.to_text()?; - let coverage_result: CoverageResultMsg = serde_json::from_str(msg_text).unwrap(); + let coverage_result: CoverageResultMsg = + serde_json::from_str(msg_text).unwrap(); // eprintln!("cover result {:#?}", coverage_result); // dbg!(msg); let msg = self.socket.next().await.unwrap(); dbg!(msg); - + Ok(coverage_result.result.result) } } @@ -77,37 +95,36 @@ impl CoverageCollector { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CoverageRange { - pub start_offset: usize, - pub end_offset: usize, - pub count: usize, + pub start_offset: usize, + pub end_offset: usize, + pub count: usize, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FunctionCoverageResult { - pub function_name: String, - pub ranges: Vec, - pub is_block_coverage: bool, + pub function_name: String, + pub ranges: Vec, + pub is_block_coverage: bool, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CoverageResult { - pub script_id: String, - pub url: String, - pub functions: Vec, + pub script_id: String, + pub url: String, + pub functions: Vec, } - #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct Res { - result: Vec, + result: Vec, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct CoverageResultMsg { - id: usize, - result: Res, + id: usize, + result: Res, } diff --git a/cli/lib.rs b/cli/lib.rs index d97f6909d3550b..37f6d761f359c2 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -512,8 +512,6 @@ async fn test_command( // probably on separate thread // * ensure that --inspect flag is on so inspector is available - let inspector_url = Url::parse("ws://127.0.0.1:9229/ws/463ebedf-b2f2-4a73-96b1-f1987cc22300").unwrap(); - let mut worker = create_main_worker(global_state.clone(), main_module.clone())?; @@ -536,7 +534,10 @@ async fn test_command( // TODO: * start collecting coverage // coverage_collector.start_collecting(); - let mut coverage_collector = CoverageCollector::connect(inspector_url).await?; + let deno_inspector = worker.inspector.unwrap(); + let inspector_url = Url::parse(&deno_inspector.debugger_url)?; + let mut coverage_collector = + CoverageCollector::connect(inspector_url).await?; let execute_result = worker.execute_module(&main_module).await; execute_result?; @@ -560,10 +561,14 @@ async fn test_command( eprintln!("polled worker"); let coverage_report = coverage_collector.get_report().await?; - let test_modules = test_modules.into_iter().map(|u| u.to_string()).collect::>(); - let filtered_report = coverage_report.into_iter().filter(|e| { - test_modules.contains(&e.url) - }).collect::>(); + let test_modules = test_modules + .into_iter() + .map(|u| u.to_string()) + .collect::>(); + let filtered_report = coverage_report + .into_iter() + .filter(|e| test_modules.contains(&e.url)) + .collect::>(); eprintln!("test modules {:#?}", test_modules); eprintln!("coverage report: {:#?}", filtered_report); From bc5cfc313194564caf7c8696eb2560c3bfa0e8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Sat, 18 Apr 2020 13:07:08 +0200 Subject: [PATCH 8/9] remove flaky test --- cli/lib.rs | 2 +- cli/tests/integration_tests.rs | 75 ---------------------------------- 2 files changed, 1 insertion(+), 76 deletions(-) diff --git a/cli/lib.rs b/cli/lib.rs index 37f6d761f359c2..4f12b68578c978 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -534,7 +534,7 @@ async fn test_command( // TODO: * start collecting coverage // coverage_collector.start_collecting(); - let deno_inspector = worker.inspector.unwrap(); + let deno_inspector = worker.inspector.as_ref().unwrap(); let inspector_url = Url::parse(&deno_inspector.debugger_url)?; let mut coverage_collector = CoverageCollector::connect(inspector_url).await?; diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index cabc7a6af65018..819ccda728a2f3 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -2213,81 +2213,6 @@ async fn inspector_break_on_first_line() { child.kill().unwrap(); } -#[tokio::test] -async fn inspector_coverage() { - let script = deno::test_util::root_path() - .join("cli") - .join("tests") - .join("inspector_coverage.js"); - let mut child = util::deno_cmd() - .arg("run") - // Warning: each inspector test should be on its own port to avoid - // conflicting with another inspector test. - .arg("--inspect-brk=127.0.0.1:9231") - .arg(script) - .stdout(std::process::Stdio::piped()) - .stderr(std::process::Stdio::piped()) - .spawn() - .unwrap(); - - let stderr = child.stderr.as_mut().unwrap(); - let ws_url = extract_ws_url_from_stderr(stderr); - 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, mut socket_rx) = socket.split(); - - let stdout = child.stdout.as_mut().unwrap(); - let mut stdout_lines = std::io::BufReader::new(stdout).lines(); - - use TestStep::*; - let test_steps = vec![ - WsSend(r#"{"id":1,"method":"Runtime.enable"}"#), - WsSend(r#"{"id":2,"method":"Profiler.enable"}"#), - WsSend( - r#"{"id":3,"method":"Profiler.startPreciseCoverage", "params": {"callCount": false, "detailed": true } }"#, - ), - WsRecv( - r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#, - ), - WsRecv(r#"{"id":1,"result":{}}"#), - WsRecv(r#"{"id":2,"result":{}}"#), - WsRecv(r#"{"id":3,"result":{"timestamp":"#), - WsSend(r#"{"id":4,"method":"Runtime.runIfWaitingForDebugger"}"#), - WsRecv(r#"{"id":4,"result":{}}"#), - StdOut("hello a"), - StdOut("hello b"), - StdOut("hello b"), - WsSend(r#"{"id":5,"method":"Profiler.takePreciseCoverage"}"#), - WsSend(r#"{"id":6,"method":"Profiler.stopPreciseCoverage"}"#), - WsRecv(r#"{"id":5,"result":{"result":[{"#), - ]; - - for step in test_steps { - match step { - StdOut(s) => match stdout_lines.next() { - Some(Ok(line)) => assert_eq!(line, s), - other => panic!(other), - }, - WsRecv(s) => loop { - let msg = match socket_rx.next().await { - Some(Ok(msg)) => msg.to_string(), - other => panic!(other), - }; - if !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#) { - assert!(msg.starts_with(s)); - break; - } - }, - WsSend(s) => socket_tx.send(s.into()).await.unwrap(), - } - } - - child.kill().unwrap(); -} - #[tokio::test] async fn inspector_pause() { let script = deno::test_util::root_path() From 35ff5a746daf3291ab86639e50f8b66b8b02a2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Sat, 18 Apr 2020 13:37:22 +0200 Subject: [PATCH 9/9] optional coverage --- cli/flags.rs | 10 ++++++++ cli/lib.rs | 66 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/cli/flags.rs b/cli/flags.rs index 772c719a8849b7..75ac50e2760183 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -68,6 +68,7 @@ pub enum DenoSubcommand { allow_none: bool, include: Option>, filter: Option, + coverage: bool, }, Types, Upgrade { @@ -541,6 +542,7 @@ fn test_parse(flags: &mut Flags, matches: &clap::ArgMatches) { let failfast = matches.is_present("failfast"); let allow_none = matches.is_present("allow_none"); + let coverage = matches.is_present("coverage"); let filter = matches.value_of("filter").map(String::from); let include = if matches.is_present("files") { let files: Vec = matches @@ -558,6 +560,7 @@ fn test_parse(flags: &mut Flags, matches: &clap::ArgMatches) { include, filter, allow_none, + coverage, }; } @@ -975,6 +978,11 @@ fn test_subcommand<'a, 'b>() -> App<'a, 'b> { .takes_value(true) .help("A pattern to filter the tests to run by"), ) + .arg( + Arg::with_name("coverage") + .long("coverage") + .help("Collect coverage information, requires --inspect flag"), + ) .arg( Arg::with_name("files") .help("List of file names to run") @@ -2303,6 +2311,7 @@ mod tests { filter: None, allow_none: true, include: Some(svec!["dir1/", "dir2/"]), + coverage: false, }, allow_read: true, allow_net: true, @@ -2322,6 +2331,7 @@ mod tests { allow_none: false, filter: Some("foo".to_string()), include: Some(svec!["dir1"]), + coverage: false, }, allow_read: true, ..Flags::default() diff --git a/cli/lib.rs b/cli/lib.rs index 4f12b68578c978..9f2e654b66e080 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -485,6 +485,7 @@ async fn test_command( fail_fast: bool, allow_none: bool, filter: Option, + coverage: bool, ) -> Result<(), ErrBox> { let global_state = GlobalState::new(flags.clone())?; let cwd = std::env::current_dir().expect("No current directory"); @@ -534,10 +535,21 @@ async fn test_command( // TODO: * start collecting coverage // coverage_collector.start_collecting(); - let deno_inspector = worker.inspector.as_ref().unwrap(); - let inspector_url = Url::parse(&deno_inspector.debugger_url)?; - let mut coverage_collector = - CoverageCollector::connect(inspector_url).await?; + let mut maybe_coverage_collector = if coverage { + let deno_inspector = match worker.inspector.as_ref() { + Some(inspector) => inspector, + None => { + return Err( + OpError::other("coverage option requires --inspect flag".to_string()) + .into(), + ) + } + }; + let inspector_url = Url::parse(&deno_inspector.debugger_url)?; + Some(CoverageCollector::connect(inspector_url).await?) + } else { + None + }; let execute_result = worker.execute_module(&main_module).await; execute_result?; @@ -552,25 +564,27 @@ async fn test_command( // * parse coverage report to CoverageParser together with list // of test files and prepare a test/JSON report // coverage_collector.stop_collecting(); - coverage_collector.stop_collecting().await?; - eprintln!("stop collecting"); - (&mut *worker).await?; - (&mut *worker).await?; - (&mut *worker).await?; - (&mut *worker).await?; - eprintln!("polled worker"); - let coverage_report = coverage_collector.get_report().await?; - - let test_modules = test_modules - .into_iter() - .map(|u| u.to_string()) - .collect::>(); - let filtered_report = coverage_report - .into_iter() - .filter(|e| test_modules.contains(&e.url)) - .collect::>(); - eprintln!("test modules {:#?}", test_modules); - eprintln!("coverage report: {:#?}", filtered_report); + + if let Some(coverage_collector) = maybe_coverage_collector.as_mut() { + coverage_collector.stop_collecting().await?; + eprintln!("stop collecting"); + (&mut *worker).await?; + (&mut *worker).await?; + (&mut *worker).await?; + (&mut *worker).await?; + eprintln!("polled worker"); + let coverage_report = coverage_collector.get_report().await?; + let test_modules = test_modules + .into_iter() + .map(|u| u.to_string()) + .collect::>(); + let filtered_report = coverage_report + .into_iter() + .filter(|e| test_modules.contains(&e.url)) + .collect::>(); + eprintln!("test modules {:#?}", test_modules); + eprintln!("coverage report: {:#?}", filtered_report); + } Ok(()) } @@ -631,9 +645,9 @@ pub fn main() { include, allow_none, filter, - } => { - test_command(flags, include, fail_fast, allow_none, filter).boxed_local() - } + coverage, + } => test_command(flags, include, fail_fast, allow_none, filter, coverage) + .boxed_local(), DenoSubcommand::Completions { buf } => { if let Err(e) = write_to_stdout_ignore_sigpipe(&buf) { eprintln!("{}", e);