diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2020-06-18 17:54:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-18 11:54:55 -0400 |
commit | 90c5dcfe79cd010b429e775a3ebcb3c5d78fa6ff (patch) | |
tree | 461a23f50869c7cbe9bf621c60d11bd3eaaa8800 | |
parent | a2969ecd27645bafc7195baa7cfecbebfd8d2bf4 (diff) |
chore(test): move testing utilities to test_util crate (#6360)
-rw-r--r-- | Cargo.lock | 12 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | cli/Cargo.toml | 1 | ||||
-rw-r--r-- | cli/file_fetcher.rs | 30 | ||||
-rw-r--r-- | cli/http_util.rs | 26 | ||||
-rw-r--r-- | cli/main.rs | 1 | ||||
-rw-r--r-- | cli/module_graph.rs | 10 | ||||
-rw-r--r-- | cli/test_runner.rs | 1 | ||||
-rw-r--r-- | cli/test_util.rs | 87 | ||||
-rw-r--r-- | cli/tests/integration_tests.rs | 368 | ||||
-rw-r--r-- | cli/worker.rs | 2 | ||||
-rw-r--r-- | test_plugin/Cargo.toml | 3 | ||||
-rw-r--r-- | test_plugin/tests/integration_tests.rs | 23 | ||||
-rw-r--r-- | test_util/Cargo.toml | 14 | ||||
-rw-r--r-- | test_util/src/lib.rs | 366 |
15 files changed, 435 insertions, 512 deletions
diff --git a/Cargo.lock b/Cargo.lock index 942210ca1..c80d6a47c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,6 +474,7 @@ dependencies = [ "sys-info", "tempfile", "termcolor", + "test_util", "tokio", "tokio-rustls", "tokio-tungstenite", @@ -2514,6 +2515,17 @@ version = "0.0.1" dependencies = [ "deno_core", "futures 0.3.5", + "test_util", +] + +[[package]] +name = "test_util" +version = "0.1.0" +dependencies = [ + "lazy_static", + "os_pipe", + "regex", + "tempfile", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c26258ee8..cae86fd20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,8 @@ members = [ "core", "tools/hyper_hello", "deno_typescript", - "test_plugin" + "test_plugin", + "test_util", ] exclude = [ "std/hash/_wasm" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 9e4aabf1b..7b1301a32 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -74,6 +74,7 @@ nix = "0.17.0" os_pipe = "0.9.2" # Used for testing inspector. Keep in-sync with warp. tokio-tungstenite = { version = "0.10.1", features = ["connect"] } +test_util = { path = "../test_util" } [target.'cfg(unix)'.dev-dependencies] pty = "0.2.2" diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index c1873fa8f..1613117a1 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -810,7 +810,7 @@ mod tests { #[tokio::test] async fn test_get_source_code_1() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (temp_dir, fetcher) = test_setup(); let fetcher_1 = fetcher.clone(); let fetcher_2 = fetcher.clone(); @@ -925,7 +925,7 @@ mod tests { #[tokio::test] async fn test_get_source_code_2() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (temp_dir, fetcher) = test_setup(); let module_url = Url::parse("http://localhost:4545/cli/tests/subdir/mismatch_ext.ts") @@ -1010,7 +1010,7 @@ mod tests { #[tokio::test] async fn test_get_source_code_multiple_downloads_of_same_file() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let specifier = ModuleSpecifier::resolve_url( "http://localhost:4545/cli/tests/subdir/mismatch_ext.ts", @@ -1056,7 +1056,7 @@ mod tests { #[tokio::test] async fn test_get_source_code_3() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let redirect_module_url = Url::parse( @@ -1110,7 +1110,7 @@ mod tests { #[tokio::test] async fn test_get_source_code_4() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let double_redirect_url = Url::parse( "http://localhost:4548/cli/tests/subdir/redirects/redirect1.js", @@ -1168,7 +1168,7 @@ mod tests { #[tokio::test] async fn test_get_source_code_5() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let double_redirect_url = Url::parse( @@ -1230,7 +1230,7 @@ mod tests { #[tokio::test] async fn test_get_source_code_6() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let double_redirect_url = Url::parse( "http://localhost:4548/cli/tests/subdir/redirects/redirect1.js", @@ -1272,7 +1272,7 @@ mod tests { #[tokio::test] async fn test_get_source_code_7() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); // Testing redirect with Location set to absolute url. @@ -1327,7 +1327,7 @@ mod tests { #[tokio::test] async fn test_get_source_no_remote() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let module_url = Url::parse("http://localhost:4545/cli/tests/002_hello.ts").unwrap(); @@ -1351,7 +1351,7 @@ mod tests { #[tokio::test] async fn test_get_source_cached_only() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let fetcher_1 = fetcher.clone(); let fetcher_2 = fetcher.clone(); @@ -1402,7 +1402,7 @@ mod tests { #[tokio::test] async fn test_fetch_source_0() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let module_url = Url::parse("http://127.0.0.1:4545/cli/tests/subdir/mt_video_mp2t.t3.ts") @@ -1443,7 +1443,7 @@ mod tests { #[tokio::test] async fn test_fetch_source_2() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let fetcher_1 = fetcher.clone(); let fetcher_2 = fetcher.clone(); @@ -1815,7 +1815,7 @@ mod tests { #[tokio::test] async fn test_fetch_with_etag() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let module_url = Url::parse("http://127.0.0.1:4545/etag_script.ts").unwrap(); @@ -1949,7 +1949,7 @@ mod tests { #[tokio::test] async fn test_fetch_with_types_header() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let module_url = Url::parse("http://127.0.0.1:4545/xTypeScriptTypes.js").unwrap(); @@ -1975,7 +1975,7 @@ mod tests { #[tokio::test] async fn test_fetch_with_types_reference() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let (_temp_dir, fetcher) = test_setup(); let module_url = Url::parse("http://127.0.0.1:4545/referenceTypes.js").unwrap(); diff --git a/cli/http_util.rs b/cli/http_util.rs index 2278afc8d..b528f411f 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -248,7 +248,7 @@ mod tests { #[tokio::test] async fn test_fetch_string() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); // Relies on external http server. See tools/http_server.py let url = Url::parse("http://127.0.0.1:4545/cli/tests/fixture.json").unwrap(); @@ -267,7 +267,7 @@ mod tests { #[tokio::test] async fn test_fetch_gzip() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); // Relies on external http server. See tools/http_server.py let url = Url::parse( "http://127.0.0.1:4545/cli/tests/053_import_compression/gziped", @@ -291,7 +291,7 @@ mod tests { #[tokio::test] async fn test_fetch_with_etag() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let url = Url::parse("http://127.0.0.1:4545/etag_script.ts").unwrap(); let client = create_http_client(None).unwrap(); let result = fetch_once(client.clone(), &url, None).await; @@ -316,7 +316,7 @@ mod tests { #[tokio::test] async fn test_fetch_brotli() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); // Relies on external http server. See tools/http_server.py let url = Url::parse( "http://127.0.0.1:4545/cli/tests/053_import_compression/brotli", @@ -341,7 +341,7 @@ mod tests { #[tokio::test] async fn test_fetch_once_with_redirect() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); // Relies on external http server. See tools/http_server.py let url = Url::parse("http://127.0.0.1:4546/cli/tests/fixture.json").unwrap(); @@ -398,13 +398,13 @@ mod tests { #[tokio::test] async fn test_fetch_with_cafile_string() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); // Relies on external http server. See tools/http_server.py let url = Url::parse("https://localhost:5545/cli/tests/fixture.json").unwrap(); let client = create_http_client(Some(String::from( - crate::test_util::root_path() + test_util::root_path() .join("std/http/testdata/tls/RootCA.pem") .to_str() .unwrap(), @@ -424,14 +424,14 @@ mod tests { #[tokio::test] async fn test_fetch_with_cafile_gzip() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); // Relies on external http server. See tools/http_server.py let url = Url::parse( "https://localhost:5545/cli/tests/053_import_compression/gziped", ) .unwrap(); let client = create_http_client(Some(String::from( - crate::test_util::root_path() + test_util::root_path() .join("std/http/testdata/tls/RootCA.pem") .to_str() .unwrap(), @@ -454,10 +454,10 @@ mod tests { #[tokio::test] async fn test_fetch_with_cafile_with_etag() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let url = Url::parse("https://localhost:5545/etag_script.ts").unwrap(); let client = create_http_client(Some(String::from( - crate::test_util::root_path() + test_util::root_path() .join("std/http/testdata/tls/RootCA.pem") .to_str() .unwrap(), @@ -486,14 +486,14 @@ mod tests { #[tokio::test] async fn test_fetch_with_cafile_brotli() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); // Relies on external http server. See tools/http_server.py let url = Url::parse( "https://localhost:5545/cli/tests/053_import_compression/brotli", ) .unwrap(); let client = create_http_client(Some(String::from( - crate::test_util::root_path() + test_util::root_path() .join("std/http/testdata/tls/RootCA.pem") .to_str() .unwrap(), diff --git a/cli/main.rs b/cli/main.rs index b8ac2ae42..1749a38cf 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -58,7 +58,6 @@ mod startup_data; pub mod state; mod swc_util; mod test_runner; -pub mod test_util; mod tokio_util; mod tsc; mod upgrade; diff --git a/cli/module_graph.rs b/cli/module_graph.rs index 54c53c623..9cded48a0 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -564,7 +564,7 @@ mod tests { #[ignore] #[tokio::test] async fn source_graph_fetch() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let module_specifier = ModuleSpecifier::resolve_url_or_path( "http://localhost:4545/cli/tests/019_media_types.ts", @@ -657,7 +657,7 @@ mod tests { #[tokio::test] async fn source_graph_type_references() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let module_specifier = ModuleSpecifier::resolve_url_or_path( "http://localhost:4545/cli/tests/type_definitions.ts", @@ -715,7 +715,7 @@ mod tests { #[tokio::test] async fn source_graph_type_references2() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let module_specifier = ModuleSpecifier::resolve_url_or_path( "http://localhost:4545/cli/tests/type_directives_02.ts", @@ -764,7 +764,7 @@ mod tests { #[tokio::test] async fn source_graph_type_references3() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let module_specifier = ModuleSpecifier::resolve_url_or_path( "http://localhost:4545/cli/tests/type_directives_01.ts", @@ -807,7 +807,7 @@ mod tests { #[tokio::test] async fn source_graph_different_langs() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); // ModuleGraphLoader was mistakenly parsing this file as TSX // https://github.com/denoland/deno/issues/5867 diff --git a/cli/test_runner.rs b/cli/test_runner.rs index b4623fc9f..5de323d1c 100644 --- a/cli/test_runner.rs +++ b/cli/test_runner.rs @@ -93,7 +93,6 @@ pub fn render_test_file( #[cfg(test)] mod tests { use super::*; - use crate::test_util; #[test] fn test_prepare_test_modules_urls() { diff --git a/cli/test_util.rs b/cli/test_util.rs deleted file mode 100644 index 6d8c58db5..000000000 --- a/cli/test_util.rs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -#![cfg(test)] -use std::path::PathBuf; -use std::process::Child; -use std::process::Command; -use std::process::Stdio; -use std::sync::Mutex; -use std::sync::MutexGuard; - -lazy_static! { - static ref GUARD: Mutex<()> = Mutex::new(()); -} - -pub fn root_path() -> PathBuf { - PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/..")) -} - -pub fn tests_path() -> PathBuf { - root_path().join("cli").join("tests") -} - -pub fn target_dir() -> PathBuf { - let current_exe = std::env::current_exe().unwrap(); - let target_dir = current_exe.parent().unwrap().parent().unwrap(); - println!("target_dir {}", target_dir.display()); - target_dir.into() -} - -pub fn deno_exe_path() -> PathBuf { - // Something like /Users/rld/src/deno/target/debug/deps/deno - let mut p = target_dir().join("deno"); - if cfg!(windows) { - p.set_extension("exe"); - } - p -} - -pub struct HttpServerGuard<'a> { - #[allow(dead_code)] - g: MutexGuard<'a, ()>, - child: Child, -} - -impl<'a> Drop for HttpServerGuard<'a> { - fn drop(&mut self) { - match self.child.try_wait() { - Ok(None) => { - self.child.kill().expect("failed to kill http_server.py"); - } - Ok(Some(status)) => { - panic!("http_server.py exited unexpectedly {}", status) - } - Err(e) => panic!("http_server.py err {}", e), - } - } -} - -/// Starts tools/http_server.py when the returned guard is dropped, the server -/// will be killed. -pub fn http_server<'a>() -> HttpServerGuard<'a> { - // TODO(bartlomieju) Allow tests to use the http server in parallel. - let g = GUARD.lock().unwrap(); - - println!("tools/http_server.py starting..."); - let mut child = Command::new("python") - .current_dir(root_path()) - .args(&["-u", "tools/http_server.py"]) - .stdout(Stdio::piped()) - .spawn() - .expect("failed to execute child"); - - let stdout = child.stdout.as_mut().unwrap(); - use std::io::{BufRead, BufReader}; - let lines = BufReader::new(stdout).lines(); - // Wait for "ready" on stdout. See tools/http_server.py - for maybe_line in lines { - if let Ok(line) = maybe_line { - if line.starts_with("ready") { - break; - } - } else { - panic!(maybe_line.unwrap_err()); - } - } - - HttpServerGuard { child, g } -} diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index db2773150..235d6ce11 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -1,12 +1,12 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -#[macro_use] -extern crate lazy_static; #[cfg(unix)] extern crate nix; #[cfg(unix)] extern crate pty; extern crate tempfile; +use test_util as util; + use futures::prelude::*; use std::io::{BufRead, Write}; use std::process::Command; @@ -2956,367 +2956,3 @@ fn exec_path() { let expected = std::fs::canonicalize(util::deno_exe_path()).unwrap(); assert_eq!(expected, actual); } - -mod util { - use os_pipe::pipe; - use regex::Regex; - use std::io::Read; - use std::io::Write; - use std::path::PathBuf; - use std::process::Child; - use std::process::Command; - use std::process::Output; - use std::process::Stdio; - use std::sync::Mutex; - use std::sync::MutexGuard; - use tempfile::TempDir; - - pub const PERMISSION_VARIANTS: [&str; 5] = - ["read", "write", "env", "net", "run"]; - pub const PERMISSION_DENIED_PATTERN: &str = "PermissionDenied"; - - lazy_static! { - static ref DENO_DIR: TempDir = TempDir::new().expect("tempdir fail"); - - // STRIP_ANSI_RE and strip_ansi_codes are lifted from the "console" crate. - // Copyright 2017 Armin Ronacher <armin.ronacher@active-4.com>. MIT License. - static ref STRIP_ANSI_RE: Regex = Regex::new( - r"[\x1b\x9b][\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]" - ).unwrap(); - - static ref GUARD: Mutex<()> = Mutex::new(()); - } - - pub fn root_path() -> PathBuf { - PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/..")) - } - - pub fn tests_path() -> PathBuf { - root_path().join("cli").join("tests") - } - - pub fn target_dir() -> PathBuf { - let current_exe = std::env::current_exe().unwrap(); - let target_dir = current_exe.parent().unwrap().parent().unwrap(); - println!("target_dir {}", target_dir.display()); - target_dir.into() - } - - pub fn deno_exe_path() -> PathBuf { - // Something like /Users/rld/src/deno/target/debug/deps/deno - let mut p = target_dir().join("deno"); - if cfg!(windows) { - p.set_extension("exe"); - } - p - } - - pub struct HttpServerGuard<'a> { - #[allow(dead_code)] - g: MutexGuard<'a, ()>, - child: Child, - } - - impl<'a> Drop for HttpServerGuard<'a> { - fn drop(&mut self) { - match self.child.try_wait() { - Ok(None) => { - self.child.kill().expect("failed to kill http_server.py"); - } - Ok(Some(status)) => { - panic!("http_server.py exited unexpectedly {}", status) - } - Err(e) => panic!("http_server.py err {}", e), - } - } - } - - /// Starts tools/http_server.py when the returned guard is dropped, the server - /// will be killed. - pub fn http_server<'a>() -> HttpServerGuard<'a> { - // TODO(bartlomieju) Allow tests to use the http server in parallel. - let g = GUARD.lock().unwrap(); - - println!("tools/http_server.py starting..."); - let mut child = Command::new("python") - .current_dir(root_path()) - .args(&["-u", "tools/http_server.py"]) - .stdout(Stdio::piped()) - .spawn() - .expect("failed to execute child"); - - let stdout = child.stdout.as_mut().unwrap(); - use std::io::{BufRead, BufReader}; - let lines = BufReader::new(stdout).lines(); - // Wait for "ready" on stdout. See tools/http_server.py - for maybe_line in lines { - if let Ok(line) = maybe_line { - if line.starts_with("ready") { - break; - } - } else { - panic!(maybe_line.unwrap_err()); - } - } - - HttpServerGuard { child, g } - } - - /// Helper function to strip ansi codes. - pub fn strip_ansi_codes(s: &str) -> std::borrow::Cow<str> { - STRIP_ANSI_RE.replace_all(s, "") - } - - pub fn run_and_collect_output( - expect_success: bool, - args: &str, - input: Option<Vec<&str>>, - envs: Option<Vec<(String, String)>>, - need_http_server: bool, - ) -> (String, String) { - let mut deno_process_builder = deno_cmd(); - deno_process_builder - .args(args.split_whitespace()) - .current_dir(&tests_path()) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); - if let Some(envs) = envs { - deno_process_builder.envs(envs); - } - let http_guard = if need_http_server { - Some(http_server()) - } else { - None - }; - let mut deno = deno_process_builder - .spawn() - .expect("failed to spawn script"); - if let Some(lines) = input { - let stdin = deno.stdin.as_mut().expect("failed to get stdin"); - stdin - .write_all(lines.join("\n").as_bytes()) - .expect("failed to write to stdin"); - } - let Output { - stdout, - stderr, - status, - } = deno.wait_with_output().expect("failed to wait on child"); - drop(http_guard); - let stdout = String::from_utf8(stdout).unwrap(); - let stderr = String::from_utf8(stderr).unwrap(); - if expect_success != status.success() { - eprintln!("stdout: <<<{}>>>", stdout); - eprintln!("stderr: <<<{}>>>", stderr); - panic!("Unexpected exit code: {:?}", status.code()); - } - (stdout, stderr) - } - - pub fn deno_cmd() -> Command { - let e = deno_exe_path(); - assert!(e.exists()); - let mut c = Command::new(e); - c.env("DENO_DIR", DENO_DIR.path()); - c - } - - pub fn run_python_script(script: &str) { - let output = Command::new("python") - .env("DENO_DIR", DENO_DIR.path()) - .current_dir(root_path()) - .arg(script) - .arg(format!("--build-dir={}", target_dir().display())) - .arg(format!("--executable={}", deno_exe_path().display())) - .output() - .expect("failed to spawn script"); - if !output.status.success() { - let stdout = String::from_utf8(output.stdout).unwrap(); - let stderr = String::from_utf8(output.stderr).unwrap(); - panic!( - "{} executed with failing error code\n{}{}", - script, stdout, stderr - ); - } - } - - #[derive(Debug, Default)] - pub struct CheckOutputIntegrationTest { - pub args: &'static str, - pub output: &'static str, - pub input: Option<&'static str>, - pub output_str: Option<&'static str>, - pub exit_code: i32, - pub http_server: bool, - } - - impl CheckOutputIntegrationTest { - pub fn run(&self) { - let args = self.args.split_whitespace(); - let root = root_path(); - let deno_exe = deno_exe_path(); - println!("root path {}", root.display()); - println!("deno_exe path {}", deno_exe.display()); - - let http_server_guard = if self.http_server { - Some(http_server()) - } else { - None - }; - - let (mut reader, writer) = pipe().unwrap(); - let tests_dir = root.join("cli").join("tests"); - let mut command = deno_cmd(); - println!("deno_exe args {}", self.args); - println!("deno_exe tests path {:?}", &tests_dir); - command.args(args); - command.current_dir(&tests_dir); - command.stdin(Stdio::piped()); - let writer_clone = writer.try_clone().unwrap(); - command.stderr(writer_clone); - command.stdout(writer); - - let mut process = command.spawn().expect("failed to execute process"); - - if let Some(input) = self.input { - let mut p_stdin = process.stdin.take().unwrap(); - write!(p_stdin, "{}", input).unwrap(); - } - - // Very important when using pipes: This parent process is still - // holding its copies of the write ends, and we have to close them - // before we read, otherwise the read end will never report EOF. The - // Command object owns the writers now, and dropping it closes them. - drop(command); - - let mut actual = String::new(); - reader.read_to_string(&mut actual).unwrap(); - - let status = process.wait().expect("failed to finish process"); - let exit_code = status.code().unwrap(); - - drop(http_server_guard); - - actual = strip_ansi_codes(&actual).to_string(); - - if self.exit_code != exit_code { - println!("OUTPUT\n{}\nOUTPUT", actual); - panic!( - "bad exit code, expected: {:?}, actual: {:?}", - self.exit_code, exit_code - ); - } - - let expected = if let Some(s) = self.output_str { - s.to_owned() - } else { - let output_path = tests_dir.join(self.output); - println!("output path {}", output_path.display()); - std::fs::read_to_string(output_path).expect("cannot read output") - }; - - if !wildcard_match(&expected, &actual) { - println!("OUTPUT\n{}\nOUTPUT", actual); - println!("EXPECTED\n{}\nEXPECTED", expected); - panic!("pattern match failed"); - } - } - } - - fn wildcard_match(pattern: &str, s: &str) -> bool { - pattern_match(pattern, s, "[WILDCARD]") - } - - pub fn pattern_match(pattern: &str, s: &str, wildcard: &str) -> bool { - // Normalize line endings - let mut s = s.replace("\r\n", "\n"); - let pattern = pattern.replace("\r\n", "\n"); - - if pattern == wildcard { - return true; - } - - let parts = pattern.split(wildcard).collect::<Vec<&str>>(); - if parts.len() == 1 { - return pattern == s; - } - - if !s.starts_with(parts[0]) { - return false; - } - - // If the first line of the pattern is just a wildcard the newline character - // needs to be pre-pended so it can safely match anything or nothing and - // continue matching. - if pattern.lines().next() == Some(wildcard) { - s.insert_str(0, "\n"); - } - - let mut t = s.split_at(parts[0].len()); - - for (i, part) in parts.iter().enumerate() { - if i == 0 { - continue; - } - dbg!(part, i); - if i == parts.len() - 1 && (*part == "" || *part == "\n") { - dbg!("exit 1 true", i); - return true; - } - if let Some(found) = t.1.find(*part) { - dbg!("found ", found); - t = t.1.split_at(found + part.len()); - } else { - dbg!("exit false ", i); - return false; - } - } - - dbg!("end ", t.1.len()); - t.1.is_empty() - } - - #[test] - fn test_wildcard_match() { - let fixtures = vec![ - ("foobarbaz", "foobarbaz", true), - ("[WILDCARD]", "foobarbaz", true), - ("foobar", "foobarbaz", false), - ("foo[WILDCARD]baz", "foobarbaz", true), - ("foo[WILDCARD]baz", "foobazbar", false), - ("foo[WILDCARD]baz[WILDCARD]qux", "foobarbazqatqux", true), - ("foo[WILDCARD]", "foobar", true), - ("foo[WILDCARD]baz[WILDCARD]", "foobarbazqat", true), - // check with different line endings - ("foo[WILDCARD]\nbaz[WILDCARD]\n", "foobar\nbazqat\n", true), - ( - "foo[WILDCARD]\nbaz[WILDCARD]\n", - "foobar\r\nbazqat\r\n", - true, - ), - ( - "foo[WILDCARD]\r\nbaz[WILDCARD]\n", - "foobar\nbazqat\r\n", - true, - ), - ( - "foo[WILDCARD]\r\nbaz[WILDCARD]\r\n", - "foobar\nbazqat\n", - true, - ), - ( - "foo[WILDCARD]\r\nbaz[WILDCARD]\r\n", - "foobar\r\nbazqat\r\n", - true, - ), - ]; - - // Iterate through the fixture lists, testing each one - for (pattern, string, expected) in fixtures { - let actual = wildcard_match(pattern, string); - dbg!(pattern, string, expected); - assert_eq!(actual, expected); - } - } -} diff --git a/cli/worker.rs b/cli/worker.rs index 70cadcc23..4ac1ee6a6 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -394,7 +394,7 @@ mod tests { #[tokio::test] async fn execute_006_url_imports() { - let http_server_guard = crate::test_util::http_server(); + let http_server_guard = test_util::http_server(); let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .parent() .unwrap() diff --git a/test_plugin/Cargo.toml b/test_plugin/Cargo.toml index 3787579a8..ca86b96e9 100644 --- a/test_plugin/Cargo.toml +++ b/test_plugin/Cargo.toml @@ -11,3 +11,6 @@ crate-type = ["cdylib"] [dependencies] futures = "0.3.5" deno_core = { path = "../core" } + +[dev-dependencies] +test_util = { path = "../test_util" }
\ No newline at end of file diff --git a/test_plugin/tests/integration_tests.rs b/test_plugin/tests/integration_tests.rs index 8716048b1..53d1c2441 100644 --- a/test_plugin/tests/integration_tests.rs +++ b/test_plugin/tests/integration_tests.rs @@ -2,29 +2,8 @@ // cd test_plugin // ../target/debug/deno run --unstable --allow-plugin tests/test.js debug -use std::path::PathBuf; use std::process::Command; - -fn target_dir() -> PathBuf { - let current_exe = std::env::current_exe().unwrap(); - let target_dir = current_exe.parent().unwrap().parent().unwrap(); - println!("target_dir {}", target_dir.display()); - target_dir.into() -} - -fn deno_exe_path() -> PathBuf { - // Something like /Users/rld/src/deno/target/debug/deps/deno - let mut p = target_dir().join("deno"); - if cfg!(windows) { - p.set_extension("exe"); - } - p -} - -fn deno_cmd() -> Command { - assert!(deno_exe_path().exists()); - Command::new(deno_exe_path()) -} +use test_util::deno_cmd; #[cfg(debug_assertions)] const BUILD_VARIANT: &str = "debug"; diff --git a/test_util/Cargo.toml b/test_util/Cargo.toml new file mode 100644 index 000000000..9160236cd --- /dev/null +++ b/test_util/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "test_util" +version = "0.1.0" +authors = ["the Deno authors"] +edition = "2018" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lazy_static = "1.4.0" +os_pipe = "0.9.2" +regex = "1.3.9" +tempfile = "3.1.0"
\ No newline at end of file diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs new file mode 100644 index 000000000..85cfa0879 --- /dev/null +++ b/test_util/src/lib.rs @@ -0,0 +1,366 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +#[macro_use] +extern crate lazy_static; + +use os_pipe::pipe; +use regex::Regex; +use std::io::Read; +use std::io::Write; +use std::path::PathBuf; +use std::process::Child; +use std::process::Command; +use std::process::Output; +use std::process::Stdio; +use std::sync::Mutex; +use std::sync::MutexGuard; +use tempfile::TempDir; + +pub const PERMISSION_VARIANTS: [&str; 5] = + ["read", "write", "env", "net", "run"]; +pub const PERMISSION_DENIED_PATTERN: &str = "PermissionDenied"; + +lazy_static! { + static ref DENO_DIR: TempDir = TempDir::new().expect("tempdir fail"); + + // STRIP_ANSI_RE and strip_ansi_codes are lifted from the "console" crate. + // Copyright 2017 Armin Ronacher <armin.ronacher@active-4.com>. MIT License. + static ref STRIP_ANSI_RE: Regex = Regex::new( + r"[\x1b\x9b][\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]" + ).unwrap(); + + static ref GUARD: Mutex<()> = Mutex::new(()); +} + +pub fn root_path() -> PathBuf { + PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/..")) +} + +pub fn tests_path() -> PathBuf { + root_path().join("cli").join("tests") +} + +pub fn target_dir() -> PathBuf { + let current_exe = std::env::current_exe().unwrap(); + let target_dir = current_exe.parent().unwrap().parent().unwrap(); + println!("target_dir {}", target_dir.display()); + target_dir.into() +} + +pub fn deno_exe_path() -> PathBuf { + // Something like /Users/rld/src/deno/target/debug/deps/deno + let mut p = target_dir().join("deno"); + if cfg!(windows) { + p.set_extension("exe"); + } + p +} + +pub struct HttpServerGuard<'a> { + #[allow(dead_code)] + g: MutexGuard<'a, ()>, + child: Child, +} + +impl<'a> Drop for HttpServerGuard<'a> { + fn drop(&mut self) { + match self.child.try_wait() { + Ok(None) => { + self.child.kill().expect("failed to kill http_server.py"); + } + Ok(Some(status)) => { + panic!("http_server.py exited unexpectedly {}", status) + } + Err(e) => panic!("http_server.py err {}", e), + } + } +} + +/// Starts tools/http_server.py when the returned guard is dropped, the server +/// will be killed. +pub fn http_server<'a>() -> HttpServerGuard<'a> { + // TODO(bartlomieju) Allow tests to use the http server in parallel. + let g = GUARD.lock().unwrap(); + + println!("tools/http_server.py starting..."); + let mut child = Command::new("python") + .current_dir(root_path()) + .args(&["-u", "tools/http_server.py"]) + .stdout(Stdio::piped()) + .spawn() + .expect("failed to execute child"); + + let stdout = child.stdout.as_mut().unwrap(); + use std::io::{BufRead, BufReader}; + let lines = BufReader::new(stdout).lines(); + // Wait for "ready" on stdout. See tools/http_server.py + for maybe_line in lines { + if let Ok(line) = maybe_line { + if line.starts_with("ready") { + break; + } + } else { + panic!(maybe_line.unwrap_err()); + } + } + + HttpServerGuard { child, g } +} + +/// Helper function to strip ansi codes. +pub fn strip_ansi_codes(s: &str) -> std::borrow::Cow<str> { + STRIP_ANSI_RE.replace_all(s, "") +} + +pub fn run_and_collect_output( + expect_success: bool, + args: &str, + input: Option<Vec<&str>>, + envs: Option<Vec<(String, String)>>, + need_http_server: bool, +) -> (String, String) { + let mut deno_process_builder = deno_cmd(); + deno_process_builder + .args(args.split_whitespace()) + .current_dir(&tests_path()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + if let Some(envs) = envs { + deno_process_builder.envs(envs); + } + let http_guard = if need_http_server { + Some(http_server()) + } else { + None + }; + let mut deno = deno_process_builder + .spawn() + .expect("failed to spawn script"); + if let Some(lines) = input { + let stdin = deno.stdin.as_mut().expect("failed to get stdin"); + stdin + .write_all(lines.join("\n").as_bytes()) + .expect("failed to write to stdin"); + } + let Output { + stdout, + stderr, + status, + } = deno.wait_with_output().expect("failed to wait on child"); + drop(http_guard); + let stdout = String::from_utf8(stdout).unwrap(); + let stderr = String::from_utf8(stderr).unwrap(); + if expect_success != status.success() { + eprintln!("stdout: <<<{}>>>", stdout); + eprintln!("stderr: <<<{}>>>", stderr); + panic!("Unexpected exit code: {:?}", status.code()); + } + (stdout, stderr) +} + +pub fn deno_cmd() -> Command { + let e = deno_exe_path(); + assert!(e.exists()); + let mut c = Command::new(e); + c.env("DENO_DIR", DENO_DIR.path()); + c +} + +pub fn run_python_script(script: &str) { + let output = Command::new("python") + .env("DENO_DIR", DENO_DIR.path()) + .current_dir(root_path()) + .arg(script) + .arg(format!("--build-dir={}", target_dir().display())) + .arg(format!("--executable={}", deno_exe_path().display())) + .output() + .expect("failed to spawn script"); + if !output.status.success() { + let stdout = String::from_utf8(output.stdout).unwrap(); + let stderr = String::from_utf8(output.stderr).unwrap(); + panic!( + "{} executed with failing error code\n{}{}", + script, stdout, stderr + ); + } +} + +#[derive(Debug, Default)] +pub struct CheckOutputIntegrationTest { + pub args: &'static str, + pub output: &'static str, + pub input: Option<&'static str>, + pub output_str: Option<&'static str>, + pub exit_code: i32, + pub http_server: bool, +} + +impl CheckOutputIntegrationTest { + pub fn run(&self) { + let args = self.args.split_whitespace(); + let root = root_path(); + let deno_exe = deno_exe_path(); + println!("root path {}", root.display()); + println!("deno_exe path {}", deno_exe.display()); + + let http_server_guard = if self.http_server { + Some(http_server()) + } else { + None + }; + + let (mut reader, writer) = pipe().unwrap(); + let tests_dir = root.join("cli").join("tests"); + let mut command = deno_cmd(); + println!("deno_exe args {}", self.args); + println!("deno_exe tests path {:?}", &tests_dir); + command.args(args); + command.current_dir(&tests_dir); + command.stdin(Stdio::piped()); + let writer_clone = writer.try_clone().unwrap(); + command.stderr(writer_clone); + command.stdout(writer); + + let mut process = command.spawn().expect("failed to execute process"); + + if let Some(input) = self.input { + let mut p_stdin = process.stdin.take().unwrap(); + write!(p_stdin, "{}", input).unwrap(); + } + + // Very important when using pipes: This parent process is still + // holding its copies of the write ends, and we have to close them + // before we read, otherwise the read end will never report EOF. The + // Command object owns the writers now, and dropping it closes them. + drop(command); + + let mut actual = String::new(); + reader.read_to_string(&mut actual).unwrap(); + + let status = process.wait().expect("failed to finish process"); + let exit_code = status.code().unwrap(); + + drop(http_server_guard); + + actual = strip_ansi_codes(&actual).to_string(); + + if self.exit_code != exit_code { + println!("OUTPUT\n{}\nOUTPUT", actual); + panic!( + "bad exit code, expected: {:?}, actual: {:?}", + self.exit_code, exit_code + ); + } + + let expected = if let Some(s) = self.output_str { + s.to_owned() + } else { + let output_path = tests_dir.join(self.output); + println!("output path {}", output_path.display()); + std::fs::read_to_string(output_path).expect("cannot read output") + }; + + if !wildcard_match(&expected, &actual) { + println!("OUTPUT\n{}\nOUTPUT", actual); + println!("EXPECTED\n{}\nEXPECTED", expected); + panic!("pattern match failed"); + } + } +} + +fn wildcard_match(pattern: &str, s: &str) -> bool { + pattern_match(pattern, s, "[WILDCARD]") +} + +pub fn pattern_match(pattern: &str, s: &str, wildcard: &str) -> bool { + // Normalize line endings + let mut s = s.replace("\r\n", "\n"); + let pattern = pattern.replace("\r\n", "\n"); + + if pattern == wildcard { + return true; + } + + let parts = pattern.split(wildcard).collect::<Vec<&str>>(); + if parts.len() == 1 { + return pattern == s; + } + + if !s.starts_with(parts[0]) { + return false; + } + + // If the first line of the pattern is just a wildcard the newline character + // needs to be pre-pended so it can safely match anything or nothing and + // continue matching. + if pattern.lines().next() == Some(wildcard) { + s.insert_str(0, "\n"); + } + + let mut t = s.split_at(parts[0].len()); + + for (i, part) in parts.iter().enumerate() { + if i == 0 { + continue; + } + dbg!(part, i); + if i == parts.len() - 1 && (*part == "" || *part == "\n") { + dbg!("exit 1 true", i); + return true; + } + if let Some(found) = t.1.find(*part) { + dbg!("found ", found); + t = t.1.split_at(found + part.len()); + } else { + dbg!("exit false ", i); + return false; + } + } + + dbg!("end ", t.1.len()); + t.1.is_empty() +} + +#[test] +fn test_wildcard_match() { + let fixtures = vec![ + ("foobarbaz", "foobarbaz", true), + ("[WILDCARD]", "foobarbaz", true), + ("foobar", "foobarbaz", false), + ("foo[WILDCARD]baz", "foobarbaz", true), + ("foo[WILDCARD]baz", "foobazbar", false), + ("foo[WILDCARD]baz[WILDCARD]qux", "foobarbazqatqux", true), + ("foo[WILDCARD]", "foobar", true), + ("foo[WILDCARD]baz[WILDCARD]", "foobarbazqat", true), + // check with different line endings + ("foo[WILDCARD]\nbaz[WILDCARD]\n", "foobar\nbazqat\n", true), + ( + "foo[WILDCARD]\nbaz[WILDCARD]\n", + "foobar\r\nbazqat\r\n", + true, + ), + ( + "foo[WILDCARD]\r\nbaz[WILDCARD]\n", + "foobar\nbazqat\r\n", + true, + ), + ( + "foo[WILDCARD]\r\nbaz[WILDCARD]\r\n", + "foobar\nbazqat\n", + true, + ), + ( + "foo[WILDCARD]\r\nbaz[WILDCARD]\r\n", + "foobar\r\nbazqat\r\n", + true, + ), + ]; + + // Iterate through the fixture lists, testing each one + for (pattern, string, expected) in fixtures { + let actual = wildcard_match(pattern, string); + dbg!(pattern, string, expected); + assert_eq!(actual, expected); + } +} |