diff --git a/cli/tests/integration/cert_tests.rs b/cli/tests/integration/cert_tests.rs index 4ccc38809e..2b0807848e 100644 --- a/cli/tests/integration/cert_tests.rs +++ b/cli/tests/integration/cert_tests.rs @@ -9,6 +9,7 @@ use std::io::Cursor; use std::io::Read; use std::sync::Arc; use test_util as util; +use util::testdata_path; use util::TestContext; itest_flaky!(cafile_url_imports { @@ -75,7 +76,7 @@ fn cafile_env_fetch() { let module_url = Url::parse("https://localhost:5545/cert/cafile_url_imports.ts").unwrap(); let context = TestContext::with_http_server(); - let cafile = context.testdata_path().join("tls/RootCA.pem"); + let cafile = testdata_path().join("tls/RootCA.pem"); context .new_command() @@ -91,7 +92,7 @@ fn cafile_fetch() { let module_url = Url::parse("http://localhost:4545/cert/cafile_url_imports.ts").unwrap(); let context = TestContext::with_http_server(); - let cafile = context.testdata_path().join("tls/RootCA.pem"); + let cafile = testdata_path().join("tls/RootCA.pem"); context .new_command() .args(format!("cache --quiet --cert {} {}", cafile, module_url,)) diff --git a/cli/tests/integration/compile_tests.rs b/cli/tests/integration/compile_tests.rs index 78249079db..1d0a36145a 100644 --- a/cli/tests/integration/compile_tests.rs +++ b/cli/tests/integration/compile_tests.rs @@ -3,6 +3,7 @@ use test_util as util; use util::assert_contains; use util::assert_not_contains; +use util::testdata_path; use util::TestContext; use util::TestContextBuilder; @@ -705,7 +706,7 @@ fn dynamic_import_unanalyzable() { context .new_command() - .cwd(util::root_path().join("cli")) + .current_dir(util::root_path().join("cli")) .name(&exe) .env("NO_COLOR", "") .run() @@ -935,11 +936,10 @@ fn run_npm_bin_compile_test(opts: RunNpmBinCompileOptions) { let context = TestContextBuilder::for_npm().use_temp_cwd().build(); let temp_dir = context.temp_dir(); - let testdata_path = context.testdata_path(); let main_specifier = if opts.input_specifier.starts_with("npm:") { opts.input_specifier.to_string() } else { - testdata_path.join(opts.input_specifier).to_string() + testdata_path().join(opts.input_specifier).to_string() }; let mut args = vec!["compile".to_string()]; diff --git a/cli/tests/integration/fmt_tests.rs b/cli/tests/integration/fmt_tests.rs index edebedc274..b8c77c529e 100644 --- a/cli/tests/integration/fmt_tests.rs +++ b/cli/tests/integration/fmt_tests.rs @@ -31,7 +31,7 @@ fn fmt_test() { let output = context .new_command() - .cwd(&testdata_fmt_dir) + .current_dir(&testdata_fmt_dir) .args_vec(vec![ "fmt".to_string(), format!( @@ -50,7 +50,7 @@ fn fmt_test() { // Check without ignore. let output = context .new_command() - .cwd(&testdata_fmt_dir) + .current_dir(&testdata_fmt_dir) .args_vec(vec![ "fmt".to_string(), "--check".to_string(), @@ -66,7 +66,7 @@ fn fmt_test() { // Format the source file. let output = context .new_command() - .cwd(&testdata_fmt_dir) + .current_dir(&testdata_fmt_dir) .args_vec(vec![ "fmt".to_string(), badly_formatted_js.to_string(), @@ -95,7 +95,7 @@ fn fmt_stdin_syntax_error() { .current_dir(util::testdata_path()) .arg("fmt") .arg("-") - .set_stdin_text("import { example }") + .stdin_text("import { example }") .split_output() .run(); assert!(output.stdout().is_empty()); @@ -141,7 +141,7 @@ fn fmt_auto_ignore_git_and_node_modules() { let output = context .new_command() - .cwd(t) + .current_dir(t) .env("NO_COLOR", "1") .args("fmt") .run(); diff --git a/cli/tests/integration/info_tests.rs b/cli/tests/integration/info_tests.rs index bfba400804..47c5cc1d07 100644 --- a/cli/tests/integration/info_tests.rs +++ b/cli/tests/integration/info_tests.rs @@ -11,7 +11,7 @@ fn info_with_compiled_source() { let output = context .new_command() - .cwd(util::testdata_path()) + .current_dir(util::testdata_path()) .args_vec(["cache", module_path]) .run(); output.assert_exit_code(0); @@ -19,7 +19,7 @@ fn info_with_compiled_source() { let output = context .new_command() - .cwd(util::testdata_path()) + .current_dir(util::testdata_path()) .args_vec(["info", module_path]) .split_output() .run(); diff --git a/cli/tests/integration/install_tests.rs b/cli/tests/integration/install_tests.rs index d9a7a4fb64..ae1cb4bf73 100644 --- a/cli/tests/integration/install_tests.rs +++ b/cli/tests/integration/install_tests.rs @@ -83,7 +83,7 @@ fn install_custom_dir_env_var() { context .new_command() - .cwd(util::root_path()) // different cwd + .current_dir(util::root_path()) // different cwd .args("install --check --name echo_test http://localhost:4545/echo.ts") .envs([ ("HOME", temp_dir_str.as_str()), @@ -124,7 +124,7 @@ fn installer_test_local_module_run() { context .new_command() - .cwd(util::root_path()) + .current_dir(util::root_path()) .args_vec([ "install", "--name", @@ -152,7 +152,7 @@ fn installer_test_local_module_run() { let output = context .new_command() .name(&file_path) - .cwd(temp_dir.path()) + .current_dir(temp_dir.path()) .args("foo") .env("PATH", util::target_dir()) .run(); @@ -183,7 +183,7 @@ fn installer_test_remote_module_run() { let output = context .new_command() .name(&bin_file_path) - .cwd(root_dir) + .current_dir(root_dir) .args("foo") .env("PATH", util::target_dir()) .run(); diff --git a/cli/tests/integration/npm_tests.rs b/cli/tests/integration/npm_tests.rs index f49e3e9889..fe3e6cd0f8 100644 --- a/cli/tests/integration/npm_tests.rs +++ b/cli/tests/integration/npm_tests.rs @@ -2135,7 +2135,7 @@ fn top_level_install_package_json_explicit_opt_in() { let output = test_context .new_command() .args("run -") - .stdin("console.log(5)") + .stdin_text("console.log(5)") .run(); output.assert_matches_text(concat!( "Initialize @denotest/esm-basic@1.0.0\n", diff --git a/cli/tests/integration/repl_tests.rs b/cli/tests/integration/repl_tests.rs index c161477d52..a4675c388b 100644 --- a/cli/tests/integration/repl_tests.rs +++ b/cli/tests/integration/repl_tests.rs @@ -890,7 +890,7 @@ fn repl_with_quiet_flag() { #[test] fn repl_unit_tests() { util::with_pty(&["repl"], |mut console| { - console.write_line( + console.write_line_raw( "\ console.log('Hello from outside of test!'); \ Deno.test('test1', async (t) => { \ @@ -912,11 +912,11 @@ fn repl_unit_tests() { console.expect(" step1 ... ok ("); console.expect("test1 ... ok ("); console.expect("test2 ... FAILED ("); - console.expect(" ERRORS "); + console.expect("ERRORS"); console.expect("test2 => :6:6"); console.expect("error: Error: some message"); console.expect(" at :7:9"); - console.expect(" FAILURES "); + console.expect("FAILURES"); console.expect("test2 => :6:6"); console.expect("FAILED | 1 passed (1 step) | 1 failed ("); console.expect("undefined"); @@ -1058,11 +1058,13 @@ fn package_json_uncached_no_error() { // should support getting the package now though console .write_line("import { getValue, setValue } from '@denotest/esm-basic';"); - console.expect_all(&["undefined", "Initialize"]); + console.expect_all(&["undefined", "Download"]); console.write_line("setValue(12 + 30);"); console.expect("undefined"); console.write_line("getValue()"); - console.expect("42") + console.expect("42"); + + assert!(temp_dir.path().join("node_modules").exists()); }); } diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs index 43c8930717..be3f74df0c 100644 --- a/cli/tests/integration/run_tests.rs +++ b/cli/tests/integration/run_tests.rs @@ -908,11 +908,7 @@ fn lock_no_declaration_files() { .run(); output.assert_matches_file("lockfile/no_dts/main.cache.out"); let lockfile = context.temp_dir().path().join("deno.lock"); - lockfile.assert_matches_file( - context - .testdata_path() - .join("lockfile/no_dts/deno.lock.out"), - ); + lockfile.assert_matches_file("lockfile/no_dts/deno.lock.out"); } #[test] @@ -2093,10 +2089,7 @@ console.log("executing javascript"); "#; let mut p = util::deno_cmd() - .arg("run") - .args(["--ext", "js"]) - .arg("--check") - .arg("-") + .args("run --ext js --check -") .stdin(std::process::Stdio::piped()) .stdout_piped() .spawn() diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs index cd886fbd7c..c5efa771ba 100644 --- a/cli/tools/upgrade.rs +++ b/cli/tools/upgrade.rs @@ -593,7 +593,16 @@ fn get_url( UpgradeCheckKind::Execution => "", UpgradeCheckKind::Lsp => "?lsp", }; - format!("https://dl.deno.land/{}{}", file_name, query_param) + format!("{}/{}{}", base_upgrade_url(), file_name, query_param) +} + +fn base_upgrade_url() -> Cow<'static, str> { + // this is used to get the current version + if let Ok(url) = env::var("DENO_DONT_USE_INTERNAL_BASE_UPGRADE_URL") { + Cow::Owned(url) + } else { + Cow::Borrowed("https://dl.deno.land") + } } async fn download_package( diff --git a/test_util/src/builders.rs b/test_util/src/builders.rs index 414a0103f3..ffc6589029 100644 --- a/test_util/src/builders.rs +++ b/test_util/src/builders.rs @@ -44,7 +44,6 @@ pub struct TestContextBuilder { temp_dir_path: Option, cwd: Option, envs: HashMap, - deno_exe: Option, } impl TestContextBuilder { @@ -133,15 +132,14 @@ impl TestContextBuilder { } else { temp_dir }; - let testdata_dir = testdata_path(); if let Some(temp_copy_dir) = &self.copy_temp_dir { - let test_data_path = testdata_dir.join(temp_copy_dir); + let test_data_path = testdata_path().join(temp_copy_dir); let temp_copy_dir = temp_dir.path().join(temp_copy_dir); temp_copy_dir.create_dir_all(); test_data_path.copy_to_recursive(&temp_copy_dir); } - let deno_exe = self.deno_exe.clone().unwrap_or_else(deno_exe_path); + let deno_exe = deno_exe_path(); println!("deno_exe path {}", deno_exe); let http_server_guard = if self.use_http_server { @@ -150,15 +148,23 @@ impl TestContextBuilder { None }; + let cwd = if self.use_temp_cwd || self.copy_temp_dir.is_some() { + temp_dir.path().to_owned() + } else { + testdata_path().clone() + }; + let cwd = match &self.cwd { + Some(specified_cwd) => cwd.join(specified_cwd), + None => cwd, + }; + TestContext { - cwd: self.cwd.clone(), + cwd, deno_exe, envs: self.envs.clone(), - use_temp_cwd: self.use_temp_cwd || self.copy_temp_dir.is_some(), _http_server_guard: http_server_guard, deno_dir, temp_dir, - testdata_dir, } } } @@ -167,12 +173,10 @@ impl TestContextBuilder { pub struct TestContext { deno_exe: PathRef, envs: HashMap, - use_temp_cwd: bool, - cwd: Option, + cwd: PathRef, _http_server_guard: Option>, deno_dir: TempDir, temp_dir: TempDir, - testdata_dir: PathRef, } impl Default for TestContext { @@ -186,10 +190,6 @@ impl TestContext { TestContextBuilder::default().use_http_server().build() } - pub fn testdata_path(&self) -> &PathRef { - &self.testdata_dir - } - pub fn deno_dir(&self) -> &TempDir { &self.deno_dir } @@ -199,24 +199,15 @@ impl TestContext { } pub fn new_command(&self) -> TestCommandBuilder { - TestCommandBuilder { - command_name: self.deno_exe.to_string(), - args: Default::default(), - args_vec: Default::default(), - stdin: Default::default(), - envs: Default::default(), - envs_remove: Default::default(), - env_clear: Default::default(), - cwd: Default::default(), - split_output: false, - context: self.clone(), - } + TestCommandBuilder::new(self.deno_dir.clone()) + .envs(self.envs.clone()) + .current_dir(&self.cwd) } pub fn new_lsp_command(&self) -> LspClientBuilder { - let mut builder = LspClientBuilder::new(); - builder.deno_exe(&self.deno_exe).set_test_context(self); - builder + LspClientBuilder::new_with_dir(self.deno_dir.clone()) + .deno_exe(&self.deno_exe) + .set_root_dir(self.temp_dir.path().clone()) } pub fn run_npm(&self, args: impl AsRef) { @@ -229,41 +220,190 @@ impl TestContext { } } +/// We can't clone an stdio, so if someone clones a DenoCmd, +/// we want to set this to `Cloned` and show the user a helpful +/// panic message. +enum StdioContainer { + Cloned, + Inner(RefCell>), +} + +impl Clone for StdioContainer { + fn clone(&self) -> Self { + Self::Cloned + } +} + +impl StdioContainer { + pub fn new(stdio: Stdio) -> Self { + Self::Inner(RefCell::new(Some(stdio))) + } + + pub fn take(&self) -> Stdio { + match self { + StdioContainer::Cloned => panic!("Cannot run a command after it was cloned. You need to reset the stdio value."), + StdioContainer::Inner(inner) => { + match inner.borrow_mut().take() { + Some(value) => value, + None => panic!("Cannot run a command that was previously run. You need to reset the stdio value between runs."), + } + }, + } + } +} + +#[derive(Clone)] pub struct TestCommandBuilder { + deno_dir: TempDir, + stdin: Option, + stdout: Option, + stderr: Option, + stdin_text: Option, command_name: String, - args: String, - args_vec: Vec, - stdin: Option, + cwd: Option, envs: HashMap, envs_remove: HashSet, env_clear: bool, - cwd: Option, + args_text: String, + args_vec: Vec, split_output: bool, - context: TestContext, } impl TestCommandBuilder { + pub fn new(deno_dir: TempDir) -> Self { + Self { + deno_dir, + stdin: None, + stdout: None, + stderr: None, + stdin_text: None, + split_output: false, + cwd: None, + envs: Default::default(), + envs_remove: Default::default(), + env_clear: false, + command_name: "deno".to_string(), + args_text: "".to_string(), + args_vec: Default::default(), + } + } + pub fn name(mut self, name: impl AsRef) -> Self { self.command_name = name.as_ref().to_string_lossy().to_string(); self } - pub fn args(mut self, text: impl AsRef) -> Self { - self.args = text.as_ref().to_string(); + pub fn args(mut self, args: impl AsRef) -> Self { + self.args_text = args.as_ref().to_string(); self } - pub fn args_vec, I: IntoIterator>( - mut self, - args: I, - ) -> Self { - self.args_vec = args.into_iter().map(|a| a.as_ref().to_string()).collect(); + pub fn args_vec(mut self, args: I) -> Self + where + I: IntoIterator, + S: AsRef, + { + self.args_vec.extend( + args + .into_iter() + .map(|s| s.as_ref().to_string_lossy().to_string()), + ); self } - pub fn stdin(mut self, text: impl AsRef) -> Self { - self.stdin = Some(text.as_ref().to_string()); + pub fn arg(mut self, arg: S) -> Self + where + S: AsRef, + { self + .args_vec + .push(arg.as_ref().to_string_lossy().to_string()); + self + } + + pub fn env_clear(mut self) -> Self { + self.env_clear = true; + self + } + + pub fn envs(self, vars: I) -> Self + where + I: IntoIterator, + K: AsRef, + V: AsRef, + { + let mut this = self; + for (key, value) in vars { + this = this.env(key, value); + } + this + } + + pub fn env(mut self, key: K, val: V) -> Self + where + K: AsRef, + V: AsRef, + { + self.envs.insert( + key.as_ref().to_string_lossy().to_string(), + val.as_ref().to_string_lossy().to_string(), + ); + self + } + + pub fn env_remove(mut self, key: K) -> Self + where + K: AsRef, + { + self + .envs_remove + .insert(key.as_ref().to_string_lossy().to_string()); + self + } + + pub fn stdin>(mut self, cfg: T) -> Self { + self.stdin = Some(StdioContainer::new(cfg.into())); + self + } + + pub fn stdout>(mut self, cfg: T) -> Self { + self.stdout = Some(StdioContainer::new(cfg.into())); + self + } + + pub fn stderr>(mut self, cfg: T) -> Self { + self.stderr = Some(StdioContainer::new(cfg.into())); + self + } + + pub fn current_dir>(mut self, dir: P) -> Self { + let dir = dir.as_ref().to_string_lossy().to_string(); + self.cwd = Some(match self.cwd { + Some(current) => current.join(dir), + None => PathRef::new(dir), + }); + self + } + + pub fn stdin_piped(self) -> Self { + self.stdin(std::process::Stdio::piped()) + } + + pub fn stdout_piped(self) -> Self { + self.stdout(std::process::Stdio::piped()) + } + + pub fn stderr_piped(self) -> Self { + self.stderr(std::process::Stdio::piped()) + } + + pub fn piped_output(self) -> Self { + self.stdout_piped().stderr_piped() + } + + pub fn stdin_text(mut self, text: impl AsRef) -> Self { + self.stdin_text = Some(text.as_ref().to_string()); + self.stdin_piped() } /// Splits the output into stdout and stderr rather than having them combined. @@ -275,118 +415,6 @@ impl TestCommandBuilder { self } - pub fn env( - mut self, - key: impl AsRef, - value: impl AsRef, - ) -> Self { - self.envs.insert( - key.as_ref().to_string_lossy().to_string(), - value.as_ref().to_string_lossy().to_string(), - ); - self - } - - pub fn env_remove(mut self, key: impl AsRef) -> Self { - self - .envs_remove - .insert(key.as_ref().to_string_lossy().to_string()); - self - } - - pub fn envs>( - self, - envs: impl IntoIterator, - ) -> Self { - let mut this = self; - for (k, v) in envs { - this = this.env(k, v); - } - this - } - - pub fn env_clear(mut self) -> Self { - self.env_clear = true; - self - } - - pub fn cwd(mut self, cwd: impl AsRef) -> Self { - self.cwd = Some(cwd.as_ref().to_string_lossy().to_string()); - self - } - - fn build_cwd(&self) -> PathRef { - let root_dir = if self.context.use_temp_cwd { - self.context.temp_dir.path().to_owned() - } else { - self.context.testdata_dir.clone() - }; - let specified_cwd = self.cwd.as_ref().or(self.context.cwd.as_ref()); - match specified_cwd { - Some(cwd) => root_dir.join(cwd), - None => root_dir, - } - } - - fn build_command_path(&self) -> PathRef { - let command_name = if cfg!(windows) && self.command_name == "npm" { - "npm.cmd" - } else { - &self.command_name - }; - if command_name == "deno" { - deno_exe_path() - } else { - PathRef::new(PathBuf::from(command_name)) - } - } - - fn build_args(&self) -> Vec { - if self.args_vec.is_empty() { - std::borrow::Cow::Owned( - self - .args - .split_whitespace() - .map(|s| s.to_string()) - .collect::>(), - ) - } else { - assert!( - self.args.is_empty(), - "Do not provide args when providing args_vec." - ); - std::borrow::Cow::Borrowed(&self.args_vec) - } - .iter() - .map(|arg| { - arg.replace( - "$TESTDATA", - &self.context.testdata_dir.as_path().to_string_lossy(), - ) - }) - .collect::>() - } - - fn build_envs(&self) -> HashMap { - let mut envs = self.context.envs.clone(); - envs.insert( - "DENO_DIR".to_string(), - self.context.deno_dir.path().to_string(), - ); - for key in &self.envs_remove { - envs.remove(key); - } - for (key, value) in &self.envs { - envs.insert(key.to_string(), value.to_string()); - } - if !envs.contains_key("NPM_CONFIG_REGISTRY") - && !self.envs_remove.contains("NPM_CONFIG_REGISTRY") - { - envs.insert("NPM_CONFIG_REGISTRY".to_string(), npm_registry_unset_url()); - } - envs - } - pub fn with_pty(&self, mut action: impl FnMut(Pty)) { if !Pty::is_supported() { return; @@ -408,184 +436,33 @@ impl TestCommandBuilder { } } - action(Pty::new( - self.build_command_path().as_path(), - &args, - self.build_cwd().as_path(), - Some(envs), - )) + let cwd = self + .cwd + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| std::env::current_dir().unwrap()); + let command_path = self.build_command_path(); + + println!("command {} {}", command_path, args.join(" ")); + println!("command cwd {}", cwd.display()); + action(Pty::new(command_path.as_path(), &args, &cwd, Some(envs))) } - #[track_caller] - pub fn run(&self) -> TestCommandOutput { - let args = self.build_args(); - let mut command = self.start_build_command(&args); - - if self.split_output { - command = command.split_output(); - } - - if let Some(input) = &self.stdin { - command = command.set_stdin_text(input.clone()); - } - command = command.set_testdata_dir(&self.context.testdata_dir); - - command.run() + pub fn output(&self) -> Result { + assert!(self.stdin_text.is_none(), "use spawn instead"); + self.build_command().output() } - pub fn spawn_with_piped_output(&self) -> DenoChild { - self - .start_build_command(&self.build_args()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .unwrap() + pub fn status(&self) -> Result { + assert!(self.stdin_text.is_none(), "use spawn instead"); + self.build_command().status() } - fn start_build_command(&self, args: &[String]) -> DenoCmd { - let mut command = Command::new(self.build_command_path()); - let cwd = self.build_cwd(); - - println!("command {} {}", self.command_name, args.join(" ")); - println!("command cwd {}", cwd); - command.args(args.iter()); - if self.env_clear { - command.env_clear(); - } - let envs = self.build_envs(); - command.envs(envs); - command.current_dir(cwd); - command.stdin(Stdio::piped()); - DenoCmd::new_raw(self.context.temp_dir.clone(), command) - } -} - -pub struct DenoCmd { - deno_dir: TempDir, - cmd: Command, - stdin_text: Option, - split_output: bool, - testdata_dir: Option, -} - -impl DenoCmd { - pub fn new_raw(deno_dir: TempDir, cmd: Command) -> Self { - Self { - deno_dir, - cmd, - stdin_text: None, - split_output: false, - testdata_dir: None, - } - } - - pub fn args(mut self, args: I) -> Self - where - I: IntoIterator, - S: AsRef, - { - self.cmd.args(args); - self - } - - pub fn arg(mut self, arg: S) -> Self - where - S: AsRef, - { - self.cmd.arg(arg); - self - } - - pub fn envs(mut self, vars: I) -> Self - where - I: IntoIterator, - K: AsRef, - V: AsRef, - { - self.cmd.envs(vars); - self - } - - pub fn env(mut self, key: K, val: V) -> Self - where - K: AsRef, - V: AsRef, - { - self.cmd.env(key, val); - self - } - - pub fn env_remove(mut self, key: K) -> Self - where - K: AsRef, - { - self.cmd.env_remove(key); - self - } - - pub fn stdin>(mut self, cfg: T) -> Self { - self.cmd.stdin(cfg); - self - } - - pub fn stdout>(mut self, cfg: T) -> Self { - self.cmd.stdout(cfg); - self - } - - pub fn stderr>(mut self, cfg: T) -> Self { - self.cmd.stderr(cfg); - self - } - - pub fn current_dir>(mut self, dir: P) -> Self { - self.cmd.current_dir(dir); - self - } - - pub fn output(mut self) -> Result { - self.cmd.output() - } - - pub fn status(mut self) -> Result { - self.cmd.status() - } - - pub fn stdin_piped(self) -> Self { - self.stdin(std::process::Stdio::piped()) - } - - pub fn stdout_piped(self) -> Self { - self.stdout(std::process::Stdio::piped()) - } - - pub fn stderr_piped(self) -> Self { - self.stderr(std::process::Stdio::piped()) - } - - pub fn piped_output(self) -> Self { - self.stdout_piped().stderr_piped() - } - - pub fn set_stdin_text(mut self, text: impl AsRef) -> Self { - self.stdin_text = Some(text.as_ref().to_string()); - self.stdin_piped() - } - - pub fn set_testdata_dir(mut self, dir: impl AsRef) -> Self { - self.testdata_dir = Some(PathRef::new(dir)); - self - } - - pub fn split_output(mut self) -> Self { - self.split_output = true; - self - } - - pub fn spawn(mut self) -> Result { + pub fn spawn(&self) -> Result { + let child = self.build_command().spawn()?; let mut child = DenoChild { _deno_dir: self.deno_dir.clone(), - child: self.cmd.spawn()?, + child, }; if let Some(input) = &self.stdin_text { @@ -596,7 +473,11 @@ impl DenoCmd { Ok(child) } - pub fn run(self) -> TestCommandOutput { + pub fn spawn_with_piped_output(&self) -> DenoChild { + self.clone().piped_output().spawn().unwrap() + } + + pub fn run(&self) -> TestCommandOutput { fn read_pipe_to_string(mut pipe: os_pipe::PipeReader) -> String { let mut output = String::new(); pipe.read_to_string(&mut output).unwrap(); @@ -614,7 +495,7 @@ impl DenoCmd { text } - let mut command = self.cmd; + let mut command = self.build_command(); let args = command .get_args() .map(ToOwned::to_owned) @@ -675,14 +556,90 @@ impl DenoCmd { signal, combined, std_out_err, - testdata_dir: self.testdata_dir.unwrap_or_else(testdata_path), asserted_exit_code: RefCell::new(false), asserted_stdout: RefCell::new(false), asserted_stderr: RefCell::new(false), asserted_combined: RefCell::new(false), - _temp_dir: self.deno_dir.clone(), + _deno_dir: self.deno_dir.clone(), } } + + fn build_command(&self) -> Command { + let command_path = self.build_command_path(); + let args = self.build_args(); + println!("command {} {}", command_path, args.join(" ")); + let mut command = Command::new(command_path); + if let Some(cwd) = &self.cwd { + println!("command cwd {}", cwd); + command.current_dir(cwd); + } + if let Some(stdin) = &self.stdin { + command.stdin(stdin.take()); + } + if let Some(stdout) = &self.stdout { + command.stdout(stdout.take()); + } + if let Some(stderr) = &self.stderr { + command.stderr(stderr.take()); + } + + command.args(args.iter()); + if self.env_clear { + command.env_clear(); + } + let envs = self.build_envs(); + command.envs(envs); + command.stdin(Stdio::piped()); + command + } + + fn build_command_path(&self) -> PathRef { + let command_name = if cfg!(windows) && self.command_name == "npm" { + "npm.cmd" + } else { + &self.command_name + }; + if command_name == "deno" { + deno_exe_path() + } else { + PathRef::new(PathBuf::from(command_name)) + } + } + + fn build_args(&self) -> Vec { + if self.args_vec.is_empty() { + std::borrow::Cow::Owned( + self + .args_text + .split_whitespace() + .map(|s| s.to_string()) + .collect::>(), + ) + } else { + assert!( + self.args_text.is_empty(), + "Do not provide args when providing args_vec." + ); + std::borrow::Cow::Borrowed(&self.args_vec) + } + .iter() + .map(|arg| arg.replace("$TESTDATA", &testdata_path().to_string_lossy())) + .collect::>() + } + + fn build_envs(&self) -> HashMap { + let mut envs = self.envs.clone(); + if !envs.contains_key("DENO_DIR") { + envs.insert("DENO_DIR".to_string(), self.deno_dir.path().to_string()); + } + if !envs.contains_key("NPM_CONFIG_REGISTRY") { + envs.insert("NPM_CONFIG_REGISTRY".to_string(), npm_registry_unset_url()); + } + for key in &self.envs_remove { + envs.remove(key); + } + envs + } } pub struct DenoChild { @@ -717,13 +674,12 @@ pub struct TestCommandOutput { std_out_err: Option<(String, String)>, exit_code: Option, signal: Option, - testdata_dir: PathRef, asserted_stdout: RefCell, asserted_stderr: RefCell, asserted_combined: RefCell, asserted_exit_code: RefCell, // keep alive for the duration of the output reference - _temp_dir: TempDir, + _deno_dir: TempDir, } impl Drop for TestCommandOutput { @@ -768,10 +724,6 @@ impl Drop for TestCommandOutput { } impl TestCommandOutput { - pub fn testdata_dir(&self) -> &PathRef { - &self.testdata_dir - } - pub fn skip_output_check(&self) -> &Self { *self.asserted_combined.borrow_mut() = true; self.skip_stdout_check(); @@ -927,7 +879,7 @@ impl TestCommandOutput { actual: &str, file_path: impl AsRef, ) -> &Self { - let output_path = self.testdata_dir().join(file_path); + let output_path = testdata_path().join(file_path); println!("output path {}", output_path); let expected_text = output_path.read_to_string(); self.inner_assert_matches_text(actual, expected_text) diff --git a/test_util/src/fs.rs b/test_util/src/fs.rs index b2f0eceac5..5a0ad8d5bd 100644 --- a/test_util/src/fs.rs +++ b/test_util/src/fs.rs @@ -14,6 +14,7 @@ use serde::de::DeserializeOwned; use serde::Serialize; use crate::assertions::assert_wildcard_match; +use crate::testdata_path; /// Represents a path on the file system, which can be used /// to perform specific actions. @@ -218,7 +219,7 @@ impl PathRef { } pub fn assert_matches_file(&self, wildcard_file: impl AsRef) -> &Self { - let wildcard_file = PathRef::new(wildcard_file); + let wildcard_file = testdata_path().join(wildcard_file); println!("output path {}", wildcard_file); let expected_text = wildcard_file.read_to_string(); self.assert_matches_text(&expected_text) diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 24be651d12..97ae464797 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -83,7 +83,6 @@ mod npm; pub mod pty; pub use builders::DenoChild; -pub use builders::DenoCmd; pub use builders::TestCommandBuilder; pub use builders::TestCommandOutput; pub use builders::TestContext; @@ -2099,7 +2098,7 @@ pub fn run_and_collect_output_with_args( need_http_server: bool, ) -> (String, String) { let mut deno_process_builder = deno_cmd() - .args(args) + .args_vec(args) .current_dir(testdata_path()) .stdin(Stdio::piped()) .piped_output(); @@ -2139,18 +2138,15 @@ pub fn new_deno_dir() -> TempDir { TempDir::new() } -pub fn deno_cmd() -> DenoCmd { +pub fn deno_cmd() -> TestCommandBuilder { let deno_dir = new_deno_dir(); deno_cmd_with_deno_dir(&deno_dir) } -pub fn deno_cmd_with_deno_dir(deno_dir: &TempDir) -> DenoCmd { - let exe_path = deno_exe_path(); - assert!(exe_path.exists()); - let mut cmd = Command::new(exe_path); - cmd.env("DENO_DIR", deno_dir.path()); - cmd.env("NPM_CONFIG_REGISTRY", npm_registry_unset_url()); - DenoCmd::new_raw(deno_dir.clone(), cmd) +pub fn deno_cmd_with_deno_dir(deno_dir: &TempDir) -> TestCommandBuilder { + TestCommandBuilder::new(deno_dir.clone()) + .env("DENO_DIR", deno_dir.path()) + .env("NPM_CONFIG_REGISTRY", npm_registry_unset_url()) } pub fn run_powershell_script_file( @@ -2227,7 +2223,7 @@ impl<'a> CheckOutputIntegrationTest<'a> { command_builder = command_builder.args_vec(self.args_vec.clone()); } if let Some(input) = &self.input { - command_builder = command_builder.stdin(input); + command_builder = command_builder.stdin_text(input); } for (key, value) in &self.envs { command_builder = command_builder.env(key, value); @@ -2236,7 +2232,7 @@ impl<'a> CheckOutputIntegrationTest<'a> { command_builder = command_builder.env_clear(); } if let Some(cwd) = &self.cwd { - command_builder = command_builder.cwd(cwd); + command_builder = command_builder.current_dir(cwd); } command_builder.run() diff --git a/test_util/src/lsp.rs b/test_util/src/lsp.rs index 2fb227709f..222415cdd8 100644 --- a/test_util/src/lsp.rs +++ b/test_util/src/lsp.rs @@ -1,11 +1,8 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. use crate::deno_exe_path; -use crate::new_deno_dir; use crate::npm_registry_url; use crate::PathRef; -use crate::TestContext; -use crate::TestContextBuilder; use super::TempDir; @@ -464,23 +461,29 @@ pub struct LspClientBuilder { print_stderr: bool, capture_stderr: bool, deno_exe: PathRef, - context: Option, + root_dir: PathRef, use_diagnostic_sync: bool, + deno_dir: TempDir, } impl LspClientBuilder { #[allow(clippy::new_without_default)] pub fn new() -> Self { + Self::new_with_dir(TempDir::new()) + } + + pub fn new_with_dir(deno_dir: TempDir) -> Self { Self { print_stderr: false, capture_stderr: false, deno_exe: deno_exe_path(), - context: None, + root_dir: deno_dir.path().clone(), use_diagnostic_sync: true, + deno_dir, } } - pub fn deno_exe(&mut self, exe_path: impl AsRef) -> &mut Self { + pub fn deno_exe(mut self, exe_path: impl AsRef) -> Self { self.deno_exe = PathRef::new(exe_path); self } @@ -488,25 +491,25 @@ impl LspClientBuilder { // not deprecated, this is just here so you don't accidentally // commit code with this enabled #[deprecated] - pub fn print_stderr(&mut self) -> &mut Self { + pub fn print_stderr(mut self) -> Self { self.print_stderr = true; self } - pub fn capture_stderr(&mut self) -> &mut Self { + pub fn capture_stderr(mut self) -> Self { self.capture_stderr = true; self } /// Whether to use the synchronization messages to better sync diagnostics /// between the test client and server. - pub fn use_diagnostic_sync(&mut self, value: bool) -> &mut Self { + pub fn use_diagnostic_sync(mut self, value: bool) -> Self { self.use_diagnostic_sync = value; self } - pub fn set_test_context(&mut self, test_context: &TestContext) -> &mut Self { - self.context = Some(test_context.clone()); + pub fn set_root_dir(mut self, root_dir: PathRef) -> Self { + self.root_dir = root_dir; self } @@ -515,11 +518,7 @@ impl LspClientBuilder { } pub fn build_result(&self) -> Result { - let deno_dir = self - .context - .as_ref() - .map(|c| c.deno_dir().clone()) - .unwrap_or_else(new_deno_dir); + let deno_dir = self.deno_dir.clone(); let mut command = Command::new(&self.deno_exe); command .env("DENO_DIR", deno_dir.path()) @@ -576,10 +575,7 @@ impl LspClientBuilder { reader, request_id: 1, start: Instant::now(), - context: self - .context - .clone() - .unwrap_or_else(|| TestContextBuilder::new().build()), + root_dir: self.root_dir.clone(), writer, deno_dir, stderr_lines_rx, @@ -596,7 +592,7 @@ pub struct LspClient { start: Instant, writer: io::BufWriter, deno_dir: TempDir, - context: TestContext, + root_dir: PathRef, stderr_lines_rx: Option>, config: serde_json::Value, supports_workspace_configuration: bool, @@ -712,7 +708,7 @@ impl LspClient { mut config: Value, ) { let mut builder = InitializeParamsBuilder::new(config.clone()); - builder.set_root_uri(self.context.temp_dir().uri()); + builder.set_root_uri(self.root_dir.uri_dir()); do_build(&mut builder); let params: InitializeParams = builder.build(); // `config` must be updated to account for the builder changes.