/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
tests/util/lib/lib.rs
810 строк
21 KB
Bartek Iwańczuk
fix(ext/websocket): disable HTTP/2 server push on wss upgrade path (#36327)
27 июл 2026, 15:54
Не верифицирован
27 июл 2026, 15:54
b05ad48
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. 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 once_cell::sync::Lazy; use parking_lot::Mutex; use parking_lot::MutexGuard; use pretty_assertions::assert_eq; use pty::Pty; use url::Url; pub mod assertions; mod builders; pub mod consts; mod fs; pub mod hash; #[cfg(feature = "lsp")] pub mod lsp; mod macros; mod memory; mod parsers; pub mod print; pub mod process; pub mod pty; mod semaphore; pub mod test_runner; mod wildcard; pub use builders::DenoChild; pub use builders::TestCommandBuilder; pub use builders::TestCommandOutput; pub use builders::TestContext; pub use builders::TestContextBuilder; pub use fs::PathRef; pub use fs::TempDir; #[cfg(feature = "lsp")] pub use fs::url_to_notebook_cell_uri; #[cfg(feature = "lsp")] pub use fs::url_to_uri; pub use inventory::submit; pub use parsers::StraceOutput; pub use parsers::WrkOutput; pub use parsers::parse_max_mem; pub use parsers::parse_strace_output; pub use parsers::parse_wrk_output; pub use test_macro::test; pub use wildcard::WildcardMatchResult; pub use wildcard::wildcard_match_detailed; pub const PERMISSION_VARIANTS: [&str; 5] = ["read", "write", "env", "net", "run"]; pub const PERMISSION_DENIED_PATTERN: &str = "NotCapable"; static GUARD: Lazy<Mutex<HttpServerCount>> = Lazy::new(Default::default); pub static IS_CI: Lazy<bool> = Lazy::new(|| std::env::var("CI").is_ok()); pub fn env_vars_for_npm_tests() -> Vec<(String, String)> { vec![ ("NPM_CONFIG_REGISTRY".to_string(), npm_registry_url()), ("NPM_CONFIG_MIN_RELEASE_AGE".to_string(), "0".to_string()), ("JSR_NPM_URL".to_string(), npm_jsr_registry_url()), ("NODEJS_ORG_MIRROR".to_string(), nodejs_org_mirror_url()), ("NO_COLOR".to_string(), "1".to_string()), ("SOCKET_DEV_URL".to_string(), socket_dev_api_url()), ] } pub fn env_vars_for_jsr_tests_with_git_check() -> Vec<(String, String)> { vec![ ("JSR_URL".to_string(), jsr_registry_url()), ("DISABLE_JSR_PROVENANCE".to_string(), "true".to_string()), ("NO_COLOR".to_string(), "1".to_string()), ] } pub fn env_vars_for_jsr_tests() -> Vec<(String, String)> { let mut vars = env_vars_for_jsr_tests_with_git_check(); vars.push(( "DENO_TESTING_DISABLE_GIT_CHECK".to_string(), "1".to_string(), )); vars } pub fn env_vars_for_jsr_provenance_tests() -> Vec<(String, String)> { let mut envs = env_vars_for_jsr_tests(); envs.retain(|(key, _)| key != "DISABLE_JSR_PROVENANCE"); envs.extend(vec![ ("REKOR_URL".to_string(), rekor_url()), ("FULCIO_URL".to_string(), fulcio_url()), ( "DISABLE_JSR_MANIFEST_VERIFICATION_FOR_TESTING".to_string(), "true".to_string(), ), ]); // set GHA variable for attestation. envs.extend([ ("CI".to_string(), "true".to_string()), ("GITHUB_ACTIONS".to_string(), "true".to_string()), ("ACTIONS_ID_TOKEN_REQUEST_URL".to_string(), gha_token_url()), ( "ACTIONS_ID_TOKEN_REQUEST_TOKEN".to_string(), "dummy".to_string(), ), ( "GITHUB_REPOSITORY".to_string(), "littledivy/deno_sdl2".to_string(), ), ( "GITHUB_SERVER_URL".to_string(), "https://github.com".to_string(), ), ("GITHUB_REF".to_string(), "refs/tags/sdl2@0.0.1".to_string()), ("GITHUB_SHA".to_string(), "lol".to_string()), ("GITHUB_RUN_ID".to_string(), "1".to_string()), ("GITHUB_RUN_ATTEMPT".to_string(), "1".to_string()), ( "RUNNER_ENVIRONMENT".to_string(), "github-hosted".to_string(), ), ( "GITHUB_WORKFLOW_REF".to_string(), "littledivy/deno_sdl2@refs/tags/sdl2@0.0.1".to_string(), ), ]); envs } pub fn env_vars_for_jsr_npm_tests() -> Vec<(String, String)> { vec![ ("NPM_CONFIG_REGISTRY".to_string(), npm_registry_url()), ("NPM_CONFIG_MIN_RELEASE_AGE".to_string(), "0".to_string()), ("JSR_NPM_URL".to_string(), npm_jsr_registry_url()), ("JSR_URL".to_string(), jsr_registry_url()), ( "DENO_TESTING_DISABLE_GIT_CHECK".to_string(), "1".to_string(), ), ("DISABLE_JSR_PROVENANCE".to_string(), "true".to_string()), ("NO_COLOR".to_string(), "1".to_string()), ("NODEJS_ORG_MIRROR".to_string(), nodejs_org_mirror_url()), ] } pub fn root_path() -> PathRef { let mut p = target_dir(); while p.file_name().unwrap() != "target" { p = p.parent(); } p.parent() } /// Path to the pre-downloaded native TypeScript compiler, if present. /// /// `tools/download_tsc.ts` materializes it under /// `<target>/.native_tsc/deno_dir/tsc/<version>/<platform>/lib/tsc`. The harness /// injects this per-test as `DENO_TSC_BIN` (see `TestContextBuilder::build`) so /// `deno check`/`run --check`/`cache --check` reuse it instead of downloading /// the compiler into every test's fresh `DENO_DIR`. Returns `None` when it /// hasn't been downloaded yet. pub fn native_tsc_bin_path() -> Option<PathRef> { let exe = if cfg!(windows) { "tsc.exe" } else { "tsc" }; // `tools/download_tsc.ts` defaults its cache to `./target/.native_tsc/deno_dir` // (relative to the repo root), i.e. the `target` root - not the per-profile // `target/debug`|`target/release` dir that `target_dir()` returns. let tsc_root = root_path() .join("target") .join(".native_tsc") .join("deno_dir") .join("tsc"); for version in std::fs::read_dir(tsc_root.as_path()).ok()?.flatten() { for platform in std::fs::read_dir(version.path()) .into_iter() .flatten() .flatten() { let bin = PathRef::new(platform.path()).join("lib").join(exe); if bin.exists() { return Some(bin); } } } None } pub fn prebuilt_path() -> PathRef { third_party_path().join("prebuilt") } pub fn tests_path() -> PathRef { root_path().join("tests") } pub fn testdata_path() -> PathRef { tests_path().join("testdata") } pub fn third_party_path() -> PathRef { root_path().join("third_party") } pub fn ffi_tests_path() -> PathRef { root_path().join("tests").join("ffi") } pub fn napi_tests_path() -> PathRef { root_path().join("tests").join("napi") } pub fn deno_config_path() -> PathRef { root_path().join("tests").join("config").join("deno.json") } /// Test server registry url. pub fn npm_registry_url() -> String { format!("http://localhost:{}/", consts::PUBLIC_NPM_REGISTRY_PORT) } pub fn npm_registry_unset_url() -> String { "http://NPM_CONFIG_REGISTRY.is.unset".to_string() } pub fn npm_jsr_registry_url() -> String { format!("http://localhost:{}/", consts::PUBLIC_NPM_JSR_REGISTRY_PORT) } pub fn npm_jsr_registry_unset_url() -> String { "http://JSR_NPM_URL.is.unset".to_string() } pub fn nodejs_org_mirror_url() -> String { format!( "http://127.0.0.1:{}/", consts::NODEJS_ORG_MIRROR_SERVER_PORT ) } pub fn nodejs_org_mirror_unset_url() -> String { "http://NODEJS_ORG_MIRROR.is.unset".to_string() } pub fn jsr_registry_url() -> String { format!("http://127.0.0.1:{}/", consts::JSR_REGISTRY_SERVER_PORT) } pub fn rekor_url() -> String { format!("http://127.0.0.1:{}", consts::PROVENANCE_MOCK_SERVER_PORT) } pub fn fulcio_url() -> String { format!("http://127.0.0.1:{}", consts::PROVENANCE_MOCK_SERVER_PORT) } pub fn gha_token_url() -> String { format!( "http://127.0.0.1:{}/gha_oidc?test=true", consts::PROVENANCE_MOCK_SERVER_PORT ) } pub fn socket_dev_api_url() -> String { format!("http://localhost:{}/", consts::SOCKET_DEV_API_PORT) } pub fn jsr_registry_unset_url() -> String { "http://JSR_URL.is.unset".to_string() } pub fn std_path() -> PathRef { root_path().join("tests").join("util").join("std") } pub fn std_file_url() -> String { Url::from_directory_path(std_path()).unwrap().to_string() } pub fn target_dir() -> PathRef { let current_exe = std::env::current_exe().unwrap(); let target_dir = current_exe.parent().unwrap().parent().unwrap(); PathRef::new(target_dir) } pub fn deno_exe_path() -> PathRef { // Allow overriding the deno executable path via environment variable if let Ok(path) = std::env::var("DENO_TEST_UTIL_DENO_EXE") { return PathRef::new(PathBuf::from(path)); } // Something like /Users/rld/src/deno/target/debug/deps/deno let mut p = target_dir().join("deno").to_path_buf(); if cfg!(windows) { p.set_extension("exe"); } PathRef::new(p) } pub fn denort_exe_path() -> PathRef { let mut p = target_dir().join("denort").to_path_buf(); if cfg!(windows) { p.set_extension("exe"); } PathRef::new(p) } pub fn prebuilt_tool_path(tool: &str) -> PathRef { let mut exe = tool.to_string(); exe.push_str(if cfg!(windows) { ".exe" } else { "" }); prebuilt_path().join(platform_dir_name()).join(exe) } pub fn platform_dir_name() -> &'static str { if cfg!(target_os = "linux") { "linux64" } else if cfg!(target_os = "macos") { "mac" } else if cfg!(target_os = "windows") { "win" } else { unreachable!() } } pub fn test_server_path() -> PathBuf { let mut p = target_dir().join("test_server").to_path_buf(); if cfg!(windows) { p.set_extension("exe"); } p } fn ensure_test_server_built() { // if the test server doesn't exist then remind the developer to build first if !test_server_path().exists() { panic!( "Test server not found. Please cargo build before running the tests." ); } } pub const TEST_SERVERS_COUNT: usize = 39; #[derive(Default)] struct HttpServerCount { count: usize, test_server: Option<HttpServerStarter>, } impl HttpServerCount { fn inc(&mut self) { self.count += 1; if self.test_server.is_none() { self.test_server = Some(Default::default()); } } fn dec(&mut self) { assert!(self.count > 0); self.count -= 1; if self.count == 0 { self.test_server.take(); } } } impl Drop for HttpServerCount { fn drop(&mut self) { assert_eq!(self.count, 0); assert!(self.test_server.is_none()); } } struct HttpServerStarter { test_server: Child, } impl Default for HttpServerStarter { fn default() -> Self { println!("test_server starting..."); let mut test_server = Command::new(test_server_path()) .current_dir(testdata_path()) .stdout(Stdio::piped()) .spawn() .inspect_err(|_| { ensure_test_server_built(); }) .expect("failed to execute test_server"); let stdout = test_server.stdout.as_mut().unwrap(); use std::io::BufRead; use std::io::BufReader; let lines = BufReader::new(stdout).lines(); // Wait for all the servers to report being ready. let mut ready_count = 0; for maybe_line in lines { if let Ok(line) = maybe_line { if line.starts_with("ready:") { ready_count += 1; } if ready_count == TEST_SERVERS_COUNT { break; } } else { panic!("{}", maybe_line.unwrap_err()); } } Self { test_server } } } impl Drop for HttpServerStarter { fn drop(&mut self) { match self.test_server.try_wait() { Ok(None) => { self.test_server.kill().expect("failed to kill test_server"); let _ = self.test_server.wait(); } Ok(Some(status)) => { panic!("test_server exited unexpectedly {status}") } Err(e) => panic!("test_server error: {e}"), } } } fn lock_http_server<'a>() -> MutexGuard<'a, HttpServerCount> { GUARD.lock() } pub struct HttpServerGuard {} impl Drop for HttpServerGuard { fn drop(&mut self) { let mut g = lock_http_server(); g.dec(); } } /// Adds a reference to a shared target/debug/test_server subprocess. When the /// last instance of the HttpServerGuard is dropped, the subprocess will be /// killed. pub fn http_server() -> HttpServerGuard { let mut g = lock_http_server(); g.inc(); HttpServerGuard {} } /// Helper function to strip ansi codes. pub fn strip_ansi_codes(s: &str) -> std::borrow::Cow<'_, str> { console_static_text::ansi::strip_ansi_codes(s) } pub fn run( cmd: &[&str], input: Option<&[&str]>, envs: Option<Vec<(String, String)>>, current_dir: Option<&str>, expect_success: bool, ) { let mut process_builder = Command::new(cmd[0]); process_builder.args(&cmd[1..]).stdin(Stdio::piped()); if let Some(dir) = current_dir { process_builder.current_dir(dir); } if let Some(envs) = envs { process_builder.envs(envs); } let mut prog = process_builder.spawn().expect("failed to spawn script"); if let Some(lines) = input { let stdin = prog.stdin.as_mut().expect("failed to get stdin"); stdin .write_all(lines.join("\n").as_bytes()) .expect("failed to write to stdin"); } let status = prog.wait().expect("failed to wait on child"); if expect_success != status.success() { panic!("Unexpected exit code: {:?}", status.code()); } } pub fn run_collect( cmd: &[&str], input: Option<&[&str]>, envs: Option<Vec<(String, String)>>, current_dir: Option<&str>, expect_success: bool, ) -> (String, String) { let mut process_builder = Command::new(cmd[0]); process_builder .args(&cmd[1..]) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()); if let Some(dir) = current_dir { process_builder.current_dir(dir); } if let Some(envs) = envs { process_builder.envs(envs); } let mut prog = process_builder.spawn().expect("failed to spawn script"); if let Some(lines) = input { let stdin = prog.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, } = prog.wait_with_output().expect("failed to wait on child"); 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 run_and_collect_output( expect_success: bool, args: &str, input: Option<Vec<&str>>, envs: Option<Vec<(String, String)>>, need_http_server: bool, ) -> (String, String) { run_and_collect_output_with_args( expect_success, args.split_whitespace().collect(), input, envs, need_http_server, ) } pub fn run_and_collect_output_with_args( expect_success: bool, args: Vec<&str>, input: Option<Vec<&str>>, envs: Option<Vec<(String, String)>>, need_http_server: bool, ) -> (String, String) { let mut deno_process_builder = deno_cmd() .args_vec(args) .current_dir(testdata_path()) .stdin(Stdio::piped()) .piped_output(); if let Some(envs) = envs { deno_process_builder = 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"); 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 new_deno_dir() -> TempDir { TempDir::new() } 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) -> TestCommandBuilder { TestCommandBuilder::new(deno_dir.clone()) .env("DENO_DIR", deno_dir.path()) .env("NPM_CONFIG_REGISTRY", npm_registry_unset_url()) .env("JSR_NPM_URL", npm_jsr_registry_unset_url()) .env("NODEJS_ORG_MIRROR", nodejs_org_mirror_unset_url()) .env("JSR_URL", jsr_registry_unset_url()) } pub fn run_powershell_script_file( script_file_path: &str, args: Vec<&str>, ) -> std::result::Result<(), i64> { let deno_dir = new_deno_dir(); let mut command = Command::new("powershell.exe"); command .env("DENO_DIR", deno_dir.path()) .current_dir(testdata_path()) .arg("-file") .arg(script_file_path); for arg in args { command.arg(arg); } let output = command.output().expect("failed to spawn script"); let stdout = String::from_utf8(output.stdout).unwrap(); let stderr = String::from_utf8(output.stderr).unwrap(); println!("{stdout}"); if !output.status.success() { panic!( "{script_file_path} executed with failing error code\n{stdout}{stderr}" ); } Ok(()) } #[derive(Debug, Default)] pub struct CheckOutputIntegrationTest<'a> { pub args: &'a str, pub args_vec: Vec<&'a str>, pub output: &'a str, pub input: Option<&'a str>, pub output_str: Option<&'a str>, pub exit_code: i32, pub http_server: bool, pub envs: Vec<(String, String)>, pub env_clear: bool, pub temp_cwd: bool, /// Copies the files at the specified directory in the "testdata" directory /// to the temp folder and runs the test from there. This is useful when /// the test creates files in the testdata directory (ex. a node_modules folder) pub copy_temp_dir: Option<&'a str>, /// Relative to "testdata" directory pub cwd: Option<&'a str>, } impl CheckOutputIntegrationTest<'_> { pub fn output(&self) -> TestCommandOutput { let mut context_builder = TestContextBuilder::default(); if self.temp_cwd { context_builder = context_builder.use_temp_cwd(); } if let Some(dir) = &self.copy_temp_dir { context_builder = context_builder.use_copy_temp_dir(dir); } if self.http_server { context_builder = context_builder.use_http_server(); } let context = context_builder.build(); let mut command_builder = context.new_command(); if !self.args.is_empty() { command_builder = command_builder.args(self.args); } if !self.args_vec.is_empty() { command_builder = command_builder.args_vec(self.args_vec.clone()); } if let Some(input) = &self.input { command_builder = command_builder.stdin_text(input); } for (key, value) in &self.envs { command_builder = command_builder.env(key, value); } if self.env_clear { command_builder = command_builder.env_clear(); } if let Some(cwd) = &self.cwd { command_builder = command_builder.current_dir(cwd); } command_builder.run() } } pub fn with_pty(deno_args: &[&str], action: impl FnMut(Pty)) { let context = TestContextBuilder::default().use_temp_cwd().build(); context.new_command().args_vec(deno_args).with_pty(action); } pub(crate) mod colors { use std::io::Write; use termcolor::Ansi; use termcolor::Color; use termcolor::ColorSpec; use termcolor::WriteColor; pub fn bold<S: AsRef<str>>(s: S) -> String { let mut style_spec = ColorSpec::new(); style_spec.set_bold(true); style(s, style_spec) } pub fn red<S: AsRef<str>>(s: S) -> String { fg_color(s, Color::Red) } pub fn bold_red<S: AsRef<str>>(s: S) -> String { bold_fg_color(s, Color::Red) } pub fn green<S: AsRef<str>>(s: S) -> String { fg_color(s, Color::Green) } pub fn bold_green<S: AsRef<str>>(s: S) -> String { bold_fg_color(s, Color::Green) } pub fn bold_blue<S: AsRef<str>>(s: S) -> String { bold_fg_color(s, Color::Blue) } pub fn gray<S: AsRef<str>>(s: S) -> String { fg_color(s, Color::Ansi256(245)) } pub fn yellow<S: AsRef<str>>(s: S) -> String { fg_color(s, Color::Yellow) } fn bold_fg_color<S: AsRef<str>>(s: S, color: Color) -> String { let mut style_spec = ColorSpec::new(); style_spec.set_bold(true); style_spec.set_fg(Some(color)); style(s, style_spec) } fn fg_color<S: AsRef<str>>(s: S, color: Color) -> String { let mut style_spec = ColorSpec::new(); style_spec.set_fg(Some(color)); style(s, style_spec) } fn style<S: AsRef<str>>(s: S, colorspec: ColorSpec) -> String { let mut v = Vec::new(); let mut ansi_writer = Ansi::new(&mut v); ansi_writer.set_color(&colorspec).unwrap(); ansi_writer.write_all(s.as_ref().as_bytes()).unwrap(); ansi_writer.reset().unwrap(); String::from_utf8_lossy(&v).into_owned() } } #[derive(Debug, Clone)] pub struct TestMacroCase { pub name: &'static str, pub module_name: &'static str, pub file: &'static str, /// 1-indexed pub line: u32, /// 1-indexed pub col: u32, pub func: fn(), pub flaky: bool, pub ignore: bool, pub timeout: Option<usize>, } inventory::collect!(TestMacroCase); pub fn collect_and_filter_tests( main_category: &mut file_test_runner::collection::CollectedTestCategory< &'static TestMacroCase, >, ) { for test in inventory::iter::<TestMacroCase>() { main_category.children.push( file_test_runner::collection::CollectedCategoryOrTest::Test( file_test_runner::collection::CollectedTest { name: format!("{}::{}", test.module_name, test.name), path: PathBuf::from(test.file), // line and col are 1-indexed, but file_test_runner uses // 0-indexed numbers, so keep as-is for line to put it on // probably the function name and then do col - 1 to make // the column 0-indexed line_and_column: Some((test.line, test.col - 1)), data: test, }, ), ); } if let Some(filter) = file_test_runner::collection::parse_cli_arg_filter() { main_category.filter_children(&filter); } }