1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-07 06:46:59 -05:00

chore: combine TestCommandBuilder with DenoCmd (#21248)

This commit is contained in:
David Sherret 2023-11-17 22:46:15 -05:00 committed by Bartek Iwańczuk
parent 6a9a09c6f5
commit e635d66f4a
No known key found for this signature in database
GPG key ID: 0C6BCDDC3B3AD750
13 changed files with 353 additions and 403 deletions

View file

@ -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,))

View file

@ -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()];

View file

@ -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();

View file

@ -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();

View file

@ -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();

View file

@ -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",

View file

@ -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 => <anonymous>:6:6");
console.expect("error: Error: some message");
console.expect(" at <anonymous>:7:9");
console.expect(" FAILURES ");
console.expect("FAILURES");
console.expect("test2 => <anonymous>: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());
});
}

View file

@ -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()

View file

@ -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(

View file

@ -44,7 +44,6 @@ pub struct TestContextBuilder {
temp_dir_path: Option<PathBuf>,
cwd: Option<String>,
envs: HashMap<String, String>,
deno_exe: Option<PathRef>,
}
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<String, String>,
use_temp_cwd: bool,
cwd: Option<String>,
cwd: PathRef,
_http_server_guard: Option<Rc<HttpServerGuard>>,
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<str>) {
@ -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<Option<Stdio>>),
}
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<StdioContainer>,
stdout: Option<StdioContainer>,
stderr: Option<StdioContainer>,
stdin_text: Option<String>,
command_name: String,
args: String,
args_vec: Vec<String>,
stdin: Option<String>,
cwd: Option<PathRef>,
envs: HashMap<String, String>,
envs_remove: HashSet<String>,
env_clear: bool,
cwd: Option<String>,
args_text: String,
args_vec: Vec<String>,
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<OsStr>) -> Self {
self.command_name = name.as_ref().to_string_lossy().to_string();
self
}
pub fn args(mut self, text: impl AsRef<str>) -> Self {
self.args = text.as_ref().to_string();
pub fn args(mut self, args: impl AsRef<str>) -> Self {
self.args_text = args.as_ref().to_string();
self
}
pub fn args_vec<T: AsRef<str>, I: IntoIterator<Item = T>>(
mut self,
args: I,
) -> Self {
self.args_vec = args.into_iter().map(|a| a.as_ref().to_string()).collect();
pub fn args_vec<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<std::ffi::OsStr>,
{
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<str>) -> Self {
self.stdin = Some(text.as_ref().to_string());
pub fn arg<S>(mut self, arg: S) -> Self
where
S: AsRef<std::ffi::OsStr>,
{
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<I, K, V>(self, vars: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<std::ffi::OsStr>,
V: AsRef<std::ffi::OsStr>,
{
let mut this = self;
for (key, value) in vars {
this = this.env(key, value);
}
this
}
pub fn env<K, V>(mut self, key: K, val: V) -> Self
where
K: AsRef<std::ffi::OsStr>,
V: AsRef<std::ffi::OsStr>,
{
self.envs.insert(
key.as_ref().to_string_lossy().to_string(),
val.as_ref().to_string_lossy().to_string(),
);
self
}
pub fn env_remove<K>(mut self, key: K) -> Self
where
K: AsRef<std::ffi::OsStr>,
{
self
.envs_remove
.insert(key.as_ref().to_string_lossy().to_string());
self
}
pub fn stdin<T: Into<Stdio>>(mut self, cfg: T) -> Self {
self.stdin = Some(StdioContainer::new(cfg.into()));
self
}
pub fn stdout<T: Into<Stdio>>(mut self, cfg: T) -> Self {
self.stdout = Some(StdioContainer::new(cfg.into()));
self
}
pub fn stderr<T: Into<Stdio>>(mut self, cfg: T) -> Self {
self.stderr = Some(StdioContainer::new(cfg.into()));
self
}
pub fn current_dir<P: AsRef<OsStr>>(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<str>) -> 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<OsStr>,
value: impl AsRef<OsStr>,
) -> 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<OsStr>) -> Self {
self
.envs_remove
.insert(key.as_ref().to_string_lossy().to_string());
self
}
pub fn envs<S: AsRef<OsStr>>(
self,
envs: impl IntoIterator<Item = (S, S)>,
) -> 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<OsStr>) -> 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<String> {
if self.args_vec.is_empty() {
std::borrow::Cow::Owned(
self
.args
.split_whitespace()
.map(|s| s.to_string())
.collect::<Vec<_>>(),
)
} 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::<Vec<_>>()
}
fn build_envs(&self) -> HashMap<String, String> {
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<std::process::Output, std::io::Error> {
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<std::process::ExitStatus, std::io::Error> {
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<String>,
split_output: bool,
testdata_dir: Option<PathRef>,
}
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<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<std::ffi::OsStr>,
{
self.cmd.args(args);
self
}
pub fn arg<S>(mut self, arg: S) -> Self
where
S: AsRef<std::ffi::OsStr>,
{
self.cmd.arg(arg);
self
}
pub fn envs<I, K, V>(mut self, vars: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<std::ffi::OsStr>,
V: AsRef<std::ffi::OsStr>,
{
self.cmd.envs(vars);
self
}
pub fn env<K, V>(mut self, key: K, val: V) -> Self
where
K: AsRef<std::ffi::OsStr>,
V: AsRef<std::ffi::OsStr>,
{
self.cmd.env(key, val);
self
}
pub fn env_remove<K>(mut self, key: K) -> Self
where
K: AsRef<std::ffi::OsStr>,
{
self.cmd.env_remove(key);
self
}
pub fn stdin<T: Into<Stdio>>(mut self, cfg: T) -> Self {
self.cmd.stdin(cfg);
self
}
pub fn stdout<T: Into<Stdio>>(mut self, cfg: T) -> Self {
self.cmd.stdout(cfg);
self
}
pub fn stderr<T: Into<Stdio>>(mut self, cfg: T) -> Self {
self.cmd.stderr(cfg);
self
}
pub fn current_dir<P: AsRef<Path>>(mut self, dir: P) -> Self {
self.cmd.current_dir(dir);
self
}
pub fn output(mut self) -> Result<std::process::Output, std::io::Error> {
self.cmd.output()
}
pub fn status(mut self) -> Result<std::process::ExitStatus, std::io::Error> {
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<str>) -> Self {
self.stdin_text = Some(text.as_ref().to_string());
self.stdin_piped()
}
pub fn set_testdata_dir(mut self, dir: impl AsRef<Path>) -> 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<DenoChild, std::io::Error> {
pub fn spawn(&self) -> Result<DenoChild, std::io::Error> {
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<String> {
if self.args_vec.is_empty() {
std::borrow::Cow::Owned(
self
.args_text
.split_whitespace()
.map(|s| s.to_string())
.collect::<Vec<_>>(),
)
} 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::<Vec<_>>()
}
fn build_envs(&self) -> HashMap<String, String> {
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<i32>,
signal: Option<i32>,
testdata_dir: PathRef,
asserted_stdout: RefCell<bool>,
asserted_stderr: RefCell<bool>,
asserted_combined: RefCell<bool>,
asserted_exit_code: RefCell<bool>,
// 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<Path>,
) -> &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)

View file

@ -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<Path>) -> &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)

View file

@ -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()

View file

@ -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<TestContext>,
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<Path>) -> &mut Self {
pub fn deno_exe(mut self, exe_path: impl AsRef<Path>) -> 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<LspClient> {
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<ChildStdin>,
deno_dir: TempDir,
context: TestContext,
root_dir: PathRef,
stderr_lines_rx: Option<mpsc::Receiver<String>>,
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.